diff --git a/README.md b/README.md index 3dca82efd8..7cc7c98227 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ vendor's servers. * Files in app/src/main/java/net/osmand/ and app/src/main/aidl/net/osmand/ are taken from the [OsmAnd](https://osmand.net/) project, licensed under the GPLv3 by OsmAnd BV * Files in app/src/main/java/org/bouncycastle are taken from the [Bouncy Castle](https://www.bouncycastle.org/java.html) project, licensed under the MIT license by The Legion of the Bouncy Castle Inc. * Files in app/src/main/java/com/android/nQuant are taken from the [nQuant.android](https://github.com/mcychan/nQuant.android/) project, licensed under the Apache license by Miller Cy Chan +* Files in app/src/main/java/org/concentus are taken from the [Concentus](https://github.com/lostromb/concentus) project, licensed under the BSD-3 license by various holding parties ## Download diff --git a/app/src/main/assets/licenses/concentus.txt b/app/src/main/assets/licenses/concentus.txt new file mode 100644 index 0000000000..9250838b70 --- /dev/null +++ b/app/src/main/assets/licenses/concentus.txt @@ -0,0 +1,37 @@ +Copyright (c) by various holding parties, including (but not limited to): +Skype Limited, Xiph.Org Foundation, CSIRO, Microsoft Corporation, +Jean-Marc Valin, Gregory Maxwell, Mark Borgerding, Timothy B. Terriberry, +Logan Stromberg. All rights are reserved by their respective holders. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +This repository and its redistributable packages contain independently compiled +versions of the Opus C reference library, which is maintained by Xiph.org and the +Opus open-source contributors. The source code for these libraries is freely available +at https://gitlab.xiph.org/xiph/opus/-/tags/v1.5.2, and all binaries are being +redistributed to you under the same terms of the general Opus license dictated above. diff --git a/app/src/main/assets/licenses/licenses.json b/app/src/main/assets/licenses/licenses.json index 3f54ac235d..b466a06e6c 100644 --- a/app/src/main/assets/licenses/licenses.json +++ b/app/src/main/assets/licenses/licenses.json @@ -132,6 +132,13 @@ "url": "https://github.com/cketti/ckChangeLog", "path": "licenses/ckchangelog.txt" }, + { + "name": "Concentus", + "owner": "Logan Stromberg, various holding parties", + "type": "BSD-3", + "url": "https://github.com/lostromb/concentus", + "path": "licenses/concentus.txt" + }, { "name": "solarpositioning", "owner": "Klaus A. Brunner", diff --git a/app/src/main/java/org/concentus/Analysis.java b/app/src/main/java/org/concentus/Analysis.java new file mode 100644 index 0000000000..18795e52e3 --- /dev/null +++ b/app/src/main/java/org/concentus/Analysis.java @@ -0,0 +1,575 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class Analysis { + + private static final double M_PI = 3.141592653; + private static final float cA = 0.43157974f; + private static final float cB = 0.67848403f; + private static final float cC = 0.08595542f; + private static final float cE = ((float) M_PI / 2); + + private static final int NB_TONAL_SKIP_BANDS = 9; + + static float fast_atan2f(float y, float x) { + float x2, y2; + /* Should avoid underflow on the values we'll get */ + if (Inlines.ABS16(x) + Inlines.ABS16(y) < 1e-9f) { + x *= 1e12f; + y *= 1e12f; + } + x2 = x * x; + y2 = y * y; + if (x2 < y2) { + float den = (y2 + cB * x2) * (y2 + cC * x2); + if (den != 0) { + return -x * y * (y2 + cA * x2) / den + (y < 0 ? -cE : cE); + } else { + return (y < 0 ? -cE : cE); + } + } else { + float den = (x2 + cB * y2) * (x2 + cC * y2); + if (den != 0) { + return x * y * (x2 + cA * y2) / den + (y < 0 ? -cE : cE) - (x * y < 0 ? -cE : cE); + } else { + return (y < 0 ? -cE : cE) - (x * y < 0 ? -cE : cE); + } + } + } + + static void tonality_analysis_init(TonalityAnalysisState tonal) { + tonal.Reset(); + } + + static void tonality_get_info(TonalityAnalysisState tonal, AnalysisInfo info_out, int len) { + int pos; + int curr_lookahead; + float psum; + int i; + + pos = tonal.read_pos; + curr_lookahead = tonal.write_pos - tonal.read_pos; + if (curr_lookahead < 0) { + curr_lookahead += OpusConstants.DETECT_SIZE; + } + + if (len > 480 && pos != tonal.write_pos) { + pos++; + if (pos == OpusConstants.DETECT_SIZE) { + pos = 0; + } + } + if (pos == tonal.write_pos) { + pos--; + } + if (pos < 0) { + pos = OpusConstants.DETECT_SIZE - 1; + } + + info_out.Assign(tonal.info[pos]); + tonal.read_subframe += len / 120; + while (tonal.read_subframe >= 4) { + tonal.read_subframe -= 4; + tonal.read_pos++; + } + if (tonal.read_pos >= OpusConstants.DETECT_SIZE) { + tonal.read_pos -= OpusConstants.DETECT_SIZE; + } + + /* Compensate for the delay in the features themselves. + FIXME: Need a better estimate the 10 I just made up */ + curr_lookahead = Inlines.IMAX(curr_lookahead - 10, 0); + + psum = 0; + /* Summing the probability of transition patterns that involve music at + time (DETECT_SIZE-curr_lookahead-1) */ + for (i = 0; i < OpusConstants.DETECT_SIZE - curr_lookahead; i++) { + psum += tonal.pmusic[i]; + } + for (; i < OpusConstants.DETECT_SIZE; i++) { + psum += tonal.pspeech[i]; + } + psum = psum * tonal.music_confidence + (1 - psum) * tonal.speech_confidence; + /*printf("%f %f %f\n", psum, info_out.music_prob, info_out.tonality);*/ + + info_out.music_prob = psum; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + static void tonality_analysis(TonalityAnalysisState tonal, CeltMode celt_mode, short[] x, int x_ptr, int len, int offset, int c1, int c2, int C, int lsb_depth) { + int i, b; + FFTState kfft; + int[] input; + int[] output; + int N = 480, N2 = 240; + float[] A = tonal.angle; + float[] dA = tonal.d_angle; + float[] d2A = tonal.d2_angle; + float[] tonality; + float[] noisiness; + float[] band_tonality = new float[OpusConstants.NB_TBANDS]; + float[] logE = new float[OpusConstants.NB_TBANDS]; + float[] BFCC = new float[8]; + float[] features = new float[25]; + float frame_tonality; + float max_frame_tonality; + /*float tw_sum=0;*/ + float frame_noisiness; + float pi4 = (float) (M_PI * M_PI * M_PI * M_PI); + float slope = 0; + float frame_stationarity; + float relativeE; + float[] frame_probs = new float[2]; + float alpha, alphaE, alphaE2; + float frame_loudness; + float bandwidth_mask; + int bandwidth = 0; + float maxE = 0; + float noise_floor; + int remaining; + AnalysisInfo info; //[porting note] pointer + + tonal.last_transition++; + alpha = 1.0f / Inlines.IMIN(20, 1 + tonal.count); + alphaE = 1.0f / Inlines.IMIN(50, 1 + tonal.count); + alphaE2 = 1.0f / Inlines.IMIN(1000, 1 + tonal.count); + + if (tonal.count < 4) { + tonal.music_prob = 0.5f; + } + kfft = celt_mode.mdct.kfft[0]; + if (tonal.count == 0) { + tonal.mem_fill = 240; + } + + Downmix.downmix_int(x, x_ptr, tonal.inmem, tonal.mem_fill, Inlines.IMIN(len, OpusConstants.ANALYSIS_BUF_SIZE - tonal.mem_fill), offset, c1, c2, C); + + if (tonal.mem_fill + len < OpusConstants.ANALYSIS_BUF_SIZE) { + tonal.mem_fill += len; + /* Don't have enough to update the analysis */ + return; + } + + info = tonal.info[tonal.write_pos++]; + if (tonal.write_pos >= OpusConstants.DETECT_SIZE) { + tonal.write_pos -= OpusConstants.DETECT_SIZE; + } + + input = new int[960]; + output = new int[960]; + tonality = new float[240]; + noisiness = new float[240]; + for (i = 0; i < N2; i++) { + float w = OpusTables.analysis_window[i]; + input[2 * i] = (int) (w * tonal.inmem[i]); + input[2 * i + 1] = (int) (w * tonal.inmem[N2 + i]); + input[(2 * (N - i - 1))] = (int) (w * tonal.inmem[N - i - 1]); + input[(2 * (N - i - 1)) + 1] = (int) (w * tonal.inmem[N + N2 - i - 1]); + } + Arrays.MemMove(tonal.inmem, OpusConstants.ANALYSIS_BUF_SIZE - 240, 0, 240); + + remaining = len - (OpusConstants.ANALYSIS_BUF_SIZE - tonal.mem_fill); + Downmix.downmix_int(x, x_ptr, tonal.inmem, 240, remaining, offset + OpusConstants.ANALYSIS_BUF_SIZE - tonal.mem_fill, c1, c2, C); + tonal.mem_fill = 240 + remaining; + + KissFFT.opus_fft(kfft, input, output); + + for (i = 1; i < N2; i++) { + float X1r, X2r, X1i, X2i; + float angle, d_angle, d2_angle; + float angle2, d_angle2, d2_angle2; + float mod1, mod2, avg_mod; + X1r = (float) output[2 * i] + output[2 * (N - i)]; + X1i = (float) output[(2 * i) + 1] - output[2 * (N - i) + 1]; + X2r = (float) output[(2 * i) + 1] + output[2 * (N - i) + 1]; + X2i = (float) output[2 * (N - i)] - output[2 * i]; + + angle = (float) (.5f / M_PI) * fast_atan2f(X1i, X1r); + d_angle = angle - A[i]; + d2_angle = d_angle - dA[i]; + + angle2 = (float) (.5f / M_PI) * fast_atan2f(X2i, X2r); + d_angle2 = angle2 - angle; + d2_angle2 = d_angle2 - d_angle; + + mod1 = d2_angle - (float) Math.floor(0.5f + d2_angle); + noisiness[i] = Inlines.ABS16(mod1); + mod1 *= mod1; + mod1 *= mod1; + + mod2 = d2_angle2 - (float) Math.floor(0.5f + d2_angle2); + noisiness[i] += Inlines.ABS16(mod2); + mod2 *= mod2; + mod2 *= mod2; + + avg_mod = .25f * (d2A[i] + 2.0f * mod1 + mod2); + tonality[i] = 1.0f / (1.0f + 40.0f * 16.0f * pi4 * avg_mod) - .015f; + + A[i] = angle2; + dA[i] = d_angle2; + d2A[i] = mod2; + } + + frame_tonality = 0; + max_frame_tonality = 0; + /*tw_sum = 0;*/ + info.activity = 0; + frame_noisiness = 0; + frame_stationarity = 0; + if (tonal.count == 0) { + for (b = 0; b < OpusConstants.NB_TBANDS; b++) { + tonal.lowE[b] = 1e10f; + tonal.highE[b] = -1e10f; + } + } + relativeE = 0; + frame_loudness = 0; + for (b = 0; b < OpusConstants.NB_TBANDS; b++) { + float E = 0, tE = 0, nE = 0; + float L1, L2; + float stationarity; + for (i = OpusTables.tbands[b]; i < OpusTables.tbands[b + 1]; i++) { + float binE = output[2 * i] * (float) output[2 * i] + output[2 * (N - i)] * (float) output[2 * (N - i)] + + output[2 * i + 1] * (float) output[2 * i + 1] + output[2 * (N - i) + 1] * (float) output[2 * (N - i) + 1]; + /* FIXME: It's probably best to change the BFCC filter initial state instead */ + binE *= 5.55e-17f; + E += binE; + tE += binE * tonality[i]; + nE += binE * 2.0f * (.5f - noisiness[i]); + } + + tonal.E[tonal.E_count][b] = E; + frame_noisiness += nE / (1e-15f + E); + + frame_loudness += (float) Math.sqrt(E + 1e-10f); + logE[b] = (float) Math.log(E + 1e-10f); + tonal.lowE[b] = Inlines.MIN32(logE[b], tonal.lowE[b] + 0.01f); + tonal.highE[b] = Inlines.MAX32(logE[b], tonal.highE[b] - 0.1f); + if (tonal.highE[b] < tonal.lowE[b] + 1.0f) { + tonal.highE[b] += 0.5f; + tonal.lowE[b] -= 0.5f; + } + relativeE += (logE[b] - tonal.lowE[b]) / (1e-15f + tonal.highE[b] - tonal.lowE[b]); + + L1 = L2 = 0; + for (i = 0; i < OpusConstants.NB_FRAMES; i++) { + L1 += (float) Math.sqrt(tonal.E[i][b]); + L2 += tonal.E[i][b]; + } + + stationarity = Inlines.MIN16(0.99f, L1 / (float) Math.sqrt(1e-15 + OpusConstants.NB_FRAMES * L2)); + stationarity *= stationarity; + stationarity *= stationarity; + frame_stationarity += stationarity; + /*band_tonality[b] = tE/(1e-15+E)*/ + band_tonality[b] = Inlines.MAX16(tE / (1e-15f + E), stationarity * tonal.prev_band_tonality[b]); + frame_tonality += band_tonality[b]; + if (b >= OpusConstants.NB_TBANDS - OpusConstants.NB_TONAL_SKIP_BANDS) { + frame_tonality -= band_tonality[b - OpusConstants.NB_TBANDS + OpusConstants.NB_TONAL_SKIP_BANDS]; + } + max_frame_tonality = Inlines.MAX16(max_frame_tonality, (1.0f + .03f * (b - OpusConstants.NB_TBANDS)) * frame_tonality); + slope += band_tonality[b] * (b - 8); + tonal.prev_band_tonality[b] = band_tonality[b]; + } + + bandwidth_mask = 0; + bandwidth = 0; + maxE = 0; + noise_floor = 5.7e-4f / (1 << (Inlines.IMAX(0, lsb_depth - 8))); + noise_floor *= 1 << (15 + CeltConstants.SIG_SHIFT); + noise_floor *= noise_floor; + for (b = 0; b < OpusConstants.NB_TOT_BANDS; b++) { + float E = 0; + int band_start, band_end; + /* Keep a margin of 300 Hz for aliasing */ + band_start = OpusTables.extra_bands[b]; + band_end = OpusTables.extra_bands[b + 1]; + for (i = band_start; i < band_end; i++) { + float binE = output[2 * i] * (float) output[2 * i] + output[2 * (N - i)] * (float) output[2 * (N - i)] + + output[2 * i + 1] * (float) output[2 * i + 1] + output[2 * (N - i) + 1] * (float) output[2 * (N - i) + 1]; + E += binE; + } + maxE = Inlines.MAX32(maxE, E); + tonal.meanE[b] = Inlines.MAX32((1 - alphaE2) * tonal.meanE[b], E); + E = Inlines.MAX32(E, tonal.meanE[b]); + /* Use a simple follower with 13 dB/Bark slope for spreading function */ + bandwidth_mask = Inlines.MAX32(.05f * bandwidth_mask, E); + /* Consider the band "active" only if all these conditions are met: + 1) less than 10 dB below the simple follower + 2) less than 90 dB below the peak band (maximal masking possible considering + both the ATH and the loudness-dependent slope of the spreading function) + 3) above the PCM quantization noise floor + */ + if (E > .1 * bandwidth_mask && E * 1e9f > maxE && E > noise_floor * (band_end - band_start)) { + bandwidth = b; + } + } + if (tonal.count <= 2) { + bandwidth = 20; + } + frame_loudness = 20 * (float) Math.log10(frame_loudness); + tonal.Etracker = Inlines.MAX32(tonal.Etracker - .03f, frame_loudness); + tonal.lowECount *= (1 - alphaE); + if (frame_loudness < tonal.Etracker - 30) { + tonal.lowECount += alphaE; + } + + for (i = 0; i < 8; i++) { + float sum = 0; + for (b = 0; b < 16; b++) { + sum += OpusTables.dct_table[i * 16 + b] * logE[b]; + } + BFCC[i] = sum; + } + + frame_stationarity /= OpusConstants.NB_TBANDS; + relativeE /= OpusConstants.NB_TBANDS; + if (tonal.count < 10) { + relativeE = 0.5f; + } + frame_noisiness /= OpusConstants.NB_TBANDS; + info.activity = frame_noisiness + (1 - frame_noisiness) * relativeE; + frame_tonality = (max_frame_tonality / (OpusConstants.NB_TBANDS - OpusConstants.NB_TONAL_SKIP_BANDS)); + frame_tonality = Inlines.MAX16(frame_tonality, tonal.prev_tonality * .8f); + tonal.prev_tonality = frame_tonality; + + slope /= 8 * 8; + info.tonality_slope = slope; + + tonal.E_count = (tonal.E_count + 1) % OpusConstants.NB_FRAMES; + tonal.count++; + info.tonality = frame_tonality; + + for (i = 0; i < 4; i++) { + features[i] = -0.12299f * (BFCC[i] + tonal.mem[i + 24]) + 0.49195f * (tonal.mem[i] + tonal.mem[i + 16]) + 0.69693f * tonal.mem[i + 8] - 1.4349f * tonal.cmean[i]; + } + + for (i = 0; i < 4; i++) { + tonal.cmean[i] = (1 - alpha) * tonal.cmean[i] + alpha * BFCC[i]; + } + + for (i = 0; i < 4; i++) { + features[4 + i] = 0.63246f * (BFCC[i] - tonal.mem[i + 24]) + 0.31623f * (tonal.mem[i] - tonal.mem[i + 16]); + } + for (i = 0; i < 3; i++) { + features[8 + i] = 0.53452f * (BFCC[i] + tonal.mem[i + 24]) - 0.26726f * (tonal.mem[i] + tonal.mem[i + 16]) - 0.53452f * tonal.mem[i + 8]; + } + + if (tonal.count > 5) { + for (i = 0; i < 9; i++) { + tonal.std[i] = (1 - alpha) * tonal.std[i] + alpha * features[i] * features[i]; + } + } + + for (i = 0; i < 8; i++) { + tonal.mem[i + 24] = tonal.mem[i + 16]; + tonal.mem[i + 16] = tonal.mem[i + 8]; + tonal.mem[i + 8] = tonal.mem[i]; + tonal.mem[i] = BFCC[i]; + } + for (i = 0; i < 9; i++) { + features[11 + i] = (float) Math.sqrt(tonal.std[i]); + } + features[20] = info.tonality; + features[21] = info.activity; + features[22] = frame_stationarity; + features[23] = info.tonality_slope; + features[24] = tonal.lowECount; + + if (info.enabled) { + MultiLayerPerceptron.mlp_process(OpusTables.net, features, frame_probs); + frame_probs[0] = .5f * (frame_probs[0] + 1); + /* Curve fitting between the MLP probability and the actual probability */ + frame_probs[0] = .01f + 1.21f * frame_probs[0] * frame_probs[0] - .23f * (float) Math.pow(frame_probs[0], 10); + /* Probability of active audio (as opposed to silence) */ + frame_probs[1] = .5f * frame_probs[1] + .5f; + /* Consider that silence has a 50-50 probability. */ + frame_probs[0] = frame_probs[1] * frame_probs[0] + (1 - frame_probs[1]) * .5f; + + /*printf("%f %f ", frame_probs[0], frame_probs[1]);*/ + { + /* Probability of state transition */ + float tau; + /* Represents independence of the MLP probabilities, where + beta=1 means fully independent. */ + float beta; + /* Denormalized probability of speech (p0) and music (p1) after update */ + float p0, p1; + /* Probabilities for "all speech" and "all music" */ + float s0, m0; + /* Probability sum for renormalisation */ + float psum; + /* Instantaneous probability of speech and music, with beta pre-applied. */ + float speech0; + float music0; + + /* One transition every 3 minutes of active audio */ + tau = .00005f * frame_probs[1]; + beta = .05f; + //if (1) + { + /* Adapt beta based on how "unexpected" the new prob is */ + float p, q; + p = Inlines.MAX16(.05f, Inlines.MIN16(.95f, frame_probs[0])); + q = Inlines.MAX16(.05f, Inlines.MIN16(.95f, tonal.music_prob)); + beta = .01f + .05f * Inlines.ABS16(p - q) / (p * (1 - q) + q * (1 - p)); + } + /* p0 and p1 are the probabilities of speech and music at this frame + using only information from previous frame and applying the + state transition model */ + p0 = (1 - tonal.music_prob) * (1 - tau) + tonal.music_prob * tau; + p1 = tonal.music_prob * (1 - tau) + (1 - tonal.music_prob) * tau; + /* We apply the current probability with exponent beta to work around + the fact that the probability estimates aren't independent. */ + p0 *= (float) Math.pow(1 - frame_probs[0], beta); + p1 *= (float) Math.pow(frame_probs[0], beta); + /* Normalise the probabilities to get the Marokv probability of music. */ + tonal.music_prob = p1 / (p0 + p1); + info.music_prob = tonal.music_prob; + + /* This chunk of code deals with delayed decision. */ + psum = 1e-20f; + /* Instantaneous probability of speech and music, with beta pre-applied. */ + speech0 = (float) Math.pow(1 - frame_probs[0], beta); + music0 = (float) Math.pow(frame_probs[0], beta); + if (tonal.count == 1) { + tonal.pspeech[0] = 0.5f; + tonal.pmusic[0] = 0.5f; + } + /* Updated probability of having only speech (s0) or only music (m0), + before considering the new observation. */ + s0 = tonal.pspeech[0] + tonal.pspeech[1]; + m0 = tonal.pmusic[0] + tonal.pmusic[1]; + /* Updates s0 and m0 with instantaneous probability. */ + tonal.pspeech[0] = s0 * (1 - tau) * speech0; + tonal.pmusic[0] = m0 * (1 - tau) * music0; + /* Propagate the transition probabilities */ + for (i = 1; i < OpusConstants.DETECT_SIZE - 1; i++) { + tonal.pspeech[i] = tonal.pspeech[i + 1] * speech0; + tonal.pmusic[i] = tonal.pmusic[i + 1] * music0; + } + /* Probability that the latest frame is speech, when all the previous ones were music. */ + tonal.pspeech[OpusConstants.DETECT_SIZE - 1] = m0 * tau * speech0; + /* Probability that the latest frame is music, when all the previous ones were speech. */ + tonal.pmusic[OpusConstants.DETECT_SIZE - 1] = s0 * tau * music0; + + /* Renormalise probabilities to 1 */ + for (i = 0; i < OpusConstants.DETECT_SIZE; i++) { + psum += tonal.pspeech[i] + tonal.pmusic[i]; + } + psum = 1.0f / psum; + for (i = 0; i < OpusConstants.DETECT_SIZE; i++) { + tonal.pspeech[i] *= psum; + tonal.pmusic[i] *= psum; + } + psum = tonal.pmusic[0]; + for (i = 1; i < OpusConstants.DETECT_SIZE; i++) { + psum += tonal.pspeech[i]; + } + + /* Estimate our confidence in the speech/music decisions */ + if (frame_probs[1] > .75) { + if (tonal.music_prob > .9) { + float adapt; + adapt = 1.0f / (++tonal.music_confidence_count); + tonal.music_confidence_count = Inlines.IMIN(tonal.music_confidence_count, 500); + tonal.music_confidence += adapt * Inlines.MAX16(-.2f, frame_probs[0] - tonal.music_confidence); + } + if (tonal.music_prob < .1) { + float adapt; + adapt = 1.0f / (++tonal.speech_confidence_count); + tonal.speech_confidence_count = Inlines.IMIN(tonal.speech_confidence_count, 500); + tonal.speech_confidence += adapt * Inlines.MIN16(.2f, frame_probs[0] - tonal.speech_confidence); + } + } else { + if (tonal.music_confidence_count == 0) { + tonal.music_confidence = .9f; + } + if (tonal.speech_confidence_count == 0) { + tonal.speech_confidence = .1f; + } + } + } + if (tonal.last_music != ((tonal.music_prob > .5f) ? 1 : 0)) { + tonal.last_transition = 0; + } + tonal.last_music = (tonal.music_prob > .5f) ? 1 : 0; + } else { + info.music_prob = 0; + } + + info.bandwidth = bandwidth; + info.noisiness = frame_noisiness; + info.valid = 1; + } + + static void run_analysis(TonalityAnalysisState analysis, CeltMode celt_mode, short[] analysis_pcm, int analysis_pcm_ptr, + int analysis_frame_size, int frame_size, int c1, int c2, int C, int Fs, + int lsb_depth, AnalysisInfo analysis_info) { + int offset; + int pcm_len; + + if (analysis_pcm != null) { + /* Avoid overflow/wrap-around of the analysis buffer */ + analysis_frame_size = Inlines.IMIN((OpusConstants.DETECT_SIZE - 5) * Fs / 100, analysis_frame_size); + + pcm_len = analysis_frame_size - analysis.analysis_offset; + offset = analysis.analysis_offset; + do { + tonality_analysis(analysis, celt_mode, analysis_pcm, analysis_pcm_ptr, Inlines.IMIN(480, pcm_len), offset, c1, c2, C, lsb_depth); + offset += 480; + pcm_len -= 480; + } while (pcm_len > 0); + analysis.analysis_offset = analysis_frame_size; + + analysis.analysis_offset -= frame_size; + } + + analysis_info.valid = 0; + tonality_get_info(analysis, analysis_info, frame_size); + } +} diff --git a/app/src/main/java/org/concentus/AnalysisInfo.java b/app/src/main/java/org/concentus/AnalysisInfo.java new file mode 100644 index 0000000000..aeb0e7378c --- /dev/null +++ b/app/src/main/java/org/concentus/AnalysisInfo.java @@ -0,0 +1,70 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class AnalysisInfo { + + boolean enabled = false; + int valid = 0; + float tonality = 0; + float tonality_slope = 0; + float noisiness = 0; + float activity = 0; + float music_prob = 0; + int bandwidth = 0; + + AnalysisInfo() { + } + + void Assign(AnalysisInfo other) { + this.valid = other.valid; + this.tonality = other.tonality; + this.tonality_slope = other.tonality_slope; + this.noisiness = other.noisiness; + this.activity = other.activity; + this.music_prob = other.music_prob; + this.bandwidth = other.bandwidth; + } + + void Reset() { + valid = 0; + tonality = 0; + tonality_slope = 0; + noisiness = 0; + activity = 0; + music_prob = 0; + bandwidth = 0; + } +} diff --git a/app/src/main/java/org/concentus/ApplySineWindow.java b/app/src/main/java/org/concentus/ApplySineWindow.java new file mode 100644 index 0000000000..409b3f604b --- /dev/null +++ b/app/src/main/java/org/concentus/ApplySineWindow.java @@ -0,0 +1,106 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class ApplySineWindow { + + /* Apply sine window to signal vector. */ + /* Window types: */ + /* 1 . sine window from 0 to pi/2 */ + /* 2 . sine window from pi/2 to pi */ + /* Every other sample is linearly interpolated, for speed. */ + /* Window length must be between 16 and 120 (incl) and a multiple of 4. */ + + /* Matlab code for table: + for k=16:9*4:16+2*9*4, fprintf(' %7.d,', -round(65536*pi ./ (k:4:k+8*4))); fprintf('\n'); end + */ + private static final short[] freq_table_Q16 = { + 12111, 9804, 8235, 7100, 6239, 5565, 5022, 4575, 4202, + 3885, 3612, 3375, 3167, 2984, 2820, 2674, 2542, 2422, + 2313, 2214, 2123, 2038, 1961, 1889, 1822, 1760, 1702,}; + + static void silk_apply_sine_window( + short[] px_win, /* O Pointer to windowed signal */ + int px_win_ptr, + short[] px, /* I Pointer to input signal */ + int px_ptr, + int win_type, /* I Selects a window type */ + int length /* I Window length, multiple of 4 */ + ) { + int k, f_Q16, c_Q16; + int S0_Q16, S1_Q16; + + Inlines.OpusAssert(win_type == 1 || win_type == 2); + + /* Length must be in a range from 16 to 120 and a multiple of 4 */ + Inlines.OpusAssert(length >= 16 && length <= 120); + Inlines.OpusAssert((length & 3) == 0); + + /* Frequency */ + k = (length >> 2) - 4; + Inlines.OpusAssert(k >= 0 && k <= 26); + f_Q16 = (int) freq_table_Q16[k]; + + /* Factor used for cosine approximation */ + c_Q16 = Inlines.silk_SMULWB((int) f_Q16, -f_Q16); + Inlines.OpusAssert(c_Q16 >= -32768); + + /* initialize state */ + if (win_type == 1) { + /* start from 0 */ + S0_Q16 = 0; + /* approximation of sin(f) */ + S1_Q16 = f_Q16 + Inlines.silk_RSHIFT(length, 3); + } else { + /* start from 1 */ + S0_Q16 = ((int) 1 << 16); + /* approximation of cos(f) */ + S1_Q16 = ((int) 1 << 16) + Inlines.silk_RSHIFT(c_Q16, 1) + Inlines.silk_RSHIFT(length, 4); + } + + /* Uses the recursive equation: sin(n*f) = 2 * cos(f) * sin((n-1)*f) - sin((n-2)*f) */ + /* 4 samples at a time */ + for (k = 0; k < length; k += 4) { + int pxwk = px_win_ptr + k; + int pxk = px_ptr + k; + px_win[pxwk] = (short) Inlines.silk_SMULWB(Inlines.silk_RSHIFT(S0_Q16 + S1_Q16, 1), px[pxk]); + px_win[pxwk + 1] = (short) Inlines.silk_SMULWB(S1_Q16, px[pxk + 1]); + S0_Q16 = Inlines.silk_SMULWB(S1_Q16, c_Q16) + Inlines.silk_LSHIFT(S1_Q16, 1) - S0_Q16 + 1; + S0_Q16 = Inlines.silk_min(S0_Q16, ((int) 1 << 16)); + + px_win[pxwk + 2] = (short) Inlines.silk_SMULWB(Inlines.silk_RSHIFT(S0_Q16 + S1_Q16, 1), px[pxk + 2]); + px_win[pxwk + 3] = (short) Inlines.silk_SMULWB(S0_Q16, px[pxk + 3]); + S1_Q16 = Inlines.silk_SMULWB(S0_Q16, c_Q16) + Inlines.silk_LSHIFT(S0_Q16, 1) - S1_Q16; + S1_Q16 = Inlines.silk_min(S1_Q16, ((int) 1 << 16)); + } + } +} diff --git a/app/src/main/java/org/concentus/Arrays.java b/app/src/main/java/org/concentus/Arrays.java new file mode 100644 index 0000000000..6879b90918 --- /dev/null +++ b/app/src/main/java/org/concentus/Arrays.java @@ -0,0 +1,111 @@ +/* Copyright (c) 2016 Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class Arrays { + + static int[][] InitTwoDimensionalArrayInt(int x, int y) { + return new int[x][y]; + } + + static float[][] InitTwoDimensionalArrayFloat(int x, int y) { + return new float[x][y]; + } + + static short[][] InitTwoDimensionalArrayShort(int x, int y) { + return new short[x][y]; + } + + static byte[][] InitTwoDimensionalArrayByte(int x, int y) { + return new byte[x][y]; + } + + static byte[][][] InitThreeDimensionalArrayByte(int x, int y, int z) { + return new byte[x][y][z]; + } + + static void MemSet(byte[] array, byte value) { + java.util.Arrays.fill(array, value); + } + + static void MemSet(short[] array, short value) { + java.util.Arrays.fill(array, value); + } + + static void MemSet(int[] array, int value) { + java.util.Arrays.fill(array, value); + } + + static void MemSet(float[] array, float value) { + java.util.Arrays.fill(array, value); + } + + static void MemSet(byte[] array, byte value, int length) { + java.util.Arrays.fill(array, 0, length, value); + } + + static void MemSet(short[] array, short value, int length) { + java.util.Arrays.fill(array, 0, length, value); + } + + static void MemSet(int[] array, int value, int length) { + java.util.Arrays.fill(array, 0, length, value); + } + + static void MemSet(float[] array, float value, int length) { + java.util.Arrays.fill(array, 0, length, value); + } + + static void MemSetWithOffset(byte[] array, byte value, int offset, int length) { + java.util.Arrays.fill(array, offset, offset + length, value); + } + + static void MemSetWithOffset(short[] array, short value, int offset, int length) { + java.util.Arrays.fill(array, offset, offset + length, value); + } + + static void MemSetWithOffset(int[] array, int value, int offset, int length) { + java.util.Arrays.fill(array, offset, offset + length, value); + } + + static void MemMove(byte[] array, int src_idx, int dst_idx, int length) { + System.arraycopy(array, src_idx, array, dst_idx, length); + } + + static void MemMove(short[] array, int src_idx, int dst_idx, int length) { + System.arraycopy(array, src_idx, array, dst_idx, length); + } + + static void MemMove(int[] array, int src_idx, int dst_idx, int length) { + System.arraycopy(array, src_idx, array, dst_idx, length); + } + +} diff --git a/app/src/main/java/org/concentus/Autocorrelation.java b/app/src/main/java/org/concentus/Autocorrelation.java new file mode 100644 index 0000000000..b920493ff4 --- /dev/null +++ b/app/src/main/java/org/concentus/Autocorrelation.java @@ -0,0 +1,257 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class Autocorrelation { + + /* Compute autocorrelation */ + static void silk_autocorr( + int[] results, /* O Result (length correlationCount) */ + BoxedValueInt scale, /* O Scaling of the correlation vector */ + short[] inputData, /* I Input data to correlate */ + int inputDataSize, /* I Length of input */ + int correlationCount /* I Number of correlation taps to compute */ + ) { + int corrCount = Inlines.silk_min_int(inputDataSize, correlationCount); + scale.Val = Autocorrelation._celt_autocorr(inputData, results, corrCount - 1, inputDataSize); + } + + static int _celt_autocorr( + short[] x, /* in: [0...n-1] samples x */ + int[] ac, /* out: [0...lag-1] ac values */ + int lag, + int n + ) { + int d; + int i, k; + int fastN = n - lag; + int shift; + short[] xptr; + short[] xx = new short[n]; + Inlines.OpusAssert(n > 0); + xptr = x; + + shift = 0; + { + int ac0; + ac0 = 1 + (n << 7); + if ((n & 1) != 0) { + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[0], xptr[0]), 9); + } + for (i = (n & 1); i < n; i += 2) { + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i], xptr[i]), 9); + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i + 1], xptr[i + 1]), 9); + } + shift = Inlines.celt_ilog2(ac0) - 30 + 10; + shift = (shift) / 2; + if (shift > 0) { + for (i = 0; i < n; i++) { + xx[i] = (short) (Inlines.PSHR32(xptr[i], shift)); + } + xptr = xx; + } else { + shift = 0; + } + } + CeltPitchXCorr.pitch_xcorr(xptr, xptr, ac, fastN, lag + 1); + for (k = 0; k <= lag; k++) { + for (i = k + fastN, d = 0; i < n; i++) { + d = Inlines.MAC16_16(d, xptr[i], xptr[i - k]); + } + ac[k] += d; + } + shift = 2 * shift; + if (shift <= 0) { + ac[0] += Inlines.SHL32((int) 1, -shift); + } + if (ac[0] < 268435456) { + int shift2 = 29 - Inlines.EC_ILOG(ac[0]); + for (i = 0; i <= lag; i++) { + ac[i] = Inlines.SHL32(ac[i], shift2); + } + shift -= shift2; + } else if (ac[0] >= 536870912) { + int shift2 = 1; + if (ac[0] >= 1073741824) { + shift2++; + } + for (i = 0; i <= lag; i++) { + ac[i] = Inlines.SHR32(ac[i], shift2); + } + shift += shift2; + } + + return shift; + } + + static int _celt_autocorr( + int[] x, /* in: [0...n-1] samples x */ + int[] ac, /* out: [0...lag-1] ac values */ + int[] window, + int overlap, + int lag, + int n) { + int d; + int i, k; + int fastN = n - lag; + int shift; + int[] xptr; + int[] xx = new int[n]; + + Inlines.OpusAssert(n > 0); + Inlines.OpusAssert(overlap >= 0); + + if (overlap == 0) { + xptr = x; + } else { + for (i = 0; i < n; i++) { + xx[i] = x[i]; + } + for (i = 0; i < overlap; i++) { + xx[i] = Inlines.MULT16_16_Q15(x[i], window[i]); + xx[n - i - 1] = Inlines.MULT16_16_Q15(x[n - i - 1], window[i]); + } + xptr = xx; + } + + shift = 0; + + int ac0; + ac0 = 1 + (n << 7); + if ((n & 1) != 0) { + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[0], xptr[0]), 9); + } + + for (i = (n & 1); i < n; i += 2) { + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i], xptr[i]), 9); + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i + 1], xptr[i + 1]), 9); + } + + shift = Inlines.celt_ilog2(ac0) - 30 + 10; + shift = (shift) / 2; + if (shift > 0) { + for (i = 0; i < n; i++) { + xx[i] = (Inlines.PSHR32(xptr[i], shift)); + } + xptr = xx; + } else { + shift = 0; + } + + CeltPitchXCorr.pitch_xcorr(xptr, xptr, ac, fastN, lag + 1); + for (k = 0; k <= lag; k++) { + for (i = k + fastN, d = 0; i < n; i++) { + d = Inlines.MAC16_16(d, xptr[i], xptr[i - k]); + } + ac[k] += d; + } + + shift = 2 * shift; + if (shift <= 0) { + ac[0] += Inlines.SHL32((int) 1, -shift); + } + if (ac[0] < 268435456) { + int shift2 = 29 - Inlines.EC_ILOG(ac[0]); + for (i = 0; i <= lag; i++) { + ac[i] = Inlines.SHL32(ac[i], shift2); + } + shift -= shift2; + } else if (ac[0] >= 536870912) { + int shift2 = 1; + if (ac[0] >= 1073741824) { + shift2++; + } + for (i = 0; i <= lag; i++) { + ac[i] = Inlines.SHR32(ac[i], shift2); + } + shift += shift2; + } + + return shift; + } + + private static final int QC = 10; + private static final int QS = 14; + + /* Autocorrelations for a warped frequency axis */ + static void silk_warped_autocorrelation( + int[] corr, /* O Result [order + 1] */ + BoxedValueInt scale, /* O Scaling of the correlation vector */ + short[] input, /* I Input data to correlate */ + int warping_Q16, /* I Warping coefficient */ + int length, /* I Length of input */ + int order /* I Correlation order (even) */ + ) { + int n, i, lsh; + int tmp1_QS, tmp2_QS; + int[] state_QS = new int[SilkConstants.MAX_SHAPE_LPC_ORDER + 1];// = { 0 }; + long[] corr_QC = new long[SilkConstants.MAX_SHAPE_LPC_ORDER + 1];// = { 0 }; + + /* Order must be even */ + Inlines.OpusAssert((order & 1) == 0); + Inlines.OpusAssert(2 * QS - QC >= 0); + + /* Loop over samples */ + for (n = 0; n < length; n++) { + tmp1_QS = Inlines.silk_LSHIFT32((int) input[n], QS); + /* Loop over allpass sections */ + for (i = 0; i < order; i += 2) { + /* Output of allpass section */ + tmp2_QS = Inlines.silk_SMLAWB(state_QS[i], state_QS[i + 1] - tmp1_QS, warping_Q16); + state_QS[i] = tmp1_QS; + corr_QC[i] += Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp1_QS, state_QS[0]), 2 * QS - QC); + /* Output of allpass section */ + tmp1_QS = Inlines.silk_SMLAWB(state_QS[i + 1], state_QS[i + 2] - tmp2_QS, warping_Q16); + state_QS[i + 1] = tmp2_QS; + corr_QC[i + 1] += Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp2_QS, state_QS[0]), 2 * QS - QC); + } + state_QS[order] = tmp1_QS; + corr_QC[order] += Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp1_QS, state_QS[0]), 2 * QS - QC); + } + + lsh = Inlines.silk_CLZ64(corr_QC[0]) - 35; + lsh = Inlines.silk_LIMIT(lsh, -12 - QC, 30 - QC); + scale.Val = -(QC + lsh); + Inlines.OpusAssert(scale.Val >= -30 && scale.Val <= 12); + if (lsh >= 0) { + for (i = 0; i < order + 1; i++) { + corr[i] = (int) (Inlines.silk_LSHIFT64(corr_QC[i], lsh)); + } + } else { + for (i = 0; i < order + 1; i++) { + corr[i] = (int) (Inlines.silk_RSHIFT64(corr_QC[i], -lsh)); + } + } + Inlines.OpusAssert(corr_QC[0] >= 0); + /* If breaking, decrease QC*/ + } +} diff --git a/app/src/main/java/org/concentus/BWExpander.java b/app/src/main/java/org/concentus/BWExpander.java new file mode 100644 index 0000000000..ca28a3a65a --- /dev/null +++ b/app/src/main/java/org/concentus/BWExpander.java @@ -0,0 +1,78 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class BWExpander { + + /// + /// Chirp (bw expand) LP AR filter (Fixed point implementation) + /// + /// I/O AR filter to be expanded (without leading 1) + /// I length of ar + /// I chirp factor (typically in range (0..1) ) + static void silk_bwexpander_32( + int[] ar, /* I/O AR filter to be expanded (without leading 1) */ + int d, /* I Length of ar */ + int chirp_Q16 /* I Chirp factor in Q16 */ + ) { + int i; + int chirp_minus_one_Q16 = chirp_Q16 - 65536; + + for (i = 0; i < d - 1; i++) { + ar[i] = Inlines.silk_SMULWW(chirp_Q16, ar[i]); + chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, chirp_minus_one_Q16), 16); + } + ar[d - 1] = Inlines.silk_SMULWW(chirp_Q16, ar[d - 1]); + } + + /// + /// Chirp (bw expand) LP AR filter (Fixed point implementation) + /// + /// I/O AR filter to be expanded (without leading 1) + /// I length of ar + /// I chirp factor (typically in range (0..1) ) + static void silk_bwexpander( + short[] ar, + int d, + int chirp_Q16) { + int i; + int chirp_minus_one_Q16 = chirp_Q16 - 65536; + + /* NB: Dont use silk_SMULWB, instead of silk_RSHIFT_ROUND( silk_MUL(), 16 ), below. */ + /* Bias in silk_SMULWB can lead to unstable filters */ + for (i = 0; i < d - 1; i++) { + ar[i] = (short) Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, ar[i]), 16); + chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, chirp_minus_one_Q16), 16); + } + ar[d - 1] = (short) Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, ar[d - 1]), 16); + } +} diff --git a/app/src/main/java/org/concentus/Bands.java b/app/src/main/java/org/concentus/Bands.java new file mode 100644 index 0000000000..5b4da12388 --- /dev/null +++ b/app/src/main/java/org/concentus/Bands.java @@ -0,0 +1,1512 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class Bands { + + static int hysteresis_decision( + int val, + int[] thresholds, + int[] hysteresis, + int N, + int prev) { + int i; + for (i = 0; i < N; i++) { + if (val < thresholds[i]) { + break; + } + } + + if (i > prev && val < thresholds[prev] + hysteresis[prev]) { + i = prev; + } + + if (i < prev && val > thresholds[prev - 1] - hysteresis[prev - 1]) { + i = prev; + } + + return i; + } + + static int celt_lcg_rand(int seed) { + return (1664525 * seed + 1013904223); + } + + /* This is a cos() approximation designed to be bit-exact on any platform. Bit exactness + with this approximation is important because it has an impact on the bit allocation */ + static int bitexact_cos(int x) { + int tmp; + int x2; + tmp = (4096 + ((int) (x) * (x))) >> 13; + Inlines.OpusAssert(tmp <= 32767); + x2 = (tmp); + x2 = ((32767 - x2) + Inlines.FRAC_MUL16(x2, (-7651 + Inlines.FRAC_MUL16(x2, (8277 + Inlines.FRAC_MUL16(-626, x2)))))); + Inlines.OpusAssert(x2 <= 32766); + return (1 + x2); + } + + static int bitexact_log2tan(int isin, int icos) { + int lc = Inlines.EC_ILOG((long) icos); + int ls = Inlines.EC_ILOG((long) isin); + icos <<= 15 - lc; + isin <<= 15 - ls; + return (ls - lc) * (1 << 11) + + Inlines.FRAC_MUL16(isin, Inlines.FRAC_MUL16(isin, -2597) + 7932) + - Inlines.FRAC_MUL16(icos, Inlines.FRAC_MUL16(icos, -2597) + 7932); + } + + /* Compute the amplitude (sqrt energy) in each of the bands */ + static void compute_band_energies(CeltMode m, int[][] X, int[][] bandE, int end, int C, int LM) { + int i, c, N; + short[] eBands = m.eBands; + N = m.shortMdctSize << LM; + c = 0; + + do { + for (i = 0; i < end; i++) { + int j; + int maxval = 0; + int sum = 0; + maxval = Inlines.celt_maxabs32(X[c], (eBands[i] << LM), (eBands[i + 1] - eBands[i]) << LM); + if (maxval > 0) { + int shift = Inlines.celt_ilog2(maxval) - 14 + (((m.logN[i] >> EntropyCoder.BITRES) + LM + 1) >> 1); + j = eBands[i] << LM; + if (shift > 0) { + do { + sum = Inlines.MAC16_16(sum, Inlines.EXTRACT16(Inlines.SHR32(X[c][j], shift)), + Inlines.EXTRACT16(Inlines.SHR32(X[c][j], shift))); + } while (++j < eBands[i + 1] << LM); + } else { + do { + sum = Inlines.MAC16_16(sum, Inlines.EXTRACT16(Inlines.SHL32(X[c][j], -shift)), + Inlines.EXTRACT16(Inlines.SHL32(X[c][j], -shift))); + } while (++j < eBands[i + 1] << LM); + } + /* We're adding one here to ensure the normalized band isn't larger than unity norm */ + bandE[c][i] = CeltConstants.EPSILON + Inlines.VSHR32(Inlines.celt_sqrt(sum), -shift); + } else { + bandE[c][i] = CeltConstants.EPSILON; + } + /*printf ("%f ", bandE[i+c*m->nbEBands]);*/ + } + } while (++c < C); + } + + /* Normalise each band such that the energy is one. */ + static void normalise_bands(CeltMode m, int[][] freq, int[][] X, int[][] bandE, int end, int C, int M) { + int i, c; + short[] eBands = m.eBands; + c = 0; + do { + i = 0; + do { + int g; + int j, shift; + int E; + shift = Inlines.celt_zlog2(bandE[c][i]) - 13; + E = Inlines.VSHR32(bandE[c][i], shift); + g = Inlines.EXTRACT16(Inlines.celt_rcp(Inlines.SHL32(E, 3))); + j = M * eBands[i]; + do { + X[c][j] = Inlines.MULT16_16_Q15(Inlines.VSHR32(freq[c][j], shift - 1), g); + } while (++j < M * eBands[i + 1]); + } while (++i < end); + } while (++c < C); + } + + /* De-normalise the energy to produce the synthesis from the unit-energy bands */ + static void denormalise_bands(CeltMode m, int[] X, + int[] freq, int freq_ptr, int[] bandLogE, int bandLogE_ptr, int start, + int end, int M, int downsample, int silence) { + int i, N; + int bound; + int f; + int x; + short[] eBands = m.eBands; + N = M * m.shortMdctSize; + bound = M * eBands[end]; + if (downsample != 1) { + bound = Inlines.IMIN(bound, N / downsample); + } + if (silence != 0) { + bound = 0; + start = end = 0; + } + f = freq_ptr; + x = M * eBands[start]; + + for (i = 0; i < M * eBands[start]; i++) { + freq[f++] = 0; + } + + for (i = start; i < end; i++) { + int j, band_end; + int g; + int lg; + int shift; + + j = M * eBands[i]; + band_end = M * eBands[i + 1]; + lg = Inlines.ADD16(bandLogE[bandLogE_ptr + i], Inlines.SHL16(CeltTables.eMeans[i], 6)); + + /* Handle the integer part of the log energy */ + shift = 16 - (lg >> CeltConstants.DB_SHIFT); + if (shift > 31) { + shift = 0; + g = 0; + } else { + /* Handle the fractional part. */ + g = Inlines.celt_exp2_frac(lg & ((1 << CeltConstants.DB_SHIFT) - 1)); + } + /* Handle extreme gains with negative shift. */ + if (shift < 0) { + /* For shift < -2 we'd be likely to overflow, so we're capping + the gain here. This shouldn't happen unless the bitstream is + already corrupted. */ + if (shift < -2) { + g = 32767; + shift = -2; + } + do { + freq[f] = Inlines.SHR32(Inlines.MULT16_16(X[x], g), -shift); + } while (++j < band_end); + } else { + do { + freq[f++] = Inlines.SHR32(Inlines.MULT16_16(X[x++], g), shift); + } while (++j < band_end); + } + } + + Inlines.OpusAssert(start <= end); + Arrays.MemSetWithOffset(freq, 0, freq_ptr + bound, N - bound); + } + + /* This prevents energy collapse for transients with multiple short MDCTs */ + static void anti_collapse(CeltMode m, int[][] X_, short[] collapse_masks, int LM, int C, int size, + int start, int end, int[] logE, int[] prev1logE, + int[] prev2logE, int[] pulses, int seed) { + int c, i, j, k; + for (i = start; i < end; i++) { + int N0; + int thresh, sqrt_1; + int depth; + int shift; + int thresh32; + + N0 = m.eBands[i + 1] - m.eBands[i]; + /* depth in 1/8 bits */ + Inlines.OpusAssert(pulses[i] >= 0); + depth = Inlines.celt_udiv(1 + pulses[i], (m.eBands[i + 1] - m.eBands[i])) >> LM; + + thresh32 = Inlines.SHR32(Inlines.celt_exp2((0 - Inlines.SHL16((depth), 10 - EntropyCoder.BITRES))), 1); + thresh = (Inlines.MULT16_32_Q15(((short) (0.5 + (0.5f) * (((int) 1) << (15))))/*Inlines.QCONST16(0.5f, 15)*/, Inlines.MIN32(32767, thresh32))); + { + int t; + t = N0 << LM; + shift = Inlines.celt_ilog2(t) >> 1; + t = Inlines.SHL32(t, (7 - shift) << 1); + sqrt_1 = Inlines.celt_rsqrt_norm(t); + } + + c = 0; + do { + int X; + int prev1; + int prev2; + int Ediff; + int r; + int renormalize = 0; + prev1 = prev1logE[c * m.nbEBands + i]; + prev2 = prev2logE[c * m.nbEBands + i]; + if (C == 1) { + prev1 = Inlines.MAX16(prev1, prev1logE[m.nbEBands + i]); + prev2 = Inlines.MAX16(prev2, prev2logE[m.nbEBands + i]); + } + Ediff = Inlines.EXTEND32(logE[c * m.nbEBands + i]) - Inlines.EXTEND32(Inlines.MIN16(prev1, prev2)); + Ediff = Inlines.MAX32(0, Ediff); + + if (Ediff < 16384) { + int r32 = Inlines.SHR32(Inlines.celt_exp2((short) (0 - Inlines.EXTRACT16(Ediff))), 1); + r = (2 * Inlines.MIN16(16383, (r32))); + } else { + r = 0; + } + if (LM == 3) { + r = Inlines.MULT16_16_Q14(23170, Inlines.MIN32(23169, r)); // opus bug: was MIN32 + } + r = Inlines.SHR16(Inlines.MIN16(thresh, r), 1); + r = (Inlines.SHR32(Inlines.MULT16_16_Q15(sqrt_1, r), shift)); + + X = m.eBands[i] << LM; + for (k = 0; k < 1 << LM; k++) { + /* Detect collapse */ + if ((collapse_masks[i * C + c] & 1 << k) == 0) { + /* Fill with noise */ + int Xk = X + k; + for (j = 0; j < N0; j++) { + seed = celt_lcg_rand(seed); + X_[c][Xk + (j << LM)] = ((seed & 0x8000) != 0 ? r : 0 - r); + } + renormalize = 1; + } + } + /* We just added some energy, so we need to renormalise */ + if (renormalize != 0) { + VQ.renormalise_vector(X_[c], X, N0 << LM, CeltConstants.Q15ONE); + } + } while (++c < C); + } + } + + static void intensity_stereo(CeltMode m, int[] X, int X_ptr, int[] Y, int Y_ptr, int[][] bandE, int bandID, int N) { + int i = bandID; + int j; + int a1, a2; + int left, right; + int norm; + int shift = Inlines.celt_zlog2(Inlines.MAX32(bandE[0][i], bandE[1][i])) - 13; + left = Inlines.VSHR32(bandE[0][i], shift); + right = Inlines.VSHR32(bandE[1][i], shift); + norm = CeltConstants.EPSILON + Inlines.celt_sqrt(CeltConstants.EPSILON + Inlines.MULT16_16(left, left) + Inlines.MULT16_16(right, right)); + a1 = Inlines.DIV32_16(Inlines.SHL32(left, 14), norm); + a2 = Inlines.DIV32_16(Inlines.SHL32(right, 14), norm); + for (j = 0; j < N; j++) { + int r, l; + l = X[X_ptr + j]; + r = Y[Y_ptr + j]; + X[X_ptr + j] = Inlines.EXTRACT16(Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(a1, l), a2, r), 14)); + /* Side is not encoded, no need to calculate */ + } + } + + static void stereo_split(int[] X, int X_ptr, int[] Y, int Y_ptr, int N) { + int j; + for (j = 0; j < N; j++) { + int r, l; + l = Inlines.MULT16_16(((short) (0.5 + (.70710678f) * (((int) 1) << (15))))/*Inlines.QCONST16(.70710678f, 15)*/, X[X_ptr + j]); + r = Inlines.MULT16_16(((short) (0.5 + (.70710678f) * (((int) 1) << (15))))/*Inlines.QCONST16(.70710678f, 15)*/, Y[Y_ptr + j]); + X[X_ptr + j] = Inlines.EXTRACT16(Inlines.SHR32(Inlines.ADD32(l, r), 15)); + Y[Y_ptr + j] = Inlines.EXTRACT16(Inlines.SHR32(Inlines.SUB32(r, l), 15)); + } + } + + static void stereo_merge(int[] X, int X_ptr, int[] Y, int Y_ptr, int mid, int N) { + int j; + BoxedValueInt xp = new BoxedValueInt(0); + BoxedValueInt side = new BoxedValueInt(0); + int El, Er; + int mid2; + int kl, kr; + int t, lgain, rgain; + + /* Compute the norm of X+Y and X-Y as |X|^2 + |Y|^2 +/- sum(xy) */ + Kernels.dual_inner_prod(Y, Y_ptr, X, X_ptr, Y, Y_ptr, N, xp, side); + /* Compensating for the mid normalization */ + xp.Val = Inlines.MULT16_32_Q15(mid, xp.Val); + /* mid and side are in Q15, not Q14 like X and Y */ + mid2 = Inlines.SHR16(mid, 1); + El = Inlines.MULT16_16(mid2, mid2) + side.Val - (2 * xp.Val); + Er = Inlines.MULT16_16(mid2, mid2) + side.Val + (2 * xp.Val); + if (Er < ((int) (0.5 + (6e-4f) * (((int) 1) << (28))))/*Inlines.QCONST32(6e-4f, 28)*/ || El < ((int) (0.5 + (6e-4f) * (((int) 1) << (28))))/*Inlines.QCONST32(6e-4f, 28)*/) { + System.arraycopy(X, X_ptr, Y, Y_ptr, N); + return; + } + + kl = Inlines.celt_ilog2(El) >> 1; + kr = Inlines.celt_ilog2(Er) >> 1; + t = Inlines.VSHR32(El, (kl - 7) << 1); + lgain = Inlines.celt_rsqrt_norm(t); + t = Inlines.VSHR32(Er, (kr - 7) << 1); + rgain = Inlines.celt_rsqrt_norm(t); + + if (kl < 7) { + kl = 7; + } + if (kr < 7) { + kr = 7; + } + + for (j = 0; j < N; j++) { + int r, l; + /* Apply mid scaling (side is already scaled) */ + l = Inlines.MULT16_16_P15(mid, X[X_ptr + j]); + r = Y[Y_ptr + j]; + X[X_ptr + j] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MULT16_16(lgain, Inlines.SUB16(l, r)), kl + 1)); + Y[Y_ptr + j] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MULT16_16(rgain, Inlines.ADD16(l, r)), kr + 1)); + } + } + + /* Decide whether we should spread the pulses in the current frame */ + static int spreading_decision(CeltMode m, int[][] X, BoxedValueInt average, + int last_decision, BoxedValueInt hf_average, BoxedValueInt tapset_decision, int update_hf, + int end, int C, int M) { + int i, c; + int sum = 0, nbBands = 0; + short[] eBands = m.eBands; + int decision; + int hf_sum = 0; + + Inlines.OpusAssert(end > 0); + + if (M * (eBands[end] - eBands[end - 1]) <= 8) { + return Spread.SPREAD_NONE; + } + + c = 0; + + do { + for (i = 0; i < end; i++) { + int j, N, tmp = 0; + int[] tcount = {0, 0, 0}; + int[] x = X[c]; + int x_ptr = M * eBands[i]; + N = M * (eBands[i + 1] - eBands[i]); + if (N <= 8) { + continue; + } + /* Compute rough CDF of |x[j]| */ + for (j = x_ptr; j < N + x_ptr; j++) { + int x2N; + /* Q13 */ + + x2N = Inlines.MULT16_16(Inlines.MULT16_16_Q15(x[j], x[j]), N); + if (x2N < ((short) (0.5 + (0.25f) * (((int) 1) << (13))))/*Inlines.QCONST16(0.25f, 13)*/) { + tcount[0]++; + } + if (x2N < ((short) (0.5 + (0.0625f) * (((int) 1) << (13))))/*Inlines.QCONST16(0.0625f, 13)*/) { + tcount[1]++; + } + if (x2N < ((short) (0.5 + (0.015625f) * (((int) 1) << (13))))/*Inlines.QCONST16(0.015625f, 13)*/) { + tcount[2]++; + } + } + + /* Only include four last bands (8 kHz and up) */ + if (i > m.nbEBands - 4) { + hf_sum += Inlines.celt_udiv(32 * (tcount[1] + tcount[0]), N); + } + + tmp = (2 * tcount[2] >= N ? 1 : 0) + (2 * tcount[1] >= N ? 1 : 0) + (2 * tcount[0] >= N ? 1 : 0); + sum += tmp * 256; + nbBands++; + } + } while (++c < C); + + if (update_hf != 0) { + if (hf_sum != 0) { + hf_sum = Inlines.celt_udiv(hf_sum, C * (4 - m.nbEBands + end)); + } + + hf_average.Val = (hf_average.Val + hf_sum) >> 1; + hf_sum = hf_average.Val; + + if (tapset_decision.Val == 2) { + hf_sum += 4; + } else if (tapset_decision.Val == 0) { + hf_sum -= 4; + } + if (hf_sum > 22) { + tapset_decision.Val = 2; + } else if (hf_sum > 18) { + tapset_decision.Val = 1; + } else { + tapset_decision.Val = 0; + } + } + + Inlines.OpusAssert(nbBands > 0); + /* end has to be non-zero */ + Inlines.OpusAssert(sum >= 0); + sum = Inlines.celt_udiv(sum, nbBands); + + /* Recursive averaging */ + sum = (sum + average.Val) >> 1; + average.Val = sum; + + /* Hysteresis */ + sum = (3 * sum + (((3 - last_decision) << 7) + 64) + 2) >> 2; + if (sum < 80) { + decision = Spread.SPREAD_AGGRESSIVE; + } else if (sum < 256) { + decision = Spread.SPREAD_NORMAL; + } else if (sum < 384) { + decision = Spread.SPREAD_LIGHT; + } else { + decision = Spread.SPREAD_NONE; + } + return decision; + } + + static void deinterleave_hadamard(int[] X, int X_ptr, int N0, int stride, int hadamard) { + int i, j; + int N; + N = N0 * stride; + int[] tmp = new int[N]; + + Inlines.OpusAssert(stride > 0); + if (hadamard != 0) { + int ordery = (stride - 2); + + for (i = 0; i < stride; i++) { + for (j = 0; j < N0; j++) { + tmp[CeltTables.ordery_table[ordery + i] * N0 + j] = X[j * stride + i + X_ptr]; + } + } + } else { + for (i = 0; i < stride; i++) { + for (j = 0; j < N0; j++) { + tmp[i * N0 + j] = X[j * stride + i + X_ptr]; + } + } + } + + System.arraycopy(tmp, 0, X, X_ptr, N); + } + + static void interleave_hadamard(int[] X, int X_ptr, int N0, int stride, int hadamard) { + int i, j; + int N; + N = N0 * stride; + int[] tmp = new int[N]; + + if (hadamard != 0) { + int ordery = (stride - 2); + for (i = 0; i < stride; i++) { + for (j = 0; j < N0; j++) { + tmp[j * stride + i] = X[CeltTables.ordery_table[ordery + i] * N0 + j + X_ptr]; + } + } + } else { + for (i = 0; i < stride; i++) { + for (j = 0; j < N0; j++) { + tmp[j * stride + i] = X[i * N0 + j + X_ptr]; + } + } + } + + System.arraycopy(tmp, 0, X, X_ptr, N); + } + + static void haar1(int[] X, int X_ptr, int N0, int stride) { + int i, j; + N0 >>= 1; + for (i = 0; i < stride; i++) { + for (j = 0; j < N0; j++) { + int tmpidx = X_ptr + i + (stride * 2 * j); + int tmp1, tmp2; + tmp1 = Inlines.MULT16_16(((short) (0.5 + (.70710678f) * (((int) 1) << (15))))/*Inlines.QCONST16(.70710678f, 15)*/, X[tmpidx]); + tmp2 = Inlines.MULT16_16(((short) (0.5 + (.70710678f) * (((int) 1) << (15))))/*Inlines.QCONST16(.70710678f, 15)*/, X[tmpidx + stride]); + X[tmpidx] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.ADD32(tmp1, tmp2), 15)); + X[tmpidx + stride] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.SUB32(tmp1, tmp2), 15)); + } + } + } + + static void haar1ZeroOffset(int[] X, int N0, int stride) { + int i, j; + N0 >>= 1; + for (i = 0; i < stride; i++) { + for (j = 0; j < N0; j++) { + int tmpidx = i + (stride * 2 * j); + int tmp1, tmp2; + tmp1 = Inlines.MULT16_16(((short) (0.5 + (.70710678f) * (((int) 1) << (15))))/*Inlines.QCONST16(.70710678f, 15)*/, X[tmpidx]); + tmp2 = Inlines.MULT16_16(((short) (0.5 + (.70710678f) * (((int) 1) << (15))))/*Inlines.QCONST16(.70710678f, 15)*/, X[tmpidx + stride]); + X[tmpidx] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.ADD32(tmp1, tmp2), 15)); + X[tmpidx + stride] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.SUB32(tmp1, tmp2), 15)); + } + } + } + + static int compute_qn(int N, int b, int offset, int pulse_cap, int stereo) { + short[] exp2_table8 + = {16384, 17866, 19483, 21247, 23170, 25267, 27554, 30048}; + int qn, qb; + int N2 = 2 * N - 1; + if (stereo != 0 && N == 2) { + N2--; + } + + /* The upper limit ensures that in a stereo split with itheta==16384, we'll + always have enough bits left over to code at least one pulse in the + side; otherwise it would collapse, since it doesn't get folded. */ + qb = Inlines.celt_sudiv(b + N2 * offset, N2); + qb = Inlines.IMIN(b - pulse_cap - (4 << EntropyCoder.BITRES), qb); + + qb = Inlines.IMIN(8 << EntropyCoder.BITRES, qb); + + if (qb < (1 << EntropyCoder.BITRES >> 1)) { + qn = 1; + } else { + qn = exp2_table8[qb & 0x7] >> (14 - (qb >> EntropyCoder.BITRES)); + qn = (qn + 1) >> 1 << 1; + } + Inlines.OpusAssert(qn <= 256); + return qn; + } + + public static class band_ctx { + + public int encode; + public CeltMode m; + public int i; + public int intensity; + public int spread; + public int tf_change; + public EntropyCoder ec; + public int remaining_bits; + public int[][] bandE; + public int seed; + }; + + public static class split_ctx { + + public int inv; + public int imid; + public int iside; + public int delta; + public int itheta; + public int qalloc; + }; + + static void compute_theta(band_ctx ctx, split_ctx sctx, + int[] X, int X_ptr, int[] Y, int Y_ptr, int N, BoxedValueInt b, int B, int B0, + int LM, + int stereo, BoxedValueInt fill) { + int qn; + int itheta = 0; + int delta; + int imid, iside; + int qalloc; + int pulse_cap; + int offset; + int tell; + int inv = 0; + int encode; + CeltMode m; + int i; + int intensity; + EntropyCoder ec; // porting note: pointer + int[][] bandE; + + encode = ctx.encode; + m = ctx.m; + i = ctx.i; + intensity = ctx.intensity; + ec = ctx.ec; + bandE = ctx.bandE; + + /* Decide on the resolution to give to the split parameter theta */ + pulse_cap = m.logN[i] + LM * (1 << EntropyCoder.BITRES); + offset = (pulse_cap >> 1) - (stereo != 0 && N == 2 ? CeltConstants.QTHETA_OFFSET_TWOPHASE : CeltConstants.QTHETA_OFFSET); + qn = compute_qn(N, b.Val, offset, pulse_cap, stereo); + if (stereo != 0 && i >= intensity) { + qn = 1; + } + + if (encode != 0) { + /* theta is the atan() of the ratio between the (normalized) + side and mid. With just that parameter, we can re-scale both + mid and side because we know that 1) they have unit norm and + 2) they are orthogonal. */ + itheta = VQ.stereo_itheta(X, X_ptr, Y, Y_ptr, stereo, N); + } + + tell = (int) ec.tell_frac(); + + if (qn != 1) { + if (encode != 0) { + itheta = (itheta * qn + 8192) >> 14; + } + + /* Entropy coding of the angle. We use a uniform pdf for the + time split, a step for stereo, and a triangular one for the rest. */ + if (stereo != 0 && N > 2) { + int p0 = 3; + int x = itheta; + int x0 = qn / 2; + long ft = Inlines.CapToUInt32(p0 * (x0 + 1) + x0); + /* Use a probability of p0 up to itheta=8192 and then use 1 after */ + if (encode != 0) { + ec.encode( + (x <= x0 + ? (p0 * x) + : ((x - 1 - x0) + (x0 + 1) * p0)), + (x <= x0 + ? (p0 * (x + 1)) + : ((x - x0) + (x0 + 1) * p0)), + ft); + } else { + int fs = (int) ec.decode(ft); + if (fs < (x0 + 1) * p0) { + x = fs / p0; + } else { + x = x0 + 1 + (fs - (x0 + 1) * p0); + } + + ec.dec_update( + (x <= x0 + ? p0 * x + : (x - 1 - x0) + (x0 + 1) * p0), + (x <= x0 + ? p0 * (x + 1) + : (x - x0) + (x0 + 1) * p0), + ft); + itheta = x; + } + } else if (B0 > 1 || stereo != 0) { + /* Uniform pdf */ + if (encode != 0) { + ec.enc_uint(itheta, (qn + 1)); + } else { + itheta = (int) ec.dec_uint(qn + 1); + } + } else { + int fs = 1, ft; + ft = ((qn >> 1) + 1) * ((qn >> 1) + 1); + if (encode != 0) { + int fl; + + fs = itheta <= (qn >> 1) ? itheta + 1 : qn + 1 - itheta; + fl = itheta <= (qn >> 1) ? itheta * (itheta + 1) >> 1 + : ft - ((qn + 1 - itheta) * (qn + 2 - itheta) >> 1); + + ec.encode(fl, (fl + fs), ft); + } else { + /* Triangular pdf */ + int fl = 0; + int fm; + fm = (int) ec.decode(ft); + + if (fm < ((qn >> 1) * ((qn >> 1) + 1) >> 1)) { + itheta = (Inlines.isqrt32(8 * fm + 1) - 1) >> 1; + fs = itheta + 1; + fl = itheta * (itheta + 1) >> 1; + } else { + itheta = (2 * (qn + 1) - Inlines.isqrt32(8 * (ft - fm - 1) + 1)) >> 1; + fs = qn + 1 - itheta; + fl = ft - ((qn + 1 - itheta) * (qn + 2 - itheta) >> 1); + } + + ec.dec_update(fl, (fl + fs), ft); + } + } + Inlines.OpusAssert(itheta >= 0); + itheta = Inlines.celt_udiv(itheta * 16384, qn); + if (encode != 0 && stereo != 0) { + if (itheta == 0) { + intensity_stereo(m, X, X_ptr, Y, Y_ptr, bandE, i, N); + } else { + stereo_split(X, X_ptr, Y, Y_ptr, N); + } + } + } else if (stereo != 0) { + if (encode != 0) { + inv = itheta > 8192 ? 1 : 0; + if (inv != 0) { + int j; + for (j = 0; j < N; j++) { + Y[Y_ptr + j] = (0 - Y[Y_ptr + j]); + } + } + intensity_stereo(m, X, X_ptr, Y, Y_ptr, bandE, i, N); + } + if (b.Val > 2 << EntropyCoder.BITRES && ctx.remaining_bits > 2 << EntropyCoder.BITRES) { + if (encode != 0) { + ec.enc_bit_logp(inv, 2); + } else { + inv = ec.dec_bit_logp(2); + } + } else { + inv = 0; + } + itheta = 0; + } + qalloc = (int) ec.tell_frac() - tell; + b.Val -= qalloc; + + if (itheta == 0) { + imid = 32767; + iside = 0; + fill.Val &= (1 << B) - 1; + delta = -16384; + } else if (itheta == 16384) { + imid = 0; + iside = 32767; + fill.Val &= ((1 << B) - 1) << B; + delta = 16384; + } else { + imid = bitexact_cos((short) itheta); + iside = bitexact_cos((short) (16384 - itheta)); + /* This is the mid vs side allocation that minimizes squared error + in that band. */ + delta = Inlines.FRAC_MUL16((N - 1) << 7, bitexact_log2tan(iside, imid)); + } + + sctx.inv = inv; + sctx.imid = imid; + sctx.iside = iside; + sctx.delta = delta; + sctx.itheta = itheta; + sctx.qalloc = qalloc; + } + + static int quant_band_n1(band_ctx ctx, int[] X, int X_ptr, int[] Y, int Y_ptr, int b, + int[] lowband_out, int lowband_out_ptr) { + int resynth = ctx.encode == 0 ? 1 : 0; + int c; + int stereo; + int[] x = X; + int x_ptr = X_ptr; + int encode; + EntropyCoder ec; // porting note: pointer + + encode = ctx.encode; + ec = ctx.ec; + + stereo = (Y != null) ? 1 : 0; + c = 0; + do { + int sign = 0; + if (ctx.remaining_bits >= 1 << EntropyCoder.BITRES) { + if (encode != 0) { + sign = x[x_ptr] < 0 ? 1 : 0; + ec.enc_bits(sign, 1); + } else { + sign = ec.dec_bits(1); + } + ctx.remaining_bits -= 1 << EntropyCoder.BITRES; + b -= 1 << EntropyCoder.BITRES; + } + if (resynth != 0) { + x[x_ptr] = sign != 0 ? 0 - CeltConstants.NORM_SCALING : CeltConstants.NORM_SCALING; + } + x = Y; + x_ptr = Y_ptr; + } while (++c < 1 + stereo); + if (lowband_out != null) { + lowband_out[lowband_out_ptr] = Inlines.SHR16(X[X_ptr], 4); + } + + return 1; + } + + /* This function is responsible for encoding and decoding a mono partition. + It can split the band in two and transmit the energy difference with + the two half-bands. It can be called recursively so bands can end up being + split in 8 parts. */ + static int quant_partition(band_ctx ctx, int[] X, int X_ptr, + int N, int b, int B, int[] lowband, int lowband_ptr, + int LM, + int gain, int fill) { + int cache_ptr; + int q; + int curr_bits; + int imid = 0, iside = 0; + int B0 = B; + int mid = 0, side = 0; + int cm = 0; + int resynth = (ctx.encode == 0) ? 1 : 0; + int Y = 0; + int encode; + CeltMode m; //porting note: pointer + int i; + int spread; + EntropyCoder ec; //porting note: pointer + + encode = ctx.encode; + m = ctx.m; + i = ctx.i; + spread = ctx.spread; + ec = ctx.ec; + short[] cache = m.cache.bits; + /* If we need 1.5 more bits than we can produce, split the band in two. */ + cache_ptr = m.cache.index[(LM + 1) * m.nbEBands + i]; + if (LM != -1 && b > cache[cache_ptr + cache[cache_ptr]] + 12 && N > 2) { + int mbits, sbits, delta; + int itheta; + int qalloc; + split_ctx sctx = new split_ctx(); + int next_lowband2 = 0; + int rebalance; + + N >>= 1; + Y = X_ptr + N; + LM -= 1; + if (B == 1) { + fill = (fill & 1) | (fill << 1); + } + + B = (B + 1) >> 1; + + BoxedValueInt boxed_b = new BoxedValueInt(b); + BoxedValueInt boxed_fill = new BoxedValueInt(fill); + compute_theta(ctx, sctx, X, X_ptr, X, Y, N, boxed_b, B, B0, LM, 0, boxed_fill); + b = boxed_b.Val; + fill = boxed_fill.Val; + + imid = sctx.imid; + iside = sctx.iside; + delta = sctx.delta; + itheta = sctx.itheta; + qalloc = sctx.qalloc; + mid = (imid); + side = (iside); + + /* Give more bits to low-energy MDCTs than they would otherwise deserve */ + if (B0 > 1 && ((itheta & 0x3fff) != 0)) { + if (itheta > 8192) /* Rough approximation for pre-echo masking */ { + delta -= delta >> (4 - LM); + } else /* Corresponds to a forward-masking slope of 1.5 dB per 10 ms */ { + delta = Inlines.IMIN(0, delta + (N << EntropyCoder.BITRES >> (5 - LM))); + } + } + mbits = Inlines.IMAX(0, Inlines.IMIN(b, (b - delta) / 2)); + sbits = b - mbits; + ctx.remaining_bits -= qalloc; + + if (lowband != null) { + next_lowband2 = (lowband_ptr + N); + /* >32-bit split case */ + } + + rebalance = ctx.remaining_bits; + if (mbits >= sbits) { + cm = quant_partition(ctx, X, X_ptr, N, mbits, B, + lowband, lowband_ptr, LM, + Inlines.MULT16_16_P15(gain, mid), fill); + rebalance = mbits - (rebalance - ctx.remaining_bits); + if (rebalance > 3 << EntropyCoder.BITRES && itheta != 0) { + sbits += rebalance - (3 << EntropyCoder.BITRES); + } + cm |= quant_partition(ctx, X, Y, N, sbits, B, + lowband, next_lowband2, LM, + Inlines.MULT16_16_P15(gain, side), fill >> B) << (B0 >> 1); + } else { + cm = quant_partition(ctx, X, Y, N, sbits, B, + lowband, next_lowband2, LM, + Inlines.MULT16_16_P15(gain, side), fill >> B) << (B0 >> 1); + rebalance = sbits - (rebalance - ctx.remaining_bits); + if (rebalance > 3 << EntropyCoder.BITRES && itheta != 16384) { + mbits += rebalance - (3 << EntropyCoder.BITRES); + } + cm |= quant_partition(ctx, X, X_ptr, N, mbits, B, + lowband, lowband_ptr, LM, + Inlines.MULT16_16_P15(gain, mid), fill); + } + } else { + /* This is the basic no-split case */ + q = Rate.bits2pulses(m, i, LM, b); + curr_bits = Rate.pulses2bits(m, i, LM, q); + ctx.remaining_bits -= curr_bits; + + /* Ensures we can never bust the budget */ + while (ctx.remaining_bits < 0 && q > 0) { + ctx.remaining_bits += curr_bits; + q--; + curr_bits = Rate.pulses2bits(m, i, LM, q); + ctx.remaining_bits -= curr_bits; + } + + if (q != 0) { + int K = Rate.get_pulses(q); + + /* Finally do the actual quantization */ + if (encode != 0) { + cm = VQ.alg_quant(X, X_ptr, N, K, spread, B, ec); + } else { + cm = VQ.alg_unquant(X, X_ptr, N, K, spread, B, ec, gain); + } + } else { + /* If there's no pulse, fill the band anyway */ + int j; + + if (resynth != 0) { + int cm_mask; + /* B can be as large as 16, so this shift might overflow an int on a + 16-bit platform; use a long to get defined behavior.*/ + cm_mask = (1 << B) - 1; + fill = fill & cm_mask; + + if (fill == 0) { + Arrays.MemSetWithOffset(X, 0, X_ptr, N); + } else { + if (lowband == null) { + /* Noise */ + for (j = 0; j < N; j++) { + ctx.seed = celt_lcg_rand(ctx.seed); + X[X_ptr + j] = (((int) ctx.seed) >> 20); + } + cm = cm_mask; + } else { + /* Folded spectrum */ + for (j = 0; j < N; j++) { + int tmp; + ctx.seed = celt_lcg_rand(ctx.seed); + /* About 48 dB below the "normal" folding level */ + tmp = ((short) (0.5 + (1.0f / 256) * (((int) 1) << (10))))/*Inlines.QCONST16(1.0f / 256, 10)*/; + tmp = (((ctx.seed) & 0x8000) != 0 ? tmp : 0 - tmp); + X[X_ptr + j] = (lowband[lowband_ptr + j] + tmp); + } + cm = fill; + } + + VQ.renormalise_vector(X, X_ptr, N, gain); + } + } + } + } + + return cm; + } + + private static final byte[] bit_interleave_table = {0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3}; + + private static final short[] bit_deinterleave_table = { + 0x00, 0x03, 0x0C, 0x0F, 0x30, 0x33, 0x3C, 0x3F, + 0xC0, 0xC3, 0xCC, 0xCF, 0xF0, 0xF3, 0xFC, 0xFF + }; + + /* This function is responsible for encoding and decoding a band for the mono case. */ + static int quant_band(band_ctx ctx, int[] X, int X_ptr, + int N, int b, int B, int[] lowband, int lowband_ptr, + int LM, int[] lowband_out, int lowband_out_ptr, + int gain, int[] lowband_scratch, int lowband_scratch_ptr, int fill) { + int N0 = N; + int N_B = N; + int N_B0; + int B0 = B; + int time_divide = 0; + int recombine = 0; + int longBlocks; + int cm = 0; + int resynth = ctx.encode == 0 ? 1 : 0; + int k; + int encode; + int tf_change; + + encode = ctx.encode; + tf_change = ctx.tf_change; + + longBlocks = B0 == 1 ? 1 : 0; + + N_B = Inlines.celt_udiv(N_B, B); + + /* Special case for one sample */ + if (N == 1) { + return quant_band_n1(ctx, X, X_ptr, null, 0, b, lowband_out, lowband_out_ptr); + } + + if (tf_change > 0) { + recombine = tf_change; + } + /* Band recombining to increase frequency resolution */ + + if (lowband_scratch != null && lowband != null && (recombine != 0 || ((N_B & 1) == 0 && tf_change < 0) || B0 > 1)) { + System.arraycopy(lowband, lowband_ptr, lowband_scratch, lowband_scratch_ptr, N); + lowband = lowband_scratch; + lowband_ptr = lowband_scratch_ptr; + } + + for (k = 0; k < recombine; k++) { + if (encode != 0) { + haar1(X, X_ptr, N >> k, 1 << k); + } + if (lowband != null) { + haar1(lowband, lowband_ptr, N >> k, 1 << k); + } + int idx1 = fill & 0xF; + int idx2 = fill >> 4; + if (idx1 < 0) { + System.out.println("e"); + } + if (idx2 < 0) { + System.out.println("e"); + } + fill = bit_interleave_table[fill & 0xF] | bit_interleave_table[fill >> 4] << 2; + } + B >>= recombine; + N_B <<= recombine; + + /* Increasing the time resolution */ + while ((N_B & 1) == 0 && tf_change < 0) { + if (encode != 0) { + haar1(X, X_ptr, N_B, B); + } + if (lowband != null) { + haar1(lowband, lowband_ptr, N_B, B); + } + fill |= fill << B; + B <<= 1; + N_B >>= 1; + time_divide++; + tf_change++; + } + B0 = B; + N_B0 = N_B; + + /* Reorganize the samples in time order instead of frequency order */ + if (B0 > 1) { + if (encode != 0) { + deinterleave_hadamard(X, X_ptr, N_B >> recombine, B0 << recombine, longBlocks); + } + if (lowband != null) { + deinterleave_hadamard(lowband, lowband_ptr, N_B >> recombine, B0 << recombine, longBlocks); + } + } + + cm = quant_partition(ctx, X, X_ptr, N, b, B, lowband, lowband_ptr, LM, gain, fill); + + /* This code is used by the decoder and by the resynthesis-enabled encoder */ + if (resynth != 0) { + /* Undo the sample reorganization going from time order to frequency order */ + if (B0 > 1) { + interleave_hadamard(X, X_ptr, N_B >> recombine, B0 << recombine, longBlocks); + } + + /* Undo time-freq changes that we did earlier */ + N_B = N_B0; + B = B0; + for (k = 0; k < time_divide; k++) { + B >>= 1; + N_B <<= 1; + cm |= cm >> B; + haar1(X, X_ptr, N_B, B); + } + + for (k = 0; k < recombine; k++) { + cm = bit_deinterleave_table[cm]; + haar1(X, X_ptr, N0 >> k, 1 << k); + } + B <<= recombine; + + /* Scale output for later folding */ + if (lowband_out != null) { + int j; + int n; + n = (Inlines.celt_sqrt(Inlines.SHL32(N0, 22))); + for (j = 0; j < N0; j++) { + lowband_out[lowband_out_ptr + j] = Inlines.MULT16_16_Q15(n, X[X_ptr + j]); + } + } + + cm = cm & ((1 << B) - 1); + } + return cm; + } + + /* This function is responsible for encoding and decoding a band for the stereo case. */ + static int quant_band_stereo(band_ctx ctx, int[] X, int X_ptr, int[] Y, int Y_ptr, + int N, int b, int B, int[] lowband, int lowband_ptr, + int LM, int[] lowband_out, int lowband_out_ptr, + int[] lowband_scratch, int lowband_scratch_ptr, int fill) { + int imid = 0, iside = 0; + int inv = 0; + int mid = 0, side = 0; + int cm = 0; + int resynth = ctx.encode == 0 ? 1 : 0; + int mbits, sbits, delta; + int itheta; + int qalloc; + split_ctx sctx = new split_ctx(); // porting note: stack var + int orig_fill; + int encode; + EntropyCoder ec; //porting note: pointer + + encode = ctx.encode; + ec = ctx.ec; + + /* Special case for one sample */ + if (N == 1) { + return quant_band_n1(ctx, X, X_ptr, Y, Y_ptr, b, lowband_out, lowband_out_ptr); + } + + orig_fill = fill; + + BoxedValueInt boxed_b = new BoxedValueInt(b); + BoxedValueInt boxed_fill = new BoxedValueInt(fill); + compute_theta(ctx, sctx, X, X_ptr, Y, Y_ptr, N, boxed_b, B, B, LM, 1, boxed_fill); + b = boxed_b.Val; + fill = boxed_fill.Val; + + inv = sctx.inv; + imid = sctx.imid; + iside = sctx.iside; + delta = sctx.delta; + itheta = sctx.itheta; + qalloc = sctx.qalloc; + mid = (imid); + side = (iside); + + /* This is a special case for N=2 that only works for stereo and takes + advantage of the fact that mid and side are orthogonal to encode + the side with just one bit. */ + if (N == 2) { + int c; + int sign = 0; + int[] x2, y2; + int x2_ptr, y2_ptr; + mbits = b; + sbits = 0; + /* Only need one bit for the side. */ + if (itheta != 0 && itheta != 16384) { + sbits = 1 << EntropyCoder.BITRES; + } + mbits -= sbits; + c = itheta > 8192 ? 1 : 0; + ctx.remaining_bits -= qalloc + sbits; + if (c != 0) { + x2 = Y; + x2_ptr = Y_ptr; + y2 = X; + y2_ptr = X_ptr; + } else { + x2 = X; + x2_ptr = X_ptr; + y2 = Y; + y2_ptr = Y_ptr; + } + + if (sbits != 0) { + if (encode != 0) { + /* Here we only need to encode a sign for the side. */ + sign = (x2[x2_ptr] * y2[Y_ptr + 1] - x2[x2_ptr + 1] * y2[Y_ptr] < 0) ? 1 : 0; + ec.enc_bits(sign, 1); + } else { + sign = ec.dec_bits(1); + } + } + sign = 1 - 2 * sign; + /* We use orig_fill here because we want to fold the side, but if + itheta==16384, we'll have cleared the low bits of fill. */ + cm = quant_band(ctx, x2, x2_ptr, N, mbits, B, lowband, lowband_ptr, + LM, lowband_out, lowband_out_ptr, CeltConstants.Q15ONE, lowband_scratch, lowband_scratch_ptr, orig_fill); + + /* We don't split N=2 bands, so cm is either 1 or 0 (for a fold-collapse), + and there's no need to worry about mixing with the other channel. */ + y2[Y_ptr] = ((0 - sign) * x2[x2_ptr + 1]); + y2[Y_ptr + 1] = (sign * x2[x2_ptr]); + if (resynth != 0) { + int tmp; + X[X_ptr] = Inlines.MULT16_16_Q15(mid, X[X_ptr]); + X[X_ptr + 1] = Inlines.MULT16_16_Q15(mid, X[X_ptr + 1]); + Y[Y_ptr] = Inlines.MULT16_16_Q15(side, Y[Y_ptr]); + Y[Y_ptr + 1] = Inlines.MULT16_16_Q15(side, Y[Y_ptr + 1]); + tmp = X[X_ptr]; + X[X_ptr] = Inlines.SUB16(tmp, Y[Y_ptr]); + Y[Y_ptr] = Inlines.ADD16(tmp, Y[Y_ptr]); + tmp = X[X_ptr + 1]; + X[X_ptr + 1] = Inlines.SUB16(tmp, Y[Y_ptr + 1]); + Y[Y_ptr + 1] = Inlines.ADD16(tmp, Y[Y_ptr + 1]); + } + } else { + /* "Normal" split code */ + int rebalance; + + mbits = Inlines.IMAX(0, Inlines.IMIN(b, (b - delta) / 2)); + sbits = b - mbits; + ctx.remaining_bits -= qalloc; + + rebalance = ctx.remaining_bits; + if (mbits >= sbits) { + /* In stereo mode, we do not apply a scaling to the mid because we need the normalized + mid for folding later. */ + cm = quant_band(ctx, X, X_ptr, N, mbits, B, + lowband, lowband_ptr, LM, lowband_out, lowband_out_ptr, + CeltConstants.Q15ONE, lowband_scratch, lowband_scratch_ptr, fill); + rebalance = mbits - (rebalance - ctx.remaining_bits); + if (rebalance > 3 << EntropyCoder.BITRES && itheta != 0) { + sbits += rebalance - (3 << EntropyCoder.BITRES); + } + + /* For a stereo split, the high bits of fill are always zero, so no + folding will be done to the side. */ + cm |= quant_band(ctx, Y, Y_ptr, N, sbits, B, + null, 0, LM, null, 0, + side, null, 0, fill >> B); + } else { + /* For a stereo split, the high bits of fill are always zero, so no + folding will be done to the side. */ + cm = quant_band(ctx, Y, Y_ptr, N, sbits, B, + null, 0, LM, null, 0, + side, null, 0, fill >> B); + rebalance = sbits - (rebalance - ctx.remaining_bits); + if (rebalance > 3 << EntropyCoder.BITRES && itheta != 16384) { + mbits += rebalance - (3 << EntropyCoder.BITRES); + } + /* In stereo mode, we do not apply a scaling to the mid because we need the normalized + mid for folding later. */ + cm |= quant_band(ctx, X, X_ptr, N, mbits, B, + lowband, lowband_ptr, LM, lowband_out, lowband_out_ptr, + CeltConstants.Q15ONE, lowband_scratch, lowband_scratch_ptr, fill); + } + } + + + /* This code is used by the decoder and by the resynthesis-enabled encoder */ + if (resynth != 0) { + if (N != 2) { + stereo_merge(X, X_ptr, Y, Y_ptr, mid, N); + } + if (inv != 0) { + int j; + for (j = Y_ptr; j < N + Y_ptr; j++) { + Y[j] = (short) (0 - Y[j]); + } + } + } + + return cm; + } + + static void quant_all_bands(int encode, CeltMode m, int start, int end, + int[] X_, int[] Y_, short[] collapse_masks, + int[][] bandE, int[] pulses, int shortBlocks, int spread, + int dual_stereo, int intensity, int[] tf_res, int total_bits, + int balance, EntropyCoder ec, int LM, int codedBands, + BoxedValueInt seed) { + int i; + int remaining_bits; + short[] eBands = m.eBands; + int[] norm; + int norm2; + int[] lowband_scratch; + int lowband_scratch_ptr; + int B; + int M; + int lowband_offset; + int update_lowband = 1; + int C = Y_ != null ? 2 : 1; + int norm_offset; + int resynth = encode == 0 ? 1 : 0; + band_ctx ctx = new band_ctx(); // porting note: stack var + + M = 1 << LM; + B = (shortBlocks != 0) ? M : 1; + norm_offset = M * eBands[start]; + + /* No need to allocate norm for the last band because we don't need an + output in that band. */ + norm = new int[(C * (M * eBands[m.nbEBands - 1] - norm_offset))]; + norm2 = M * eBands[m.nbEBands - 1] - norm_offset; + + /* We can use the last band as scratch space because we don't need that + scratch space for the last band. */ + lowband_scratch = X_; + lowband_scratch_ptr = M * eBands[m.nbEBands - 1]; + + lowband_offset = 0; + ctx.bandE = bandE; + ctx.ec = ec; + ctx.encode = encode; + ctx.intensity = intensity; + ctx.m = m; + ctx.seed = seed.Val; + ctx.spread = spread; + for (i = start; i < end; i++) { + int tell; + int b; + int N; + int curr_balance; + int effective_lowband = -1; + int[] X, Y; + int X_ptr, Y_ptr; + Y_ptr = 0; + int tf_change = 0; + long x_cm; + long y_cm; + int last; + + ctx.i = i; + last = (i == end - 1) ? 1 : 0; + + X = X_; + X_ptr = (M * eBands[i]); + if (Y_ != null) { + Y = Y_; + Y_ptr = (M * eBands[i]); + } else { + Y = null; + } + N = M * eBands[i + 1] - M * eBands[i]; + tell = (int) ec.tell_frac(); + + /* Compute how many bits we want to allocate to this band */ + if (i != start) { + balance -= tell; + } + remaining_bits = total_bits - tell - 1; + ctx.remaining_bits = remaining_bits; + if (i <= codedBands - 1) { + curr_balance = Inlines.celt_sudiv(balance, Inlines.IMIN(3, codedBands - i)); + b = Inlines.IMAX(0, Inlines.IMIN(16383, Inlines.IMIN(remaining_bits + 1, pulses[i] + curr_balance))); + } else { + b = 0; + } + + if (resynth != 0 && M * eBands[i] - N >= M * eBands[start] && (update_lowband != 0 || lowband_offset == 0)) { + lowband_offset = i; + } + + tf_change = tf_res[i]; + ctx.tf_change = tf_change; + if (i >= m.effEBands) { + X = norm; + X_ptr = 0; + if (Y_ != null) { + Y = norm; + Y_ptr = 0; + } + lowband_scratch = null; + } + if (i == end - 1) { + lowband_scratch = null; + } + + /* Get a conservative estimate of the collapse_mask's for the bands we're + going to be folding from. */ + if (lowband_offset != 0 && (spread != Spread.SPREAD_AGGRESSIVE || B > 1 || tf_change < 0)) { + int fold_start; + int fold_end; + int fold_i; + /* This ensures we never repeat spectral content within one band */ + effective_lowband = Inlines.IMAX(0, M * eBands[lowband_offset] - norm_offset - N); + fold_start = lowband_offset; + while (M * eBands[--fold_start] > effective_lowband + norm_offset) ; + fold_end = lowband_offset - 1; + while (M * eBands[++fold_end] < effective_lowband + norm_offset + N) ; + x_cm = y_cm = 0; + fold_i = fold_start; + do { + x_cm |= collapse_masks[fold_i * C + 0]; + y_cm |= collapse_masks[fold_i * C + C - 1]; + } while (++fold_i < fold_end); + } /* Otherwise, we'll be using the LCG to fold, so all blocks will (almost + always) be non-zero. */ else { + x_cm = y_cm = ((1 << B) - 1); + } + + if (dual_stereo != 0 && i == intensity) { + int j; + + /* Switch off dual stereo to do intensity. */ + dual_stereo = 0; + if (resynth != 0) { + for (j = 0; j < M * eBands[i] - norm_offset; j++) { + norm[j] = (Inlines.HALF32(norm[j] + norm[norm2 + j])); + } + } + } + if (dual_stereo != 0) { + x_cm = quant_band(ctx, + X, + X_ptr, + N, + b / 2, + B, + effective_lowband != -1 ? norm : null, + effective_lowband, + LM, + last != 0 ? null : norm, + M * eBands[i] - norm_offset, + CeltConstants.Q15ONE, + lowband_scratch, + lowband_scratch_ptr, + (int) x_cm); + y_cm = quant_band( + ctx, + Y, + Y_ptr, + N, + b / 2, + B, + effective_lowband != -1 ? norm : null, + norm2 + effective_lowband, + LM, + last != 0 ? null : norm, + norm2 + (M * eBands[i] - norm_offset), + CeltConstants.Q15ONE, + lowband_scratch, + lowband_scratch_ptr, + (int) y_cm); + } else { + if (Y != null) { + x_cm = quant_band_stereo( + ctx, + X, + X_ptr, + Y, + Y_ptr, + N, + b, + B, + effective_lowband != -1 ? norm : null, + effective_lowband, + LM, + last != 0 ? null : norm, + M * eBands[i] - norm_offset, + lowband_scratch, + lowband_scratch_ptr, + (int) (x_cm | y_cm)); + } else { + x_cm = quant_band( + ctx, + X, + X_ptr, + N, + b, + B, + effective_lowband != -1 ? norm : null, + effective_lowband, + LM, + last != 0 ? null : norm, + M * eBands[i] - norm_offset, + CeltConstants.Q15ONE, + lowband_scratch, + lowband_scratch_ptr, + (int) (x_cm | y_cm)); // opt: lots of pointers are created here too + } + y_cm = x_cm; + } + collapse_masks[i * C + 0] = (short) (x_cm & 0xFF); + collapse_masks[i * C + C - 1] = (short) (y_cm & 0xFF); + balance += pulses[i] + tell; + + /* Update the folding position only as long as we have 1 bit/sample depth. */ + update_lowband = (b > (N << EntropyCoder.BITRES)) ? 1 : 0; + } + + seed.Val = ctx.seed; + } +} diff --git a/app/src/main/java/org/concentus/BoxedValue.java b/app/src/main/java/org/concentus/BoxedValue.java new file mode 100644 index 0000000000..f039bc17c5 --- /dev/null +++ b/app/src/main/java/org/concentus/BoxedValue.java @@ -0,0 +1,58 @@ +/* Copyright (c) 2016 Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class BoxedValueByte { + + public byte Val; + + public BoxedValueByte(byte v) { + Val = v; + } +} + +class BoxedValueShort { + + public short Val; + + public BoxedValueShort(short v) { + Val = v; + } +} + +class BoxedValueInt { + + public int Val; + + public BoxedValueInt(int v) { + Val = v; + } +} diff --git a/app/src/main/java/org/concentus/BurgModified.java b/app/src/main/java/org/concentus/BurgModified.java new file mode 100644 index 0000000000..1d1e6726a5 --- /dev/null +++ b/app/src/main/java/org/concentus/BurgModified.java @@ -0,0 +1,337 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class BurgModified { + + /* subfr_length * nb_subfr = ( 0.005 * 16000 + 16 ) * 4 = 384 */ + private static final int MAX_FRAME_SIZE = 384; + private static final int QA = 25; + private static final int N_BITS_HEAD_ROOM = 2; + private static final int MIN_RSHIFTS = -16; + private static final int MAX_RSHIFTS = (32 - QA); + + /* Compute reflection coefficients from input signal */ + static void silk_burg_modified( + BoxedValueInt res_nrg, /* O Residual energy */ + BoxedValueInt res_nrg_Q, /* O Residual energy Q value */ + int[] A_Q16, /* O Prediction coefficients (length order) */ + short[] x, /* I Input signal, length: nb_subfr * ( D + subfr_length ) */ + int x_ptr, + int minInvGain_Q30, /* I Inverse of max prediction gain */ + int subfr_length, /* I Input signal subframe length (incl. D preceding samples) */ + int nb_subfr, /* I Number of subframes stacked in x */ + int D /* I Order */ + ) { + int k, n, s, lz, rshifts, reached_max_gain; + int C0, num, nrg, rc_Q31, invGain_Q30, Atmp_QA, Atmp1, tmp1, tmp2, x1, x2; + int x_offset; + int[] C_first_row = new int[SilkConstants.SILK_MAX_ORDER_LPC]; + int[] C_last_row = new int[SilkConstants.SILK_MAX_ORDER_LPC]; + int[] Af_QA = new int[SilkConstants.SILK_MAX_ORDER_LPC]; + int[] CAf = new int[SilkConstants.SILK_MAX_ORDER_LPC + 1]; + int[] CAb = new int[SilkConstants.SILK_MAX_ORDER_LPC + 1]; + int[] xcorr = new int[SilkConstants.SILK_MAX_ORDER_LPC]; + long C0_64; + + Inlines.OpusAssert(subfr_length * nb_subfr <= MAX_FRAME_SIZE); + + /* Compute autocorrelations, added over subframes */ + C0_64 = Inlines.silk_inner_prod16_aligned_64(x, x_ptr, x, x_ptr, subfr_length * nb_subfr); + lz = Inlines.silk_CLZ64(C0_64); + rshifts = 32 + 1 + N_BITS_HEAD_ROOM - lz; + if (rshifts > MAX_RSHIFTS) { + rshifts = MAX_RSHIFTS; + } + if (rshifts < MIN_RSHIFTS) { + rshifts = MIN_RSHIFTS; + } + + if (rshifts > 0) { + C0 = (int) Inlines.silk_RSHIFT64(C0_64, rshifts); + } else { + C0 = Inlines.silk_LSHIFT32((int) C0_64, -rshifts); + } + + CAb[0] = CAf[0] = C0 + Inlines.silk_SMMUL(((int) ((TuningParameters.FIND_LPC_COND_FAC) * ((long) 1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0) + 1; + /* Q(-rshifts) */ + Arrays.MemSet(C_first_row, 0, SilkConstants.SILK_MAX_ORDER_LPC); + if (rshifts > 0) { + for (s = 0; s < nb_subfr; s++) { + x_offset = x_ptr + s * subfr_length; + for (n = 1; n < D + 1; n++) { + C_first_row[n - 1] += (int) Inlines.silk_RSHIFT64( + Inlines.silk_inner_prod16_aligned_64(x, x_offset, x, x_offset + n, subfr_length - n), rshifts); + } + } + } else { + for (s = 0; s < nb_subfr; s++) { + int i; + int d; + x_offset = x_ptr + s * subfr_length; + CeltPitchXCorr.pitch_xcorr(x, x_offset, x, x_offset + 1, xcorr, subfr_length - D, D); + for (n = 1; n < D + 1; n++) { + for (i = n + subfr_length - D, d = 0; i < subfr_length; i++) { + d = Inlines.MAC16_16(d, x[x_offset + i], x[x_offset + i - n]); + } + xcorr[n - 1] += d; + } + for (n = 1; n < D + 1; n++) { + C_first_row[n - 1] += Inlines.silk_LSHIFT32(xcorr[n - 1], -rshifts); + } + } + } + System.arraycopy(C_first_row, 0, C_last_row, 0, SilkConstants.SILK_MAX_ORDER_LPC); + + /* Initialize */ + CAb[0] = CAf[0] = C0 + Inlines.silk_SMMUL(((int) ((TuningParameters.FIND_LPC_COND_FAC) * ((long) 1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0) + 1; + /* Q(-rshifts) */ + + invGain_Q30 = (int) 1 << 30; + reached_max_gain = 0; + for (n = 0; n < D; n++) { + /* Update first row of correlation matrix (without first element) */ + /* Update last row of correlation matrix (without last element, stored in reversed order) */ + /* Update C * Af */ + /* Update C * flipud(Af) (stored in reversed order) */ + if (rshifts > -2) { + for (s = 0; s < nb_subfr; s++) { + x_offset = x_ptr + s * subfr_length; + x1 = -Inlines.silk_LSHIFT32((int) x[x_offset + n], 16 - rshifts); + /* Q(16-rshifts) */ + x2 = -Inlines.silk_LSHIFT32((int) x[x_offset + subfr_length - n - 1], 16 - rshifts); + /* Q(16-rshifts) */ + tmp1 = Inlines.silk_LSHIFT32((int) x[x_offset + n], QA - 16); + /* Q(QA-16) */ + tmp2 = Inlines.silk_LSHIFT32((int) x[x_offset + subfr_length - n - 1], QA - 16); + /* Q(QA-16) */ + for (k = 0; k < n; k++) { + C_first_row[k] = Inlines.silk_SMLAWB(C_first_row[k], x1, x[x_offset + n - k - 1]); + /* Q( -rshifts ) */ + C_last_row[k] = Inlines.silk_SMLAWB(C_last_row[k], x2, x[x_offset + subfr_length - n + k]); + /* Q( -rshifts ) */ + Atmp_QA = Af_QA[k]; + tmp1 = Inlines.silk_SMLAWB(tmp1, Atmp_QA, x[x_offset + n - k - 1]); + /* Q(QA-16) */ + tmp2 = Inlines.silk_SMLAWB(tmp2, Atmp_QA, x[x_offset + subfr_length - n + k]); + /* Q(QA-16) */ + } + tmp1 = Inlines.silk_LSHIFT32(-tmp1, 32 - QA - rshifts); + /* Q(16-rshifts) */ + tmp2 = Inlines.silk_LSHIFT32(-tmp2, 32 - QA - rshifts); + /* Q(16-rshifts) */ + for (k = 0; k <= n; k++) { + CAf[k] = Inlines.silk_SMLAWB(CAf[k], tmp1, x[x_offset + n - k]); + /* Q( -rshift ) */ + CAb[k] = Inlines.silk_SMLAWB(CAb[k], tmp2, x[x_offset + subfr_length - n + k - 1]); + /* Q( -rshift ) */ + } + } + } else { + for (s = 0; s < nb_subfr; s++) { + x_offset = x_ptr + s * subfr_length; + x1 = -Inlines.silk_LSHIFT32((int) x[x_offset + n], -rshifts); + /* Q( -rshifts ) */ + x2 = -Inlines.silk_LSHIFT32((int) x[x_offset + subfr_length - n - 1], -rshifts); + /* Q( -rshifts ) */ + tmp1 = Inlines.silk_LSHIFT32((int) x[x_offset + n], 17); + /* Q17 */ + tmp2 = Inlines.silk_LSHIFT32((int) x[x_offset + subfr_length - n - 1], 17); + /* Q17 */ + for (k = 0; k < n; k++) { + C_first_row[k] = Inlines.silk_MLA(C_first_row[k], x1, x[x_offset + n - k - 1]); + /* Q( -rshifts ) */ + C_last_row[k] = Inlines.silk_MLA(C_last_row[k], x2, x[x_offset + subfr_length - n + k]); + /* Q( -rshifts ) */ + Atmp1 = Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 17); + /* Q17 */ + tmp1 = Inlines.silk_MLA(tmp1, x[x_offset + n - k - 1], Atmp1); + /* Q17 */ + tmp2 = Inlines.silk_MLA(tmp2, x[x_offset + subfr_length - n + k], Atmp1); + /* Q17 */ + } + tmp1 = -tmp1; + /* Q17 */ + tmp2 = -tmp2; + /* Q17 */ + for (k = 0; k <= n; k++) { + CAf[k] = Inlines.silk_SMLAWW(CAf[k], tmp1, + Inlines.silk_LSHIFT32((int) x[x_offset + n - k], -rshifts - 1)); + /* Q( -rshift ) */ + CAb[k] = Inlines.silk_SMLAWW(CAb[k], tmp2, + Inlines.silk_LSHIFT32((int) x[x_offset + subfr_length - n + k - 1], -rshifts - 1)); + /* Q( -rshift ) */ + } + } + } + + /* Calculate nominator and denominator for the next order reflection (parcor) coefficient */ + tmp1 = C_first_row[n]; + /* Q( -rshifts ) */ + tmp2 = C_last_row[n]; + /* Q( -rshifts ) */ + num = 0; + /* Q( -rshifts ) */ + nrg = Inlines.silk_ADD32(CAb[0], CAf[0]); + /* Q( 1-rshifts ) */ + for (k = 0; k < n; k++) { + Atmp_QA = Af_QA[k]; + lz = Inlines.silk_CLZ32(Inlines.silk_abs(Atmp_QA)) - 1; + lz = Inlines.silk_min(32 - QA, lz); + Atmp1 = Inlines.silk_LSHIFT32(Atmp_QA, lz); + /* Q( QA + lz ) */ + + tmp1 = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(C_last_row[n - k - 1], Atmp1), 32 - QA - lz); + /* Q( -rshifts ) */ + tmp2 = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(C_first_row[n - k - 1], Atmp1), 32 - QA - lz); + /* Q( -rshifts ) */ + num = Inlines.silk_ADD_LSHIFT32(num, Inlines.silk_SMMUL(CAb[n - k], Atmp1), 32 - QA - lz); + /* Q( -rshifts ) */ + nrg = Inlines.silk_ADD_LSHIFT32(nrg, Inlines.silk_SMMUL(Inlines.silk_ADD32(CAb[k + 1], CAf[k + 1]), + Atmp1), 32 - QA - lz); + /* Q( 1-rshifts ) */ + } + CAf[n + 1] = tmp1; + /* Q( -rshifts ) */ + CAb[n + 1] = tmp2; + /* Q( -rshifts ) */ + num = Inlines.silk_ADD32(num, tmp2); + /* Q( -rshifts ) */ + num = Inlines.silk_LSHIFT32(-num, 1); + /* Q( 1-rshifts ) */ + + /* Calculate the next order reflection (parcor) coefficient */ + if (Inlines.silk_abs(num) < nrg) { + rc_Q31 = Inlines.silk_DIV32_varQ(num, nrg, 31); + } else { + rc_Q31 = (num > 0) ? Integer.MAX_VALUE : Integer.MIN_VALUE; + } + + /* Update inverse prediction gain */ + tmp1 = ((int) 1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31); + tmp1 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, tmp1), 2); + if (tmp1 <= minInvGain_Q30) { + /* Max prediction gain exceeded; set reflection coefficient such that max prediction gain is exactly hit */ + tmp2 = ((int) 1 << 30) - Inlines.silk_DIV32_varQ(minInvGain_Q30, invGain_Q30, 30); + /* Q30 */ + rc_Q31 = Inlines.silk_SQRT_APPROX(tmp2); + /* Q15 */ + /* Newton-Raphson iteration */ + rc_Q31 = Inlines.silk_RSHIFT32(rc_Q31 + Inlines.silk_DIV32(tmp2, rc_Q31), 1); + /* Q15 */ + rc_Q31 = Inlines.silk_LSHIFT32(rc_Q31, 16); + /* Q31 */ + if (num < 0) { + /* Ensure adjusted reflection coefficients has the original sign */ + rc_Q31 = -rc_Q31; + } + invGain_Q30 = minInvGain_Q30; + reached_max_gain = 1; + } else { + invGain_Q30 = tmp1; + } + + /* Update the AR coefficients */ + for (k = 0; k < (n + 1) >> 1; k++) { + tmp1 = Af_QA[k]; + /* QA */ + tmp2 = Af_QA[n - k - 1]; + /* QA */ + Af_QA[k] = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(tmp2, rc_Q31), 1); + /* QA */ + Af_QA[n - k - 1] = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(tmp1, rc_Q31), 1); + /* QA */ + } + Af_QA[n] = Inlines.silk_RSHIFT32(rc_Q31, 31 - QA); + /* QA */ + + if (reached_max_gain != 0) { + /* Reached max prediction gain; set remaining coefficients to zero and exit loop */ + for (k = n + 1; k < D; k++) { + Af_QA[k] = 0; + } + break; + } + + /* Update C * Af and C * Ab */ + for (k = 0; k <= n + 1; k++) { + tmp1 = CAf[k]; + /* Q( -rshifts ) */ + tmp2 = CAb[n - k + 1]; + /* Q( -rshifts ) */ + CAf[k] = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(tmp2, rc_Q31), 1); + /* Q( -rshifts ) */ + CAb[n - k + 1] = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(tmp1, rc_Q31), 1); + /* Q( -rshifts ) */ + } + } + + if (reached_max_gain != 0) { + for (k = 0; k < D; k++) { + /* Scale coefficients */ + A_Q16[k] = -Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 16); + } + /* Subtract energy of preceding samples from C0 */ + if (rshifts > 0) { + for (s = 0; s < nb_subfr; s++) { + x_offset = x_ptr + s * subfr_length; + C0 -= (int) Inlines.silk_RSHIFT64(Inlines.silk_inner_prod16_aligned_64(x, x_offset, x, x_offset, D), rshifts); + } + } else { + for (s = 0; s < nb_subfr; s++) { + x_offset = x_ptr + s * subfr_length; + C0 -= Inlines.silk_LSHIFT32(Inlines.silk_inner_prod_self(x, x_offset, D), -rshifts); + } + } + /* Approximate residual energy */ + res_nrg.Val = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, C0), 2); + res_nrg_Q.Val = 0 - rshifts; + } else { + /* Return residual energy */ + nrg = CAf[0]; + /* Q( -rshifts ) */ + tmp1 = (int) 1 << 16; + /* Q16 */ + for (k = 0; k < D; k++) { + Atmp1 = Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 16); + /* Q16 */ + nrg = Inlines.silk_SMLAWW(nrg, CAf[k + 1], Atmp1); + /* Q( -rshifts ) */ + tmp1 = Inlines.silk_SMLAWW(tmp1, Atmp1, Atmp1); + /* Q16 */ + A_Q16[k] = -Atmp1; + } + res_nrg.Val = Inlines.silk_SMLAWW(nrg, Inlines.silk_SMMUL(((int) ((TuningParameters.FIND_LPC_COND_FAC) * ((long) 1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0), -tmp1);/* Q( -rshifts ) */ + res_nrg_Q.Val = -rshifts; + } + } +} diff --git a/app/src/main/java/org/concentus/CNG.java b/app/src/main/java/org/concentus/CNG.java new file mode 100644 index 0000000000..d08f9237dc --- /dev/null +++ b/app/src/main/java/org/concentus/CNG.java @@ -0,0 +1,206 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Comfort noise generation and estimation +/// +class CNG { + + /// + /// Generates excitation for CNG LPC synthesis + /// + /// O CNG excitation signal Q10 + /// I Random samples buffer Q10 + /// I Gain to apply + /// I Length + /// I/O Seed to random index generator + static void silk_CNG_exc( + int[] exc_Q10, + int exc_Q10_ptr, + int[] exc_buf_Q14, + int Gain_Q16, + int length, + BoxedValueInt rand_seed) { + int seed; + int i, idx, exc_mask; + + exc_mask = SilkConstants.CNG_BUF_MASK_MAX; + + while (exc_mask > length) { + exc_mask = Inlines.silk_RSHIFT(exc_mask, 1); + } + + seed = rand_seed.Val; + for (i = exc_Q10_ptr; i < exc_Q10_ptr + length; i++) { + seed = Inlines.silk_RAND(seed); + idx = (int) (Inlines.silk_RSHIFT(seed, 24) & exc_mask); + Inlines.OpusAssert(idx >= 0); + Inlines.OpusAssert(idx <= SilkConstants.CNG_BUF_MASK_MAX); + exc_Q10[i] = (short) Inlines.silk_SAT16(Inlines.silk_SMULWW(exc_buf_Q14[idx], Gain_Q16 >> 4)); + } + + rand_seed.Val = seed; + } + + /// + /// Resets CNG state + /// + /// I/O Decoder state + static void silk_CNG_Reset(SilkChannelDecoder psDec) { + int i, NLSF_step_Q15, NLSF_acc_Q15; + + NLSF_step_Q15 = Inlines.silk_DIV32_16(Short.MAX_VALUE, (short) (psDec.LPC_order + 1)); + NLSF_acc_Q15 = 0; + for (i = 0; i < psDec.LPC_order; i++) { + NLSF_acc_Q15 += NLSF_step_Q15; + psDec.sCNG.CNG_smth_NLSF_Q15[i] = (short) (NLSF_acc_Q15); + } + psDec.sCNG.CNG_smth_Gain_Q16 = 0; + psDec.sCNG.rand_seed = 3176576; + } + + /// + /// Updates CNG estimate, and applies the CNG when packet was lost + /// + /// I/O Decoder state + /// I/O Decoder control + /// I/O Signal + /// I Length of residual + static void silk_CNG( + SilkChannelDecoder psDec, + SilkDecoderControl psDecCtrl, + short[] frame, + int frame_ptr, + int length) { + int i, subfr; + int sum_Q6, max_Gain_Q16, gain_Q16; + short[] A_Q12 = new short[psDec.LPC_order]; + CNGState psCNG = psDec.sCNG; + + if (psDec.fs_kHz != psCNG.fs_kHz) { + /* Reset state */ + silk_CNG_Reset(psDec); + + psCNG.fs_kHz = psDec.fs_kHz; + } + + if (psDec.lossCnt == 0 && psDec.prevSignalType == SilkConstants.TYPE_NO_VOICE_ACTIVITY) { + /* Update CNG parameters */ + + /* Smoothing of LSF's */ + for (i = 0; i < psDec.LPC_order; i++) { + psCNG.CNG_smth_NLSF_Q15[i] += (short) (Inlines.silk_SMULWB((int) psDec.prevNLSF_Q15[i] - (int) psCNG.CNG_smth_NLSF_Q15[i], SilkConstants.CNG_NLSF_SMTH_Q16)); + } + + /* Find the subframe with the highest gain */ + max_Gain_Q16 = 0; + subfr = 0; + for (i = 0; i < psDec.nb_subfr; i++) { + if (psDecCtrl.Gains_Q16[i] > max_Gain_Q16) { + max_Gain_Q16 = psDecCtrl.Gains_Q16[i]; + subfr = i; + } + } + + /* Update CNG excitation buffer with excitation from this subframe */ + Arrays.MemMove(psCNG.CNG_exc_buf_Q14, 0, psDec.subfr_length, (psDec.nb_subfr - 1) * psDec.subfr_length); + + /* Smooth gains */ + for (i = 0; i < psDec.nb_subfr; i++) { + psCNG.CNG_smth_Gain_Q16 += Inlines.silk_SMULWB(psDecCtrl.Gains_Q16[i] - psCNG.CNG_smth_Gain_Q16, SilkConstants.CNG_GAIN_SMTH_Q16); + } + } + + /* Add CNG when packet is lost or during DTX */ + if (psDec.lossCnt != 0) { + int[] CNG_sig_Q10 = new int[length + SilkConstants.MAX_LPC_ORDER]; + + /* Generate CNG excitation */ + gain_Q16 = Inlines.silk_SMULWW(psDec.sPLC.randScale_Q14, psDec.sPLC.prevGain_Q16[1]); + if (gain_Q16 >= (1 << 21) || psCNG.CNG_smth_Gain_Q16 > (1 << 23)) { + gain_Q16 = Inlines.silk_SMULTT(gain_Q16, gain_Q16); + gain_Q16 = Inlines.silk_SUB_LSHIFT32(Inlines.silk_SMULTT(psCNG.CNG_smth_Gain_Q16, psCNG.CNG_smth_Gain_Q16), gain_Q16, 5); + gain_Q16 = Inlines.silk_LSHIFT32(Inlines.silk_SQRT_APPROX(gain_Q16), 16); + } else { + gain_Q16 = Inlines.silk_SMULWW(gain_Q16, gain_Q16); + gain_Q16 = Inlines.silk_SUB_LSHIFT32(Inlines.silk_SMULWW(psCNG.CNG_smth_Gain_Q16, psCNG.CNG_smth_Gain_Q16), gain_Q16, 5); + gain_Q16 = Inlines.silk_LSHIFT32(Inlines.silk_SQRT_APPROX(gain_Q16), 8); + } + + BoxedValueInt boxed_rand_seed = new BoxedValueInt(psCNG.rand_seed); + silk_CNG_exc(CNG_sig_Q10, SilkConstants.MAX_LPC_ORDER, psCNG.CNG_exc_buf_Q14, gain_Q16, length, boxed_rand_seed); + psCNG.rand_seed = boxed_rand_seed.Val; + + /* Convert CNG NLSF to filter representation */ + NLSF.silk_NLSF2A(A_Q12, psCNG.CNG_smth_NLSF_Q15, psDec.LPC_order); + + /* Generate CNG signal, by synthesis filtering */ + System.arraycopy(psCNG.CNG_synth_state, 0, CNG_sig_Q10, 0, SilkConstants.MAX_LPC_ORDER); + + for (i = 0; i < length; i++) { + int lpci = SilkConstants.MAX_LPC_ORDER + i; + Inlines.OpusAssert(psDec.LPC_order == 10 || psDec.LPC_order == 16); + /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ + sum_Q6 = Inlines.silk_RSHIFT(psDec.LPC_order, 1); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 1], A_Q12[0]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 2], A_Q12[1]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 3], A_Q12[2]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 4], A_Q12[3]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 5], A_Q12[4]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 6], A_Q12[5]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 7], A_Q12[6]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 8], A_Q12[7]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 9], A_Q12[8]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 10], A_Q12[9]); + + if (psDec.LPC_order == 16) { + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 11], A_Q12[10]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 12], A_Q12[11]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 13], A_Q12[12]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 14], A_Q12[13]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 15], A_Q12[14]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 16], A_Q12[15]); + } + + /* Update states */ + CNG_sig_Q10[lpci] = Inlines.silk_ADD_LSHIFT(CNG_sig_Q10[lpci], sum_Q6, 4); + + frame[frame_ptr + i] = Inlines.silk_ADD_SAT16(frame[frame_ptr + i], (short) (Inlines.silk_RSHIFT_ROUND(CNG_sig_Q10[lpci], 10))); + } + + System.arraycopy(CNG_sig_Q10, length, psCNG.CNG_synth_state, 0, SilkConstants.MAX_LPC_ORDER); + } else { + Arrays.MemSet(psCNG.CNG_synth_state, 0, psDec.LPC_order); + } + } +} diff --git a/app/src/main/java/org/concentus/CNGState.java b/app/src/main/java/org/concentus/CNGState.java new file mode 100644 index 0000000000..9f4d6cb8af --- /dev/null +++ b/app/src/main/java/org/concentus/CNGState.java @@ -0,0 +1,54 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Struct for CNG +/// +class CNGState { + + final int[] CNG_exc_buf_Q14 = new int[SilkConstants.MAX_FRAME_LENGTH]; + final short[] CNG_smth_NLSF_Q15 = new short[SilkConstants.MAX_LPC_ORDER]; + final int[] CNG_synth_state = new int[SilkConstants.MAX_LPC_ORDER]; + int CNG_smth_Gain_Q16 = 0; + int rand_seed = 0; + int fs_kHz = 0; + + void Reset() { + Arrays.MemSet(CNG_exc_buf_Q14, 0, SilkConstants.MAX_FRAME_LENGTH); + Arrays.MemSet(CNG_smth_NLSF_Q15, (short) 0, SilkConstants.MAX_LPC_ORDER); + Arrays.MemSet(CNG_synth_state, 0, SilkConstants.MAX_LPC_ORDER); + CNG_smth_Gain_Q16 = 0; + rand_seed = 0; + fs_kHz = 0; + } +} diff --git a/app/src/main/java/org/concentus/CWRS.java b/app/src/main/java/org/concentus/CWRS.java new file mode 100644 index 0000000000..3568ef9a5c --- /dev/null +++ b/app/src/main/java/org/concentus/CWRS.java @@ -0,0 +1,290 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class CWRS { + + /*Although derived separately, the pulse vector coding scheme is equivalent to +a Pyramid Vector Quantizer \cite{Fis86}. +Some additional notes about an early version appear at +http://people.xiph.org/~tterribe/notes/cwrs.html, but the codebook ordering +and the definitions of some terms have evolved since that was written. + +The conversion from a pulse vector to an integer index (encoding) and back +(decoding) is governed by two related functions, V(N,K) and U(N,K). + +V(N,K) = the number of combinations, with replacement, of N items, taken K +at a time, when a sign bit is added to each item taken at least once (i.e., +the number of N-dimensional unit pulse vectors with K pulses). +One way to compute this is via +V(N,K) = K>0 ? sum(k=1...K,2**k*choose(N,k)*choose(K-1,k-1)) : 1, +where choose() is the binomial function. +A table of values for N<10 and K<10 looks like: +V[10][10] = { +{1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{1, 2, 2, 2, 2, 2, 2, 2, 2, 2}, +{1, 4, 8, 12, 16, 20, 24, 28, 32, 36}, +{1, 6, 18, 38, 66, 102, 146, 198, 258, 326}, +{1, 8, 32, 88, 192, 360, 608, 952, 1408, 1992}, +{1, 10, 50, 170, 450, 1002, 1970, 3530, 5890, 9290}, +{1, 12, 72, 292, 912, 2364, 5336, 10836, 20256, 35436}, +{1, 14, 98, 462, 1666, 4942, 12642, 28814, 59906, 115598}, +{1, 16, 128, 688, 2816, 9424, 27008, 68464, 157184, 332688}, +{1, 18, 162, 978, 4482, 16722, 53154, 148626, 374274, 864146} +}; + +U(N,K) = the number of such combinations wherein N-1 objects are taken at +most K-1 at a time. +This is given by +U(N,K) = sum(k=0...K-1,V(N-1,k)) + = K>0 ? (V(N-1,K-1) + V(N,K-1))/2 : 0. +The latter expression also makes clear that U(N,K) is half the number of such +combinations wherein the first object is taken at least once. +Although it may not be clear from either of these definitions, U(N,K) is the +natural function to work with when enumerating the pulse vector codebooks, +not V(N,K). +U(N,K) is not well-defined for N=0, but with the extension +U(0,K) = K>0 ? 0 : 1, +the function becomes symmetric: U(N,K) = U(K,N), with a similar table: +U[10][10] = { +{1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, +{0, 1, 3, 5, 7, 9, 11, 13, 15, 17}, +{0, 1, 5, 13, 25, 41, 61, 85, 113, 145}, +{0, 1, 7, 25, 63, 129, 231, 377, 575, 833}, +{0, 1, 9, 41, 129, 321, 681, 1289, 2241, 3649}, +{0, 1, 11, 61, 231, 681, 1683, 3653, 7183, 13073}, +{0, 1, 13, 85, 377, 1289, 3653, 8989, 19825, 40081}, +{0, 1, 15, 113, 575, 2241, 7183, 19825, 48639, 108545}, +{0, 1, 17, 145, 833, 3649, 13073, 40081, 108545, 265729} +}; + +With this extension, V(N,K) may be written in terms of U(N,K): +V(N,K) = U(N,K) + U(N,K+1) +for all N>=0, K>=0. +Thus U(N,K+1) represents the number of combinations where the first element +is positive or zero, and U(N,K) represents the number of combinations where +it is negative. +With a large enough table of U(N,K) values, we could write O(N) encoding +and O(min(N*log(K),N+K)) decoding routines, but such a table would be +prohibitively large for small embedded devices (K may be as large as 32767 +for small N, and N may be as large as 200). + +Both functions obey the same recurrence relation: +V(N,K) = V(N-1,K) + V(N,K-1) + V(N-1,K-1), +U(N,K) = U(N-1,K) + U(N,K-1) + U(N-1,K-1), +for all N>0, K>0, with different initial conditions at N=0 or K=0. +This allows us to construct a row of one of the tables above given the +previous row or the next row. +Thus we can derive O(NK) encoding and decoding routines with O(K) memory +using only addition and subtraction. + +When encoding, we build up from the U(2,K) row and work our way forwards. +When decoding, we need to start at the U(N,K) row and work our way backwards, +which requires a means of computing U(N,K). +U(N,K) may be computed from two previous values with the same N: +U(N,K) = ((2*N-1)*U(N,K-1) - U(N,K-2))/(K-1) + U(N,K-2) +for all N>1, and since U(N,K) is symmetric, a similar relation holds for two +previous values with the same K: +U(N,K>1) = ((2*K-1)*U(N-1,K) - U(N-2,K))/(N-1) + U(N-2,K) +for all K>1. +This allows us to construct an arbitrary row of the U(N,K) table by starting +with the first two values, which are constants. +This saves roughly 2/3 the work in our O(NK) decoding routine, but costs O(K) +multiplications. +Similar relations can be derived for V(N,K), but are not used here. + +For N>0 and K>0, U(N,K) and V(N,K) take on the form of an (N-1)-degree +polynomial for fixed N. +The first few are +U(1,K) = 1, +U(2,K) = 2*K-1, +U(3,K) = (2*K-2)*K+1, +U(4,K) = (((4*K-6)*K+8)*K-3)/3, +U(5,K) = ((((2*K-4)*K+10)*K-8)*K+3)/3, +and +V(1,K) = 2, +V(2,K) = 4*K, +V(3,K) = 4*K*K+2, +V(4,K) = 8*(K*K+2)*K/3, +V(5,K) = ((4*K*K+20)*K*K+6)/3, +for all K>0. +This allows us to derive O(N) encoding and O(N*log(K)) decoding routines for +small N (and indeed decoding is also O(N) for N<3). + +@ARTICLE{Fis86, +author="Thomas R. Fischer", +title="A Pyramid Vector Quantizer", +journal="IEEE Transactions on Information Theory", +volume="IT-32", +number=4, +pages="568--583", +month=Jul, +year=1986 +}*/ + + static final int[] CELT_PVQ_U_ROW + = { + 0, 176, 351, 525, 698, 870, 1041, 1131, 1178, 1207, 1226, 1240, 1248, 1254, 1257 + }; + + /*U(N,K) = U(K,N) := N>0?K>0?U(N-1,K)+U(N,K-1)+U(N-1,K-1):0:K>0?1:0*/ + private static long CELT_PVQ_U(int _n, int _k) { + return CeltTables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[Inlines.IMIN(_n, _k)] + Inlines.IMAX(_n, _k)]; + } + + + /*V(N,K) := U(N,K)+U(N,K+1) = the number of PVQ codewords for a band of size N + with K pulses allocated to it.*/ + private static long CELT_PVQ_V(int _n, int _k) { + return (CELT_PVQ_U(_n, _k) + CELT_PVQ_U(_n, (_k) + 1)); + } + + static long icwrs(int _n, int[] _y) { + long i; + int j; + int k; + Inlines.OpusAssert(_n >= 2); + j = _n - 1; + i = (_y[j] < 0) ? 1 : 0; + k = Inlines.abs(_y[j]); + do { + j--; + i += CELT_PVQ_U(_n - j, k); + k += Inlines.abs(_y[j]); + if (_y[j] < 0) { + i += CELT_PVQ_U(_n - j, k + 1); + } + } while (j > 0); + return i; + } + + static void encode_pulses(int[] _y, int _n, int _k, EntropyCoder _enc) { + Inlines.OpusAssert(_k > 0); + _enc.enc_uint(icwrs(_n, _y), CELT_PVQ_V(_n, _k)); + } + + static int cwrsi(int _n, int _k, long _i, int[] _y) { + long p; + int s; + int k0; + short val; + int yy = 0; + int y_ptr = 0; + Inlines.OpusAssert(_k > 0); + Inlines.OpusAssert(_n > 1); + + while (_n > 2) { + long q; + /*Lots of pulses case:*/ + if (_k >= _n) { + int row; + row = CELT_PVQ_U_ROW[_n]; + /*Are the pulses in this dimension negative?*/ + p = CeltTables.CELT_PVQ_U_DATA[row + _k + 1]; + s = 0 - (_i >= p ? 1 : 0); + _i = _i - Inlines.CapToUInt32(p & s); + /*Count how many pulses were placed in this dimension.*/ + k0 = _k; + q = CeltTables.CELT_PVQ_U_DATA[row + _n]; + + if (q > _i) { + Inlines.OpusAssert(p > q); + _k = _n; + + do { + p = CeltTables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[--_k] + _n]; + } while (p > _i); + } else { + for (p = CeltTables.CELT_PVQ_U_DATA[row + _k]; p > _i; p = CeltTables.CELT_PVQ_U_DATA[row + _k]) { + _k--; + } + } + + _i -= p; + val = (short) ((k0 - _k + s) ^ s); + _y[y_ptr++] = val; + yy = Inlines.MAC16_16(yy, val, val); + } /*Lots of dimensions case:*/ else { + /*Are there any pulses in this dimension at all?*/ + p = CeltTables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[_k] + _n]; + q = CeltTables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[_k + 1] + _n]; + if (p <= _i && _i < q) { + _i -= p; + _y[y_ptr++] = 0; + } else { + /*Are the pulses in this dimension negative?*/ + s = 0 - (_i >= q ? 1 : 0); + _i = _i - Inlines.CapToUInt32(q & s); + /*Count how many pulses were placed in this dimension.*/ + k0 = _k; + do { + p = CeltTables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[--_k] + _n]; + } while (p > _i); + + _i -= p; + val = (short) ((k0 - _k + s) ^ s); + _y[y_ptr++] = val; + yy = Inlines.MAC16_16(yy, val, val); + } + } + _n--; + } + + /*_n==2*/ + p = (2L * _k + 1); + s = 0 - (_i >= p ? 1 : 0); + _i = _i - Inlines.CapToUInt32(p & s); + k0 = _k; + _k = (int) ((_i + 1) >> 1); + if (_k != 0) { + _i -= (2L * _k - 1); + } + + val = (short) ((k0 - _k + s) ^ s); + _y[y_ptr++] = val; + yy = Inlines.MAC16_16(yy, val, val); + /*_n==1*/ + s = -(int) _i; + val = (short) ((_k + s) ^ s); + _y[y_ptr] = val; + yy = Inlines.MAC16_16(yy, val, val); + return yy; + } + + static int decode_pulses(int[] _y, int _n, int _k, EntropyCoder _dec) { + return cwrsi(_n, _k, _dec.dec_uint(CELT_PVQ_V(_n, _k)), _y); + } +} diff --git a/app/src/main/java/org/concentus/CeltCommon.java b/app/src/main/java/org/concentus/CeltCommon.java new file mode 100644 index 0000000000..45426e7d4c --- /dev/null +++ b/app/src/main/java/org/concentus/CeltCommon.java @@ -0,0 +1,1231 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class CeltCommon { + + /* Table of 6*64/x, trained on real data to minimize the average error */ + private static final short[] inv_table = { + 255, 255, 156, 110, 86, 70, 59, 51, 45, 40, 37, 33, 31, 28, 26, 25, + 23, 22, 21, 20, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12, + 12, 12, 11, 11, 11, 10, 10, 10, 9, 9, 9, 9, 9, 9, 8, 8, + 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2,}; + + static int compute_vbr(CeltMode mode, AnalysisInfo analysis, int base_target, + int LM, int bitrate, int lastCodedBands, int C, int intensity, + int constrained_vbr, int stereo_saving, int tot_boost, + int tf_estimate, int pitch_change, int maxDepth, + OpusFramesize variable_duration, int lfe, int has_surround_mask, int surround_masking, + int temporal_vbr) { + /* The target rate in 8th bits per frame */ + int target; + int coded_bins; + int coded_bands; + int tf_calibration; + int nbEBands; + short[] eBands; + + nbEBands = mode.nbEBands; + eBands = mode.eBands; + + coded_bands = lastCodedBands != 0 ? lastCodedBands : nbEBands; + coded_bins = eBands[coded_bands] << LM; + if (C == 2) { + coded_bins += eBands[Inlines.IMIN(intensity, coded_bands)] << LM; + } + + target = base_target; + if (analysis.enabled && analysis.valid != 0 && analysis.activity < .4) { + target -= (int) ((coded_bins << EntropyCoder.BITRES) * (.4f - analysis.activity)); + } + + /* Stereo savings */ + if (C == 2) { + int coded_stereo_bands; + int coded_stereo_dof; + int max_frac; + coded_stereo_bands = Inlines.IMIN(intensity, coded_bands); + coded_stereo_dof = (eBands[coded_stereo_bands] << LM) - coded_stereo_bands; + /* Maximum fraction of the bits we can save if the signal is mono. */ + max_frac = Inlines.DIV32_16(Inlines.MULT16_16(((short) (0.5 + (0.8f) * (((int) 1) << (15))))/*Inlines.QCONST16(0.8f, 15)*/, coded_stereo_dof), coded_bins); + stereo_saving = Inlines.MIN16(stereo_saving, ((short) (0.5 + (1.0f) * (((int) 1) << (8))))/*Inlines.QCONST16(1.0f, 8)*/); + /*printf("%d %d %d ", coded_stereo_dof, coded_bins, tot_boost);*/ + target -= (int) Inlines.MIN32(Inlines.MULT16_32_Q15(max_frac, target), + Inlines.SHR32(Inlines.MULT16_16(stereo_saving - ((short) (0.5 + (0.1f) * (((int) 1) << (8))))/*Inlines.QCONST16(0.1f, 8)*/, (coded_stereo_dof << EntropyCoder.BITRES)), 8)); + } + /* Boost the rate according to dynalloc (minus the dynalloc average for calibration). */ + target += tot_boost - (16 << LM); + /* Apply transient boost, compensating for average boost. */ + tf_calibration = variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE + ? ((short) (0.5 + (0.02f) * (((int) 1) << (14))))/*Inlines.QCONST16(0.02f, 14)*/ : ((short) (0.5 + (0.04f) * (((int) 1) << (14))))/*Inlines.QCONST16(0.04f, 14)*/; + target += (int) Inlines.SHL32(Inlines.MULT16_32_Q15(tf_estimate - tf_calibration, target), 1); + + /* Apply tonality boost */ + if (analysis.enabled && analysis.valid != 0 && lfe == 0) { + int tonal_target; + float tonal; + + /* Tonality boost (compensating for the average). */ + tonal = Inlines.MAX16(0, analysis.tonality - .15f) - 0.09f; + tonal_target = target + (int) ((coded_bins << EntropyCoder.BITRES) * 1.2f * tonal); + if (pitch_change != 0) { + tonal_target += (int) ((coded_bins << EntropyCoder.BITRES) * .8f); + } + target = tonal_target; + } + + if (has_surround_mask != 0 && lfe == 0) { + int surround_target = target + (int) Inlines.SHR32(Inlines.MULT16_16(surround_masking, coded_bins << EntropyCoder.BITRES), CeltConstants.DB_SHIFT); + /*printf("%f %d %d %d %d %d %d ", surround_masking, coded_bins, st.end, st.intensity, surround_target, target, st.bitrate);*/ + target = Inlines.IMAX(target / 4, surround_target); + } + + { + int floor_depth; + int bins; + bins = eBands[nbEBands - 2] << LM; + /*floor_depth = Inlines.SHR32(Inlines.MULT16_16((C*bins<> 2); + target = Inlines.IMIN(target, floor_depth); + /*printf("%f %d\n", maxDepth, floor_depth);*/ + } + + if ((has_surround_mask == 0 || lfe != 0) && (constrained_vbr != 0 || bitrate < 64000)) { + int rate_factor; + rate_factor = Inlines.MAX16(0, (bitrate - 32000)); + if (constrained_vbr != 0) { + rate_factor = Inlines.MIN16(rate_factor, ((short) (0.5 + (0.67f) * (((int) 1) << (15))))/*Inlines.QCONST16(0.67f, 15)*/); + } + target = base_target + (int) Inlines.MULT16_32_Q15(rate_factor, target - base_target); + } + + if (has_surround_mask == 0 && tf_estimate < ((short) (0.5 + (.2f) * (((int) 1) << (14))))/*Inlines.QCONST16(.2f, 14)*/) { + int amount; + int tvbr_factor; + amount = Inlines.MULT16_16_Q15(((short) (0.5 + (.0000031f) * (((int) 1) << (30))))/*Inlines.QCONST16(.0000031f, 30)*/, Inlines.IMAX(0, Inlines.IMIN(32000, 96000 - bitrate))); + tvbr_factor = Inlines.SHR32(Inlines.MULT16_16(temporal_vbr, amount), CeltConstants.DB_SHIFT); + target += (int) Inlines.MULT16_32_Q15(tvbr_factor, target); + } + + /* Don't allow more than doubling the rate */ + target = Inlines.IMIN(2 * base_target, target); + + return target; + } + + static int transient_analysis(int[][] input, int len, int C, BoxedValueInt tf_estimate, BoxedValueInt tf_chan) { + int i; + int[] tmp; + int mem0, mem1; + int is_transient = 0; + int mask_metric = 0; + int c; + int tf_max; + int len2; + tf_chan.Val = 0; + tmp = new int[len]; + + len2 = len / 2; + for (c = 0; c < C; c++) { + int mean; + int unmask = 0; + int norm; + int maxE; + mem0 = 0; + mem1 = 0; + /* High-pass filter: (1 - 2*z^-1 + z^-2) / (1 - z^-1 + .5*z^-2) */ + for (i = 0; i < len; i++) { + int x, y; + x = Inlines.SHR32(input[c][i], CeltConstants.SIG_SHIFT); + y = Inlines.ADD32(mem0, x); + mem0 = mem1 + y - Inlines.SHL32(x, 1); + mem1 = x - Inlines.SHR32(y, 1); + tmp[i] = Inlines.EXTRACT16(Inlines.SHR32(y, 2)); + /*printf("%f ", tmp[i]);*/ + } + /*printf("\n");*/ + /* First few samples are bad because we don't propagate the memory */ + Arrays.MemSet(tmp, 0, 12); + + /* Normalize tmp to max range */ + { + int shift = 0; + shift = 14 - Inlines.celt_ilog2(1 + Inlines.celt_maxabs32(tmp, 0, len)); + if (shift != 0) { + for (i = 0; i < len; i++) { + tmp[i] = Inlines.SHL16(tmp[i], shift); + } + } + } + + mean = 0; + mem0 = 0; + /* Grouping by two to reduce complexity */ + /* Forward pass to compute the post-echo threshold*/ + for (i = 0; i < len2; i++) { + int x2 = (Inlines.PSHR32(Inlines.MULT16_16(tmp[2 * i], tmp[2 * i]) + Inlines.MULT16_16(tmp[2 * i + 1], tmp[2 * i + 1]), 16)); + mean += x2; + tmp[i] = (mem0 + Inlines.PSHR32(x2 - mem0, 4)); + mem0 = tmp[i]; + } + + mem0 = 0; + maxE = 0; + /* Backward pass to compute the pre-echo threshold */ + for (i = len2 - 1; i >= 0; i--) { + tmp[i] = (mem0 + Inlines.PSHR32(tmp[i] - mem0, 3)); + mem0 = tmp[i]; + maxE = Inlines.MAX16(maxE, (mem0)); + } + /*for (i=0;i> 1))); + /* Inverse of the mean energy in Q15+6 */ + norm = Inlines.SHL32((len2), 6 + 14) / Inlines.ADD32(CeltConstants.EPSILON, Inlines.SHR32(mean, 1)); + /* Compute harmonic mean discarding the unreliable boundaries + The data is smooth, so we only take 1/4th of the samples */ + unmask = 0; + for (i = 12; i < len2 - 5; i += 4) { + int id; + id = Inlines.MAX32(0, Inlines.MIN32(127, Inlines.MULT16_32_Q15((tmp[i] + CeltConstants.EPSILON), norm))); + /* Do not round to nearest */ + unmask += inv_table[id]; + } + /*printf("%d\n", unmask);*/ + /* Normalize, compensate for the 1/4th of the sample and the factor of 6 in the inverse table */ + unmask = 64 * unmask * 4 / (6 * (len2 - 17)); + if (unmask > mask_metric) { + tf_chan.Val = c; + mask_metric = unmask; + } + } + is_transient = mask_metric > 200 ? 1 : 0; + + /* Arbitrary metric for VBR boost */ + tf_max = Inlines.MAX16(0, (Inlines.celt_sqrt(27 * mask_metric) - 42)); + /* *tf_estimate = 1 + Inlines.MIN16(1, sqrt(Inlines.MAX16(0, tf_max-30))/20); */ + tf_estimate.Val = (Inlines.celt_sqrt(Inlines.MAX32(0, Inlines.SHL32(Inlines.MULT16_16(((short) (0.5 + (0.0069f) * (((int) 1) << (14))))/*Inlines.QCONST16(0.0069f, 14)*/, Inlines.MIN16(163, tf_max)), 14) - ((int) (0.5 + (0.139f) * (((int) 1) << (28))))/*Inlines.QCONST32(0.139f, 28)*/))); + /*printf("%d %f\n", tf_max, mask_metric);*/ + + /*printf("%d %f %d\n", is_transient, (float)*tf_estimate, tf_max);*/ + return is_transient; + } + + /* Looks for sudden increases of energy to decide whether we need to patch + the transient decision */ + static int patch_transient_decision(int[][] newE, int[][] oldE, int nbEBands, + int start, int end, int C) { + int i, c; + int mean_diff = 0; + int[] spread_old = new int[26]; + /* Apply an aggressive (-6 dB/Bark) spreading function to the old frame to + avoid false detection caused by irrelevant bands */ + if (C == 1) { + spread_old[start] = oldE[0][start]; + for (i = start + 1; i < end; i++) { + spread_old[i] = Inlines.MAX16((spread_old[i - 1] - ((short) (0.5 + (1.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/), oldE[0][i]); + } + } else { + spread_old[start] = Inlines.MAX16(oldE[0][start], oldE[1][start]); + for (i = start + 1; i < end; i++) { + spread_old[i] = Inlines.MAX16((spread_old[i - 1] - ((short) (0.5 + (1.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/), + Inlines.MAX16(oldE[0][i], oldE[1][i])); + } + } + for (i = end - 2; i >= start; i--) { + spread_old[i] = Inlines.MAX16(spread_old[i], (spread_old[i + 1] - ((short) (0.5 + (1.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/)); + } + /* Compute mean increase */ + c = 0; + do { + for (i = Inlines.IMAX(2, start); i < end - 1; i++) { + int x1, x2; + x1 = Inlines.MAX16(0, newE[c][i]); + x2 = Inlines.MAX16(0, spread_old[i]); + mean_diff = Inlines.ADD32(mean_diff, (Inlines.MAX16(0, Inlines.SUB16(x1, x2)))); + } + } while (++c < C); + mean_diff = Inlines.DIV32(mean_diff, C * (end - 1 - Inlines.IMAX(2, start))); + /*printf("%f %f %d\n", mean_diff, max_diff, count);*/ + return (mean_diff > ((short) (0.5 + (1.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/) ? 1 : 0; + } + + /** + * Apply window and compute the MDCT for all sub-frames and all channels in + * a frame + */ + static void compute_mdcts(CeltMode mode, int shortBlocks, int[][] input, + int[][] output, int C, int CC, int LM, int upsample) { + int overlap = mode.overlap; + int N; + int B; + int shift; + int i, b, c; + if (shortBlocks != 0) { + B = shortBlocks; + N = mode.shortMdctSize; + shift = mode.maxLM; + } else { + B = 1; + N = mode.shortMdctSize << LM; + shift = mode.maxLM - LM; + } + c = 0; + do { + for (b = 0; b < B; b++) { + /* Interleaving the sub-frames while doing the MDCTs */ + MDCT.clt_mdct_forward( + mode.mdct, + input[c], + b * N, + output[c], + b, + mode.window, + overlap, + shift, + B); + } + } while (++c < CC); + + if (CC == 2 && C == 1) { + for (i = 0; i < B * N; i++) { + output[0][i] = Inlines.ADD32(Inlines.HALF32(output[0][i]), Inlines.HALF32(output[1][i])); + } + } + if (upsample != 1) { + c = 0; + do { + int bound = B * N / upsample; + for (i = 0; i < bound; i++) { + output[c][i] *= upsample; + } + Arrays.MemSetWithOffset(output[c], 0, bound, B * N - bound); + } while (++c < C); + } + } + + static void celt_preemphasis(short[] pcmp, int pcmp_ptr, int[] inp, int inp_ptr, + int N, int CC, int upsample, int[] coef, BoxedValueInt mem, int clip) { + int i; + int coef0; + int m; + int Nu; + + coef0 = coef[0]; + m = mem.Val; + + /* Fast path for the normal 48kHz case and no clipping */ + if (coef[1] == 0 && upsample == 1 && clip == 0) { + for (i = 0; i < N; i++) { + int x = pcmp[pcmp_ptr + (CC * i)]; + /* Apply pre-emphasis */ + inp[inp_ptr + i] = Inlines.SHL32(x, CeltConstants.SIG_SHIFT) - m; + m = Inlines.SHR32(Inlines.MULT16_16(coef0, x), 15 - CeltConstants.SIG_SHIFT); + } + mem.Val = m; + return; + } + + Nu = N / upsample; + if (upsample != 1) { + Arrays.MemSetWithOffset(inp, 0, inp_ptr, N); + } + for (i = 0; i < Nu; i++) { + inp[inp_ptr + (i * upsample)] = pcmp[pcmp_ptr + (CC * i)]; + } + + for (i = 0; i < N; i++) { + int x; + x = (inp[inp_ptr + i]); + /* Apply pre-emphasis */ + inp[inp_ptr + i] = Inlines.SHL32(x, CeltConstants.SIG_SHIFT) - m; + m = Inlines.SHR32(Inlines.MULT16_16(coef0, x), 15 - CeltConstants.SIG_SHIFT); + } + + mem.Val = m; + } + + static void celt_preemphasis(short[] pcmp, int[] inp, int inp_ptr, + int N, int CC, int upsample, int[] coef, BoxedValueInt mem, int clip) { + int i; + int coef0; + int m; + int Nu; + + coef0 = coef[0]; + m = mem.Val; + + /* Fast path for the normal 48kHz case and no clipping */ + if (coef[1] == 0 && upsample == 1 && clip == 0) { + for (i = 0; i < N; i++) { + int x; + x = pcmp[CC * i]; + /* Apply pre-emphasis */ + inp[inp_ptr + i] = Inlines.SHL32(x, CeltConstants.SIG_SHIFT) - m; + m = Inlines.SHR32(Inlines.MULT16_16(coef0, x), 15 - CeltConstants.SIG_SHIFT); + } + mem.Val = m; + return; + } + + Nu = N / upsample; + if (upsample != 1) { + Arrays.MemSetWithOffset(inp, 0, inp_ptr, N); + } + for (i = 0; i < Nu; i++) { + inp[inp_ptr + (i * upsample)] = pcmp[CC * i]; + } + + for (i = 0; i < N; i++) { + int x; + x = (inp[inp_ptr + i]); + /* Apply pre-emphasis */ + inp[inp_ptr + i] = Inlines.SHL32(x, CeltConstants.SIG_SHIFT) - m; + m = Inlines.SHR32(Inlines.MULT16_16(coef0, x), 15 - CeltConstants.SIG_SHIFT); + } + + mem.Val = m; + } + + static int l1_metric(int[] tmp, int N, int LM, int bias) { + int i; + int L1; + L1 = 0; + for (i = 0; i < N; i++) { + L1 += Inlines.EXTEND32(Inlines.ABS32(tmp[i])); + } + + /* When in doubt, prefer good freq resolution */ + L1 = Inlines.MAC16_32_Q15(L1, (LM * bias), (L1)); + return L1; + + } + + static int tf_analysis(CeltMode m, int len, int isTransient, + int[] tf_res, int lambda, int[][] X, int N0, int LM, + BoxedValueInt tf_sum, int tf_estimate, int tf_chan) { + int i; + int[] metric; + int cost0; + int cost1; + int[] path0; + int[] path1; + int[] tmp; + int[] tmp_1; + int sel; + int[] selcost = new int[2]; + int tf_select = 0; + int bias; + + bias = Inlines.MULT16_16_Q14(((short) (0.5 + (.04f) * (((int) 1) << (15))))/*Inlines.QCONST16(.04f, 15)*/, Inlines.MAX16((short) (0 - ((short) (0.5 + (.25f) * (((int) 1) << (14))))/*Inlines.QCONST16(.25f, 14)*/), (((short) (0.5 + (.5f) * (((int) 1) << (14))))/*Inlines.QCONST16(.5f, 14)*/ - tf_estimate))); + /*printf("%f ", bias);*/ + + metric = new int[len]; + tmp = new int[(m.eBands[len] - m.eBands[len - 1]) << LM]; + tmp_1 = new int[(m.eBands[len] - m.eBands[len - 1]) << LM]; + path0 = new int[len]; + path1 = new int[len]; + + tf_sum.Val = 0; + for (i = 0; i < len; i++) { + int k, N; + int narrow; + int L1, best_L1; + int best_level = 0; + N = (m.eBands[i + 1] - m.eBands[i]) << LM; + /* band is too narrow to be split down to LM=-1 */ + narrow = ((m.eBands[i + 1] - m.eBands[i]) == 1) ? 1 : 0; + System.arraycopy(X[tf_chan], (m.eBands[i] << LM), tmp, 0, N); + /* Just add the right channel if we're in stereo */ + /*if (C==2) + for (j=0;j> LM, 1 << LM); + L1 = l1_metric(tmp_1, N, LM + 1, bias); + if (L1 < best_L1) { + best_L1 = L1; + best_level = -1; + } + } + /*printf ("%f ", L1);*/ + for (k = 0; k < LM + (!(isTransient != 0 || narrow != 0) ? 1 : 0); k++) { + int B; + + if (isTransient != 0) { + B = (LM - k - 1); + } else { + B = k + 1; + } + + Bands.haar1ZeroOffset(tmp, N >> k, 1 << k); + + L1 = l1_metric(tmp, N, B, bias); + + if (L1 < best_L1) { + best_L1 = L1; + best_level = k + 1; + } + } + /*printf ("%d ", isTransient ? LM-best_level : best_level);*/ + /* metric is in Q1 to be able to select the mid-point (-0.5) for narrower bands */ + if (isTransient != 0) { + metric[i] = 2 * best_level; + } else { + metric[i] = -2 * best_level; + } + tf_sum.Val += (isTransient != 0 ? LM : 0) - metric[i] / 2; + /* For bands that can't be split to -1, set the metric to the half-way point to avoid + biasing the decision */ + if (narrow != 0 && (metric[i] == 0 || metric[i] == -2 * LM)) { + metric[i] -= 1; + } + /*printf("%d ", metric[i]);*/ + } + /*printf("\n");*/ + /* Search for the optimal tf resolution, including tf_select */ + tf_select = 0; + for (sel = 0; sel < 2; sel++) { + cost0 = 0; + cost1 = isTransient != 0 ? 0 : lambda; + for (i = 1; i < len; i++) { + int curr0, curr1; + curr0 = Inlines.IMIN(cost0, cost1 + lambda); + curr1 = Inlines.IMIN(cost0 + lambda, cost1); + cost0 = curr0 + Inlines.abs(metric[i] - 2 * CeltTables.tf_select_table[LM][4 * isTransient + 2 * sel + 0]); + cost1 = curr1 + Inlines.abs(metric[i] - 2 * CeltTables.tf_select_table[LM][4 * isTransient + 2 * sel + 1]); + } + cost0 = Inlines.IMIN(cost0, cost1); + selcost[sel] = cost0; + } + /* For now, we're conservative and only allow tf_select=1 for transients. + * If tests confirm it's useful for non-transients, we could allow it. */ + if (selcost[1] < selcost[0] && isTransient != 0) { + tf_select = 1; + } + cost0 = 0; + cost1 = isTransient != 0 ? 0 : lambda; + /* Viterbi forward pass */ + for (i = 1; i < len; i++) { + int curr0, curr1; + int from0, from1; + + from0 = cost0; + from1 = cost1 + lambda; + if (from0 < from1) { + curr0 = from0; + path0[i] = 0; + } else { + curr0 = from1; + path0[i] = 1; + } + + from0 = cost0 + lambda; + from1 = cost1; + if (from0 < from1) { + curr1 = from0; + path1[i] = 0; + } else { + curr1 = from1; + path1[i] = 1; + } + cost0 = curr0 + Inlines.abs(metric[i] - 2 * CeltTables.tf_select_table[LM][4 * isTransient + 2 * tf_select + 0]); + cost1 = curr1 + Inlines.abs(metric[i] - 2 * CeltTables.tf_select_table[LM][4 * isTransient + 2 * tf_select + 1]); + } + tf_res[len - 1] = cost0 < cost1 ? 0 : 1; + /* Viterbi backward pass to check the decisions */ + for (i = len - 2; i >= 0; i--) { + if (tf_res[i + 1] == 1) { + tf_res[i] = path1[i + 1]; + } else { + tf_res[i] = path0[i + 1]; + } + } + /*printf("%d %f\n", *tf_sum, tf_estimate);*/ + + return tf_select; + } + + static void tf_encode(int start, int end, int isTransient, int[] tf_res, int LM, int tf_select, EntropyCoder enc) { + int curr, i; + int tf_select_rsv; + int tf_changed; + int logp; + int budget; + int tell; + budget = enc.storage * 8; + tell = enc.tell(); + logp = isTransient != 0 ? 2 : 4; + /* Reserve space to code the tf_select decision. */ + tf_select_rsv = (LM > 0 && tell + logp + 1 <= budget) ? 1 : 0; + budget -= tf_select_rsv; + curr = tf_changed = 0; + for (i = start; i < end; i++) { + if (tell + logp <= budget) { + enc.enc_bit_logp(tf_res[i] ^ curr, logp); + tell = enc.tell(); + curr = tf_res[i]; + tf_changed |= curr; + } else { + tf_res[i] = curr; + } + logp = isTransient != 0 ? 4 : 5; + } + /* Only code tf_select if it would actually make a difference. */ + if (tf_select_rsv != 0 + && CeltTables.tf_select_table[LM][4 * isTransient + 0 + tf_changed] + != CeltTables.tf_select_table[LM][4 * isTransient + 2 + tf_changed]) { + enc.enc_bit_logp(tf_select, 1); + } else { + tf_select = 0; + } + for (i = start; i < end; i++) { + tf_res[i] = CeltTables.tf_select_table[LM][4 * isTransient + 2 * tf_select + tf_res[i]]; + } + /*for(i=0;i Inlines.MULT16_32_Q15((m.eBands[13] << (LM + 1)), sumLR)) ? 1 : 0; + } + + static int median_of_5(int[] x, int x_ptr) { + int t0, t1, t2, t3, t4; + t2 = x[x_ptr + 2]; + if (x[x_ptr] > x[x_ptr + 1]) { + t0 = x[x_ptr + 1]; + t1 = x[x_ptr]; + } else { + t0 = x[x_ptr]; + t1 = x[x_ptr + 1]; + } + if (x[x_ptr + 3] > x[x_ptr + 4]) { + t3 = x[x_ptr + 4]; + t4 = x[x_ptr + 3]; + } else { + t3 = x[x_ptr + 3]; + t4 = x[x_ptr + 4]; + } + if (t0 > t3) { + // swap the pairs + int tmp = t3; + t3 = t0; + t0 = tmp; + tmp = t4; + t4 = t1; + t1 = tmp; + } + if (t2 > t1) { + if (t1 < t3) { + return Inlines.MIN16(t2, t3); + } else { + return Inlines.MIN16(t4, t1); + } + } else if (t2 < t3) { + return Inlines.MIN16(t1, t3); + } else { + return Inlines.MIN16(t2, t4); + } + } + + static int median_of_3(int[] x, int x_ptr) { + int t0, t1, t2; + if (x[x_ptr] > x[x_ptr + 1]) { + t0 = x[x_ptr + 1]; + t1 = x[x_ptr]; + } else { + t0 = x[x_ptr]; + t1 = x[x_ptr + 1]; + } + t2 = x[x_ptr + 2]; + if (t1 < t2) { + return t1; + } else if (t0 < t2) { + return t2; + } else { + return t0; + } + } + + static int dynalloc_analysis(int[][] bandLogE, int[][] bandLogE2, + int nbEBands, int start, int end, int C, int[] offsets, int lsb_depth, short[] logN, + int isTransient, int vbr, int constrained_vbr, short[] eBands, int LM, + int effectiveBytes, BoxedValueInt tot_boost_, int lfe, int[] surround_dynalloc) { + int i, c; + int tot_boost = 0; + int maxDepth; + int[][] follower = Arrays.InitTwoDimensionalArrayInt(2, nbEBands); + int[] noise_floor = new int[C * nbEBands]; // opt: partitioned array + + Arrays.MemSet(offsets, 0, nbEBands); + /* Dynamic allocation code */ + maxDepth = (0 - ((short) (0.5 + (31.9f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(31.9f, CeltConstants.DB_SHIFT)*/); + for (i = 0; i < end; i++) { + /* Noise floor must take into account eMeans, the depth, the width of the bands + and the preemphasis filter (approx. square of bark band ID) */ + noise_floor[i] = (Inlines.MULT16_16(((short) (0.5 + (0.0625f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.0625f, CeltConstants.DB_SHIFT)*/, logN[i]) + + ((short) (0.5 + (.5f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/ + Inlines.SHL16((9 - lsb_depth), CeltConstants.DB_SHIFT) - Inlines.SHL16(CeltTables.eMeans[i], 6) + + Inlines.MULT16_16(((short) (0.5 + (0.0062f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.0062f, CeltConstants.DB_SHIFT)*/, (i + 5) * (i + 5))); + } + c = 0; + do { + for (i = 0; i < end; i++) { + maxDepth = Inlines.MAX16(maxDepth, (bandLogE[c][i] - noise_floor[i])); + } + } while (++c < C); + /* Make sure that dynamic allocation can't make us bust the budget */ + if (effectiveBytes > 50 && LM >= 1 && lfe == 0) { + int last = 0; + c = 0; + do { + int offset; + int tmp; + int[] f = follower[c]; + f[0] = bandLogE2[c][0]; + for (i = 1; i < end; i++) { + /* The last band to be at least 3 dB higher than the previous one + is the last we'll consider. Otherwise, we run into problems on + bandlimited signals. */ + if (bandLogE2[c][i] > bandLogE2[c][i - 1] + ((short) (0.5 + (0.5f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.5f, CeltConstants.DB_SHIFT)*/) { + last = i; + } + f[i] = Inlines.MIN16((f[i - 1] + ((short) (0.5 + (1.5f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.5f, CeltConstants.DB_SHIFT)*/), bandLogE2[c][i]); + } + for (i = last - 1; i >= 0; i--) { + f[i] = Inlines.MIN16(f[i], Inlines.MIN16((f[i + 1] + ((short) (0.5 + (2.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(2.0f, CeltConstants.DB_SHIFT)*/), bandLogE2[c][i])); + } + + /* Combine with a median filter to avoid dynalloc triggering unnecessarily. + The "offset" value controls how conservative we are -- a higher offset + reduces the impact of the median filter and makes dynalloc use more bits. */ + offset = ((short) (0.5 + (1.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/; + for (i = 2; i < end - 2; i++) { + f[i] = Inlines.MAX16(f[i], median_of_5(bandLogE2[c], i - 2) - offset); + } + tmp = median_of_3(bandLogE2[c], 0) - offset; + f[0] = Inlines.MAX16(f[0], tmp); + f[1] = Inlines.MAX16(f[1], tmp); + tmp = median_of_3(bandLogE2[c], end - 3) - offset; + f[end - 2] = Inlines.MAX16(f[end - 2], tmp); + f[end - 1] = Inlines.MAX16(f[end - 1], tmp); + + for (i = 0; i < end; i++) { + f[i] = Inlines.MAX16(f[i], noise_floor[i]); + } + } while (++c < C); + if (C == 2) { + for (i = start; i < end; i++) { + /* Consider 24 dB "cross-talk" */ + follower[1][i] = Inlines.MAX16(follower[1][i], follower[0][i] - ((short) (0.5 + (4.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(4.0f, CeltConstants.DB_SHIFT)*/); + follower[0][i] = Inlines.MAX16(follower[0][i], follower[1][i] - ((short) (0.5 + (4.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(4.0f, CeltConstants.DB_SHIFT)*/); + follower[0][i] = Inlines.HALF16(Inlines.MAX16(0, bandLogE[0][i] - follower[0][i]) + Inlines.MAX16(0, bandLogE[1][i] - follower[1][i])); + } + } else { + for (i = start; i < end; i++) { + follower[0][i] = Inlines.MAX16(0, bandLogE[0][i] - follower[0][i]); + } + } + for (i = start; i < end; i++) { + follower[0][i] = Inlines.MAX16(follower[0][i], surround_dynalloc[i]); + } + /* For non-transient CBR/CVBR frames, halve the dynalloc contribution */ + if ((vbr == 0 || constrained_vbr != 0) && isTransient == 0) { + for (i = start; i < end; i++) { + follower[0][i] = Inlines.HALF16(follower[0][i]); + } + } + for (i = start; i < end; i++) { + int width; + int boost; + int boost_bits; + + if (i < 8) { + follower[0][i] *= 2; + } + if (i >= 12) { + follower[0][i] = Inlines.HALF16(follower[0][i]); + } + follower[0][i] = Inlines.MIN16(follower[0][i], ((short) (0.5 + (4) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(4, CeltConstants.DB_SHIFT)*/); + + width = C * (eBands[i + 1] - eBands[i]) << LM; + if (width < 6) { + boost = (int) Inlines.SHR32((follower[0][i]), CeltConstants.DB_SHIFT); + boost_bits = boost * width << EntropyCoder.BITRES; + } else if (width > 48) { + boost = (int) Inlines.SHR32((follower[0][i]) * 8, CeltConstants.DB_SHIFT); + boost_bits = (boost * width << EntropyCoder.BITRES) / 8; + } else { + boost = (int) Inlines.SHR32((follower[0][i]) * width / 6, CeltConstants.DB_SHIFT); + boost_bits = boost * 6 << EntropyCoder.BITRES; + } + /* For CBR and non-transient CVBR frames, limit dynalloc to 1/4 of the bits */ + if ((vbr == 0 || (constrained_vbr != 0 && isTransient == 0)) + && (tot_boost + boost_bits) >> EntropyCoder.BITRES >> 3 > effectiveBytes / 4) { + int cap = ((effectiveBytes / 4) << EntropyCoder.BITRES << 3); + offsets[i] = cap - tot_boost; + tot_boost = cap; + break; + } else { + offsets[i] = boost; + tot_boost += boost_bits; + } + } + } + + tot_boost_.Val = tot_boost; + + return maxDepth; + } + + static void deemphasis(int[][] input, int[] input_ptrs, short[] pcm, int pcm_ptr, int N, int C, int downsample, int[] coef, + int[] mem, int accum) { + int c; + int Nd; + int apply_downsampling = 0; + int coef0; + int[] scratch = new int[N]; + coef0 = coef[0]; + Nd = N / downsample; + c = 0; + do { + int j; + int x_ptr; + int y; + int m = mem[c]; + int[] x = input[c]; + x_ptr = input_ptrs[c]; + y = pcm_ptr + c; + if (downsample > 1) { + /* Shortcut for the standard (non-custom modes) case */ + for (j = 0; j < N; j++) { + int tmp = x[x_ptr + j] + m + CeltConstants.VERY_SMALL; + m = Inlines.MULT16_32_Q15(coef0, tmp); + scratch[j] = tmp; + } + apply_downsampling = 1; + } else /* Shortcut for the standard (non-custom modes) case */ if (accum != 0) // should never hit this branch? + { + for (j = 0; j < N; j++) { + int tmp = x[x_ptr + j] + m + CeltConstants.VERY_SMALL; + m = Inlines.MULT16_32_Q15(coef0, tmp); + pcm[y + (j * C)] = Inlines.SAT16(Inlines.ADD32(pcm[y + (j * C)], Inlines.SIG2WORD16(tmp))); + } + } else { + for (j = 0; j < N; j++) { + int tmp = (x[x_ptr + j] + m + CeltConstants.VERY_SMALL); // Opus bug: This can overflow. + if (x[x_ptr + j] > 0 && m > 0 && tmp < 0) // This is a hack to saturate to INT_MAXVALUE + { + tmp = Integer.MAX_VALUE; + m = Integer.MAX_VALUE; + } else { + m = Inlines.MULT16_32_Q15(coef0, tmp); + } + pcm[y + (j * C)] = Inlines.SIG2WORD16(tmp); + } + } + mem[c] = m; + + if (apply_downsampling != 0) { + /* Perform down-sampling */ + { + for (j = 0; j < Nd; j++) { + pcm[y + (j * C)] = Inlines.SIG2WORD16(scratch[j * downsample]); + } + } + } + } while (++c < C); + + } + + static void celt_synthesis(CeltMode mode, int[][] X, int[][] out_syn, int[] out_syn_ptrs, + int[] oldBandE, int start, int effEnd, int C, int CC, + int isTransient, int LM, int downsample, + int silence) { + int c, i; + int M; + int b; + int B; + int N, NB; + int shift; + int nbEBands; + int overlap; + int[] freq; + + overlap = mode.overlap; + nbEBands = mode.nbEBands; + N = mode.shortMdctSize << LM; + freq = new int[N]; + /** + * < Interleaved signal MDCTs + */ + M = 1 << LM; + + if (isTransient != 0) { + B = M; + NB = mode.shortMdctSize; + shift = mode.maxLM; + } else { + B = 1; + NB = mode.shortMdctSize << LM; + shift = mode.maxLM - LM; + } + + if (CC == 2 && C == 1) { + /* Copying a mono streams to two channels */ + int freq2; + Bands.denormalise_bands(mode, X[0], freq, 0, oldBandE, 0, start, effEnd, M, + downsample, silence); + /* Store a temporary copy in the output buffer because the IMDCT destroys its input. */ + freq2 = out_syn_ptrs[1] + (overlap / 2); + System.arraycopy(freq, 0, out_syn[1], freq2, N); + for (b = 0; b < B; b++) { + MDCT.clt_mdct_backward(mode.mdct, out_syn[1], freq2 + b, out_syn[0], out_syn_ptrs[0] + (NB * b), mode.window, overlap, shift, B); + } + for (b = 0; b < B; b++) { + MDCT.clt_mdct_backward(mode.mdct, freq, b, out_syn[1], out_syn_ptrs[1] + (NB * b), mode.window, overlap, shift, B); + } + } else if (CC == 1 && C == 2) { + /* Downmixing a stereo stream to mono */ + int freq2 = out_syn_ptrs[0] + (overlap / 2); + Bands.denormalise_bands(mode, X[0], freq, 0, oldBandE, 0, start, effEnd, M, + downsample, silence); + /* Use the output buffer as temp array before downmixing. */ + Bands.denormalise_bands(mode, X[1], out_syn[0], freq2, oldBandE, nbEBands, start, effEnd, M, + downsample, silence); + for (i = 0; i < N; i++) { + freq[i] = Inlines.HALF32(Inlines.ADD32(freq[i], out_syn[0][freq2 + i])); + } + for (b = 0; b < B; b++) { + MDCT.clt_mdct_backward(mode.mdct, freq, b, out_syn[0], out_syn_ptrs[0] + (NB * b), mode.window, overlap, shift, B); + } + } else { + /* Normal case (mono or stereo) */ + c = 0; + do { + Bands.denormalise_bands(mode, X[c], freq, 0, oldBandE, c * nbEBands, start, effEnd, M, + downsample, silence); + for (b = 0; b < B; b++) { + MDCT.clt_mdct_backward(mode.mdct, freq, b, out_syn[c], out_syn_ptrs[c] + (NB * b), mode.window, overlap, shift, B); + } + } while (++c < CC); + } + + } + + static void tf_decode(int start, int end, int isTransient, int[] tf_res, int LM, EntropyCoder dec) { + int i, curr, tf_select; + int tf_select_rsv; + int tf_changed; + int logp; + int budget; + int tell; + + budget = dec.storage * 8; + tell = dec.tell(); + logp = isTransient != 0 ? 2 : 4; + tf_select_rsv = (LM > 0 && tell + logp + 1 <= budget) ? 1 : 0; + budget -= tf_select_rsv; + tf_changed = curr = 0; + for (i = start; i < end; i++) { + if (tell + logp <= budget) { + curr ^= dec.dec_bit_logp(logp); + tell = dec.tell(); + tf_changed |= curr; + } + tf_res[i] = curr; + logp = isTransient != 0 ? 4 : 5; + } + tf_select = 0; + if (tf_select_rsv != 0 + && CeltTables.tf_select_table[LM][4 * isTransient + 0 + tf_changed] + != CeltTables.tf_select_table[LM][4 * isTransient + 2 + tf_changed]) { + tf_select = dec.dec_bit_logp(1); + } + for (i = start; i < end; i++) { + tf_res[i] = CeltTables.tf_select_table[LM][4 * isTransient + 2 * tf_select + tf_res[i]]; + } + } + + static int celt_plc_pitch_search(int[][] decode_mem, int C) { + BoxedValueInt pitch_index = new BoxedValueInt(0); + int[] lp_pitch_buf = new int[CeltConstants.DECODE_BUFFER_SIZE >> 1]; + Pitch.pitch_downsample(decode_mem, lp_pitch_buf, + CeltConstants.DECODE_BUFFER_SIZE, C); + Pitch.pitch_search(lp_pitch_buf, CeltConstants.PLC_PITCH_LAG_MAX >> 1, lp_pitch_buf, + CeltConstants.DECODE_BUFFER_SIZE - CeltConstants.PLC_PITCH_LAG_MAX, + CeltConstants.PLC_PITCH_LAG_MAX - CeltConstants.PLC_PITCH_LAG_MIN, pitch_index); + pitch_index.Val = CeltConstants.PLC_PITCH_LAG_MAX - pitch_index.Val; + + return pitch_index.Val; + } + + static int resampling_factor(int rate) { + int ret; + switch (rate) { + case 48000: + ret = 1; + break; + case 24000: + ret = 2; + break; + case 16000: + ret = 3; + break; + case 12000: + ret = 4; + break; + case 8000: + ret = 6; + break; + default: + Inlines.OpusAssert(false); + ret = 0; + break; + } + return ret; + } + + static void comb_filter_const(int[] y, int y_ptr, int[] x, int x_ptr, int T, int N, + int g10, int g11, int g12) { + int x0, x1, x2, x3, x4; + int i; + int xpt = x_ptr - T; + x4 = x[xpt - 2]; + x3 = x[xpt - 1]; + x2 = x[xpt]; + x1 = x[xpt + 1]; + for (i = 0; i < N; i++) { + x0 = x[xpt + i + 2]; + y[y_ptr + i] = x[x_ptr + i] + + Inlines.MULT16_32_Q15(g10, x2) + + Inlines.MULT16_32_Q15(g11, Inlines.ADD32(x1, x3)) + + Inlines.MULT16_32_Q15(g12, Inlines.ADD32(x0, x4)); + x4 = x3; + x3 = x2; + x2 = x1; + x1 = x0; + } + } + + private static final short[][] gains = { + new short[]{((short) (0.5 + (0.3066406250f) * (((int) 1) << (15))))/*Inlines.QCONST16(0.3066406250f, 15)*/, ((short) (0.5 + (0.2170410156f) * (((int) 1) << (15))))/*Inlines.QCONST16(0.2170410156f, 15)*/, ((short) (0.5 + (0.1296386719f) * (((int) 1) << (15))))/*Inlines.QCONST16(0.1296386719f, 15)*/}, + new short[]{((short) (0.5 + (0.4638671875f) * (((int) 1) << (15))))/*Inlines.QCONST16(0.4638671875f, 15)*/, ((short) (0.5 + (0.2680664062f) * (((int) 1) << (15))))/*Inlines.QCONST16(0.2680664062f, 15)*/, ((short) (0.5 + (0.0f) * (((int) 1) << (15))))/*Inlines.QCONST16(0.0f, 15)*/}, + new short[]{((short) (0.5 + (0.7998046875f) * (((int) 1) << (15))))/*Inlines.QCONST16(0.7998046875f, 15)*/, ((short) (0.5 + (0.1000976562f) * (((int) 1) << (15))))/*Inlines.QCONST16(0.1000976562f, 15)*/, ((short) (0.5 + (0.0f) * (((int) 1) << (15))))/*Inlines.QCONST16(0.0f, 15)*/} + }; + + static void comb_filter(int[] y, int y_ptr, int[] x, int x_ptr, int T0, int T1, int N, + int g0, int g1, int tapset0, int tapset1, + int[] window, int overlap) { + int i; + /* printf ("%d %d %f %f\n", T0, T1, g0, g1); */ + int g00, g01, g02, g10, g11, g12; + int x0, x1, x2, x3, x4; + + if (g0 == 0 && g1 == 0) { + /* OPT: Happens to work without the OPUS_MOVE(), but only because the current encoder already copies x to y */ + if (x_ptr != y_ptr) { + //x.MemMoveTo(y, N); + } + + return; + } + g00 = Inlines.MULT16_16_P15(g0, gains[tapset0][0]); + g01 = Inlines.MULT16_16_P15(g0, gains[tapset0][1]); + g02 = Inlines.MULT16_16_P15(g0, gains[tapset0][2]); + g10 = Inlines.MULT16_16_P15(g1, gains[tapset1][0]); + g11 = Inlines.MULT16_16_P15(g1, gains[tapset1][1]); + g12 = Inlines.MULT16_16_P15(g1, gains[tapset1][2]); + x1 = x[x_ptr - T1 + 1]; + x2 = x[x_ptr - T1]; + x3 = x[x_ptr - T1 - 1]; + x4 = x[x_ptr - T1 - 2]; + /* If the filter didn't change, we don't need the overlap */ + if (g0 == g1 && T0 == T1 && tapset0 == tapset1) { + overlap = 0; + } + for (i = 0; i < overlap; i++) { + int f; + x0 = x[x_ptr + i - T1 + 2]; + f = Inlines.MULT16_16_Q15(window[i], window[i]); + y[y_ptr + i] = x[x_ptr + i] + + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15((short) (CeltConstants.Q15ONE - f), g00), x[x_ptr + i - T0]) + + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15((short) (CeltConstants.Q15ONE - f), g01), Inlines.ADD32(x[x_ptr + i - T0 + 1], x[x_ptr + i - T0 - 1])) + + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15((short) (CeltConstants.Q15ONE - f), g02), Inlines.ADD32(x[x_ptr + i - T0 + 2], x[x_ptr + i - T0 - 2])) + + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15(f, g10), x2) + + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15(f, g11), Inlines.ADD32(x1, x3)) + + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15(f, g12), Inlines.ADD32(x0, x4)); + x4 = x3; + x3 = x2; + x2 = x1; + x1 = x0; + + } + if (g1 == 0) { + /* OPT: Happens to work without the OPUS_MOVE(), but only because the current encoder already copies x to y */ + if (x_ptr != y_ptr) { + //x.Point(overlap).MemMoveTo(y.Point(overlap), N - overlap); + } + return; + } + + /* Compute the part with the constant filter. */ + comb_filter_const(y, y_ptr + i, x, x_ptr + i, T1, N - i, g10, g11, g12); + } + + private static final byte[][] tf_select_table = { + new byte[]{0, -1, 0, -1, 0, -1, 0, -1}, + new byte[]{0, -1, 0, -2, 1, 0, 1, -1}, + new byte[]{0, -2, 0, -3, 2, 0, 1, -1}, + new byte[]{0, -2, 0, -3, 3, 0, 1, -1},}; + + static void init_caps(CeltMode m, int[] cap, int LM, int C) { + int i; + for (i = 0; i < m.nbEBands; i++) { + int N; + N = (m.eBands[i + 1] - m.eBands[i]) << LM; + cap[i] = (m.cache.caps[m.nbEBands * (2 * LM + C - 1) + i] + 64) * C * N >> 2; + } + } +} diff --git a/app/src/main/java/org/concentus/CeltConstants.java b/app/src/main/java/org/concentus/CeltConstants.java new file mode 100644 index 0000000000..38b3022d67 --- /dev/null +++ b/app/src/main/java/org/concentus/CeltConstants.java @@ -0,0 +1,90 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class CeltConstants { + + public static final int Q15ONE = 32767; + + public static final float CELT_SIG_SCALE = 32768.0f; + + public static final int SIG_SHIFT = 12; + + public static final int NORM_SCALING = 16384; + + public static final int DB_SHIFT = 10; + + public static final int EPSILON = 1; + public static final int VERY_SMALL = 0; + public static final short VERY_LARGE16 = ((short) 32767); + public static final short Q15_ONE = ((short) 32767); + + public static final int COMBFILTER_MAXPERIOD = 1024; + public static final int COMBFILTER_MINPERIOD = 15; + + // from opus_decode.c + public static final int DECODE_BUFFER_SIZE = 2048; + + // from modes.c + /* Alternate tuning (partially derived from Vorbis) */ + public static final int BITALLOC_SIZE = 11; + public static final int MAX_PERIOD = 1024; + + // from static_modes_float.h + public static final int TOTAL_MODES = 1; + + // from rate.h + public static final int MAX_PSEUDO = 40; + public static final int LOG_MAX_PSEUDO = 6; + + public static final int CELT_MAX_PULSES = 128; + + public static final int MAX_FINE_BITS = 8; + + public static final int FINE_OFFSET = 21; + public static final int QTHETA_OFFSET = 4; + public static final int QTHETA_OFFSET_TWOPHASE = 16; + + /* The maximum pitch lag to allow in the pitch-based PLC. It's possible to save +CPU time in the PLC pitch search by making this smaller than MAX_PERIOD. The +current value corresponds to a pitch of 66.67 Hz. */ + public static final int PLC_PITCH_LAG_MAX = 720; + + /* The minimum pitch lag to allow in the pitch-based PLC. This corresponds to a + pitch of 480 Hz. */ + public static final int PLC_PITCH_LAG_MIN = 100; + + public static final int LPC_ORDER = 24; +} diff --git a/app/src/main/java/org/concentus/CeltDecoder.java b/app/src/main/java/org/concentus/CeltDecoder.java new file mode 100644 index 0000000000..e6dad5f7f3 --- /dev/null +++ b/app/src/main/java/org/concentus/CeltDecoder.java @@ -0,0 +1,830 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Decoder state +/// +class CeltDecoder { + + CeltMode mode = null; + int overlap = 0; + int channels = 0; + int stream_channels = 0; + + int downsample = 0; + int start = 0; + int end = 0; + int signalling = 0; + + /* Everything beyond this point gets cleared on a reset */ + int rng = 0; + int error = 0; + int last_pitch_index = 0; + int loss_count = 0; + int postfilter_period = 0; + int postfilter_period_old = 0; + int postfilter_gain = 0; + int postfilter_gain_old = 0; + int postfilter_tapset = 0; + int postfilter_tapset_old = 0; + + final int[] preemph_memD = new int[2]; + + /// + /// Scratch space used by the decoder. It is actually a variable-sized + /// field that resulted in a variable-sized struct. There are 6 distinct regions inside. + /// I have laid them out into separate variables here, + /// but these were the original definitions: + /// val32 decode_mem[], Size = channels*(DECODE_BUFFER_SIZE+mode.overlap) + /// val16 lpc[], Size = channels*LPC_ORDER + /// val16 oldEBands[], Size = 2*mode.nbEBands + /// val16 oldLogE[], Size = 2*mode.nbEBands + /// val16 oldLogE2[], Size = 2*mode.nbEBands + /// val16 backgroundLogE[], Size = 2*mode.nbEBands + /// + int[][] decode_mem = null; + int[][] lpc = null; // Porting note: Split two-part array into separate arrays (one per channel) + int[] oldEBands = null; + int[] oldLogE = null; + int[] oldLogE2 = null; + int[] backgroundLogE = null; + + private void Reset() { + mode = null; + overlap = 0; + channels = 0; + stream_channels = 0; + downsample = 0; + start = 0; + end = 0; + signalling = 0; + PartialReset(); + } + + private void PartialReset() { + rng = 0; + error = 0; + last_pitch_index = 0; + loss_count = 0; + postfilter_period = 0; + postfilter_period_old = 0; + postfilter_gain = 0; + postfilter_gain_old = 0; + postfilter_tapset = 0; + postfilter_tapset_old = 0; + Arrays.MemSet(preemph_memD, 0, 2); + decode_mem = null; + lpc = null; + oldEBands = null; + oldLogE = null; + oldLogE2 = null; + backgroundLogE = null; + } + + void ResetState() { + int i; + + this.PartialReset(); + + // We have to reconstitute the dynamic buffers here. fixme: this could be better implemented + this.decode_mem = new int[this.channels][]; + this.lpc = new int[this.channels][]; + for (int c = 0; c < this.channels; c++) { + this.decode_mem[c] = new int[CeltConstants.DECODE_BUFFER_SIZE + this.mode.overlap]; + this.lpc[c] = new int[CeltConstants.LPC_ORDER]; + } + this.oldEBands = new int[2 * this.mode.nbEBands]; + this.oldLogE = new int[2 * this.mode.nbEBands]; + this.oldLogE2 = new int[2 * this.mode.nbEBands]; + this.backgroundLogE = new int[2 * this.mode.nbEBands]; + + for (i = 0; i < 2 * this.mode.nbEBands; i++) { + this.oldLogE[i] = this.oldLogE2[i] = -((short) (0.5 + (28.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + } + + int celt_decoder_init(int sampling_rate, int channels) { + int ret; + ret = this.opus_custom_decoder_init(CeltMode.mode48000_960_120, channels); + if (ret != OpusError.OPUS_OK) { + return ret; + } + this.downsample = CeltCommon.resampling_factor(sampling_rate); + if (this.downsample == 0) { + return OpusError.OPUS_BAD_ARG; + } else { + return OpusError.OPUS_OK; + } + } + + private int opus_custom_decoder_init(CeltMode mode, int channels) { + if (channels < 0 || channels > 2) { + return OpusError.OPUS_BAD_ARG; + } + + if (this == null) { + return OpusError.OPUS_ALLOC_FAIL; + } + + this.Reset(); + + this.mode = mode; + this.overlap = mode.overlap; + this.stream_channels = this.channels = channels; + + this.downsample = 1; + this.start = 0; + this.end = this.mode.effEBands; + this.signalling = 1; + + this.loss_count = 0; + + //this.decode_mem = new int[channels * (CeltConstants.DECODE_BUFFER_SIZE + mode.overlap)); + //this.lpc = new int[channels * CeltConstants.LPC_ORDER); + //this.oldEBands = new int[2 * mode.nbEBands); + //this.oldLogE = new int[2 * mode.nbEBands); + //this.oldLogE2 = new int[2 * mode.nbEBands); + //this.backgroundLogE = new int[2 * mode.nbEBands); + this.ResetState(); + + return OpusError.OPUS_OK; + } + + void celt_decode_lost(int N, int LM) { + int c; + int i; + int C = this.channels; + int[][] out_syn = new int[2][]; + int[] out_syn_ptrs = new int[2]; + CeltMode mode; + int nbEBands; + int overlap; + int noise_based; + short[] eBands; + + mode = this.mode; + nbEBands = mode.nbEBands; + overlap = mode.overlap; + eBands = mode.eBands; + + c = 0; + do { + out_syn[c] = this.decode_mem[c]; + out_syn_ptrs[c] = CeltConstants.DECODE_BUFFER_SIZE - N; + } while (++c < C); + + noise_based = (loss_count >= 5 || start != 0) ? 1 : 0; + if (noise_based != 0) { + /* Noise-based PLC/CNG */ + int[][] X; + int seed; + int end; + int effEnd; + int decay; + end = this.end; + effEnd = Inlines.IMAX(start, Inlines.IMIN(end, mode.effEBands)); + + X = Arrays.InitTwoDimensionalArrayInt(C, N); + /** + * < Interleaved normalised MDCTs + */ + + /* Energy decay */ + decay = loss_count == 0 ? ((short) (0.5 + (1.5f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.5f, CeltConstants.DB_SHIFT)*/ : ((short) (0.5 + (0.5f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.5f, CeltConstants.DB_SHIFT)*/; + c = 0; + do { + for (i = start; i < end; i++) { + this.oldEBands[c * nbEBands + i] = Inlines.MAX16(backgroundLogE[c * nbEBands + i], this.oldEBands[c * nbEBands + i] - decay); + } + } while (++c < C); + seed = this.rng; + for (c = 0; c < C; c++) { + for (i = start; i < effEnd; i++) { + int j; + int boffs; + int blen; + boffs = (eBands[i] << LM); + blen = (eBands[i + 1] - eBands[i]) << LM; + for (j = 0; j < blen; j++) { + seed = Bands.celt_lcg_rand(seed); + X[c][boffs + j] = (((int) seed) >> 20); + } + + VQ.renormalise_vector(X[c], 0, blen, CeltConstants.Q15ONE); + } + } + this.rng = seed; + + c = 0; + do { + Arrays.MemMove(this.decode_mem[c], N, 0, CeltConstants.DECODE_BUFFER_SIZE - N + (overlap >> 1)); + } while (++c < C); + + CeltCommon.celt_synthesis(mode, X, out_syn, out_syn_ptrs, this.oldEBands, start, effEnd, C, C, 0, LM, this.downsample, 0); + } else { + /* Pitch-based PLC */ + int[] window; + int fade = CeltConstants.Q15ONE; + int pitch_index; + int[] etmp; + int[] exc; + + if (loss_count == 0) { + this.last_pitch_index = pitch_index = CeltCommon.celt_plc_pitch_search(this.decode_mem, C); + } else { + pitch_index = this.last_pitch_index; + fade = ((short) (0.5 + (.8f) * (((int) 1) << (15))))/*Inlines.QCONST16(.8f, 15)*/; + } + + etmp = new int[overlap]; + exc = new int[CeltConstants.MAX_PERIOD]; + window = mode.window; + c = 0; + do { + int decay; + int attenuation; + int S1 = 0; + int[] buf; + int extrapolation_offset; + int extrapolation_len; + int exc_length; + int j; + + buf = this.decode_mem[c]; + for (i = 0; i < CeltConstants.MAX_PERIOD; i++) { + exc[i] = Inlines.ROUND16(buf[CeltConstants.DECODE_BUFFER_SIZE - CeltConstants.MAX_PERIOD + i], CeltConstants.SIG_SHIFT); + } + + if (loss_count == 0) { + int[] ac = new int[CeltConstants.LPC_ORDER + 1]; + /* Compute LPC coefficients for the last MAX_PERIOD samples before + the first loss so we can work in the excitation-filter domain. */ + Autocorrelation._celt_autocorr(exc, ac, window, overlap, + CeltConstants.LPC_ORDER, CeltConstants.MAX_PERIOD); + /* Add a noise floor of -40 dB. */ + ac[0] += Inlines.SHR32(ac[0], 13); + /* Use lag windowing to stabilize the Levinson-Durbin recursion. */ + for (i = 1; i <= CeltConstants.LPC_ORDER; i++) { + /*ac[i] *= exp(-.5*(2*M_PI*.002*i)*(2*M_PI*.002*i));*/ + ac[i] -= Inlines.MULT16_32_Q15(2 * i * i, ac[i]); + } + CeltLPC.celt_lpc(this.lpc[c], ac, CeltConstants.LPC_ORDER); + } + /* We want the excitation for 2 pitch periods in order to look for a + decaying signal, but we can't get more than MAX_PERIOD. */ + exc_length = Inlines.IMIN(2 * pitch_index, CeltConstants.MAX_PERIOD); + /* Initialize the LPC history with the samples just before the start + of the region for which we're computing the excitation. */ + { + int[] lpc_mem = new int[CeltConstants.LPC_ORDER]; + for (i = 0; i < CeltConstants.LPC_ORDER; i++) { + lpc_mem[i] + = Inlines.ROUND16(buf[CeltConstants.DECODE_BUFFER_SIZE - exc_length - 1 - i], CeltConstants.SIG_SHIFT); + } + + /* Compute the excitation for exc_length samples before the loss. */ + Kernels.celt_fir(exc, (CeltConstants.MAX_PERIOD - exc_length), this.lpc[c], 0, + exc, (CeltConstants.MAX_PERIOD - exc_length), exc_length, CeltConstants.LPC_ORDER, lpc_mem); + } + + /* Check if the waveform is decaying, and if so how fast. + We do this to avoid adding energy when concealing in a segment + with decaying energy. */ + { + int E1 = 1, E2 = 1; + int decay_length; + int shift = Inlines.IMAX(0, 2 * Inlines.celt_zlog2(Inlines.celt_maxabs16(exc, (CeltConstants.MAX_PERIOD - exc_length), exc_length)) - 20); + decay_length = exc_length >> 1; + for (i = 0; i < decay_length; i++) { + int e; + e = exc[CeltConstants.MAX_PERIOD - decay_length + i]; + E1 += Inlines.SHR32(Inlines.MULT16_16(e, e), shift); + e = exc[CeltConstants.MAX_PERIOD - 2 * decay_length + i]; + E2 += Inlines.SHR32(Inlines.MULT16_16(e, e), shift); + } + E1 = Inlines.MIN32(E1, E2); + decay = Inlines.celt_sqrt(Inlines.frac_div32(Inlines.SHR32(E1, 1), E2)); + } + + /* Move the decoder memory one frame to the left to give us room to + add the data for the new frame. We ignore the overlap that extends + past the end of the buffer, because we aren't going to use it. */ + Arrays.MemMove(buf, N, 0, CeltConstants.DECODE_BUFFER_SIZE - N); + + /* Extrapolate from the end of the excitation with a period of + "pitch_index", scaling down each period by an additional factor of + "decay". */ + extrapolation_offset = CeltConstants.MAX_PERIOD - pitch_index; + /* We need to extrapolate enough samples to cover a complete MDCT + window (including overlap/2 samples on both sides). */ + extrapolation_len = N + overlap; + /* We also apply fading if this is not the first loss. */ + attenuation = Inlines.MULT16_16_Q15(fade, decay); + for (i = j = 0; i < extrapolation_len; i++, j++) { + int tmp; + if (j >= pitch_index) { + j -= pitch_index; + attenuation = Inlines.MULT16_16_Q15(attenuation, decay); + } + buf[CeltConstants.DECODE_BUFFER_SIZE - N + i] + = Inlines.SHL32((Inlines.MULT16_16_Q15(attenuation, + exc[extrapolation_offset + j])), CeltConstants.SIG_SHIFT); + /* Compute the energy of the previously decoded signal whose + excitation we're copying. */ + tmp = Inlines.ROUND16( + buf[CeltConstants.DECODE_BUFFER_SIZE - CeltConstants.MAX_PERIOD - N + extrapolation_offset + j], + CeltConstants.SIG_SHIFT); + S1 += Inlines.SHR32(Inlines.MULT16_16(tmp, tmp), 8); + } + + { + int[] lpc_mem = new int[CeltConstants.LPC_ORDER]; + /* Copy the last decoded samples (prior to the overlap region) to + synthesis filter memory so we can have a continuous signal. */ + for (i = 0; i < CeltConstants.LPC_ORDER; i++) { + lpc_mem[i] = Inlines.ROUND16(buf[CeltConstants.DECODE_BUFFER_SIZE - N - 1 - i], CeltConstants.SIG_SHIFT); + } + /* Apply the synthesis filter to convert the excitation back into + the signal domain. */ + CeltLPC.celt_iir(buf, CeltConstants.DECODE_BUFFER_SIZE - N, this.lpc[c], + buf, CeltConstants.DECODE_BUFFER_SIZE - N, extrapolation_len, CeltConstants.LPC_ORDER, + lpc_mem); + } + + /* Check if the synthesis energy is higher than expected, which can + happen with the signal changes during our window. If so, + attenuate. */ + { + int S2 = 0; + for (i = 0; i < extrapolation_len; i++) { + int tmp = Inlines.ROUND16(buf[CeltConstants.DECODE_BUFFER_SIZE - N + i], CeltConstants.SIG_SHIFT); + S2 += Inlines.SHR32(Inlines.MULT16_16(tmp, tmp), 8); + } + /* This checks for an "explosion" in the synthesis. */ + if (!(S1 > Inlines.SHR32(S2, 2))) { + for (i = 0; i < extrapolation_len; i++) { + buf[CeltConstants.DECODE_BUFFER_SIZE - N + i] = 0; + } + } else if (S1 < S2) { + int ratio = Inlines.celt_sqrt(Inlines.frac_div32(Inlines.SHR32(S1, 1) + 1, S2 + 1)); + for (i = 0; i < overlap; i++) { + int tmp_g = CeltConstants.Q15ONE + - Inlines.MULT16_16_Q15(window[i], CeltConstants.Q15ONE - ratio); + buf[CeltConstants.DECODE_BUFFER_SIZE - N + i] + = Inlines.MULT16_32_Q15(tmp_g, buf[CeltConstants.DECODE_BUFFER_SIZE - N + i]); + } + for (i = overlap; i < extrapolation_len; i++) { + buf[CeltConstants.DECODE_BUFFER_SIZE - N + i] + = Inlines.MULT16_32_Q15(ratio, buf[CeltConstants.DECODE_BUFFER_SIZE - N + i]); + } + } + } + + /* Apply the pre-filter to the MDCT overlap for the next frame because + the post-filter will be re-applied in the decoder after the MDCT + overlap. */ + CeltCommon.comb_filter(etmp, 0, buf, CeltConstants.DECODE_BUFFER_SIZE, + this.postfilter_period, this.postfilter_period, overlap, + -this.postfilter_gain, -this.postfilter_gain, + this.postfilter_tapset, this.postfilter_tapset, null, 0); + + /* Simulate TDAC on the concealed audio so that it blends with the + MDCT of the next frame. */ + for (i = 0; i < overlap / 2; i++) { + buf[CeltConstants.DECODE_BUFFER_SIZE + i] + = Inlines.MULT16_32_Q15(window[i], etmp[overlap - 1 - i]) + + Inlines.MULT16_32_Q15(window[overlap - i - 1], etmp[i]); + } + } while (++c < C); + } + + this.loss_count = loss_count + 1; + } + + int celt_decode_with_ec(byte[] data, int data_ptr, + int len, short[] pcm, int pcm_ptr, int frame_size, EntropyCoder dec, int accum) { + int c, i, N; + int spread_decision; + int bits; + int[][] X; + int[] fine_quant; + int[] pulses; + int[] cap; + int[] offsets; + int[] fine_priority; + int[] tf_res; + short[] collapse_masks; + int[][] out_syn = new int[2][]; + int[] out_syn_ptrs = new int[2]; + int[] oldBandE, oldLogE, oldLogE2, backgroundLogE; + + int shortBlocks; + int isTransient; + int intra_ener; + int CC = this.channels; + int LM, M; + int start; + int end; + int effEnd; + int codedBands; + int alloc_trim; + int postfilter_pitch; + int postfilter_gain; + int intensity = 0; + int dual_stereo = 0; + int total_bits; + int balance; + int tell; + int dynalloc_logp; + int postfilter_tapset; + int anti_collapse_rsv; + int anti_collapse_on = 0; + int silence; + int C = this.stream_channels; + CeltMode mode; // porting note: pointer + int nbEBands; + int overlap; + short[] eBands; + + mode = this.mode; + nbEBands = mode.nbEBands; + overlap = mode.overlap; + eBands = mode.eBands; + start = this.start; + end = this.end; + frame_size *= this.downsample; + + oldBandE = this.oldEBands; + oldLogE = this.oldLogE; + oldLogE2 = this.oldLogE2; + backgroundLogE = this.backgroundLogE; + + { + for (LM = 0; LM <= mode.maxLM; LM++) { + if (mode.shortMdctSize << LM == frame_size) { + break; + } + } + if (LM > mode.maxLM) { + return OpusError.OPUS_BAD_ARG; + } + } + M = 1 << LM; + + if (len < 0 || len > 1275 || pcm == null) { + return OpusError.OPUS_BAD_ARG; + } + + N = M * mode.shortMdctSize; + c = 0; + do { + out_syn[c] = this.decode_mem[c]; + out_syn_ptrs[c] = CeltConstants.DECODE_BUFFER_SIZE - N; + } while (++c < CC); + + effEnd = end; + if (effEnd > mode.effEBands) { + effEnd = mode.effEBands; + } + + if (data == null || len <= 1) { + this.celt_decode_lost(N, LM); + CeltCommon.deemphasis(out_syn, out_syn_ptrs, pcm, pcm_ptr, N, CC, this.downsample, mode.preemph, this.preemph_memD, accum); + + return frame_size / this.downsample; + } + + if (dec == null) { + // If no entropy decoder was passed into this function, we need to create + // a new one here for local use only. It only exists in this function scope. + dec = new EntropyCoder(); + dec.dec_init(data, data_ptr, len); + } + + if (C == 1) { + for (i = 0; i < nbEBands; i++) { + oldBandE[i] = Inlines.MAX16(oldBandE[i], oldBandE[nbEBands + i]); + } + } + + total_bits = len * 8; + tell = dec.tell(); + + if (tell >= total_bits) { + silence = 1; + } else if (tell == 1) { + silence = dec.dec_bit_logp(15); + } else { + silence = 0; + } + + if (silence != 0) { + /* Pretend we've read all the remaining bits */ + tell = len * 8; + dec.nbits_total += tell - dec.tell(); + } + + postfilter_gain = 0; + postfilter_pitch = 0; + postfilter_tapset = 0; + if (start == 0 && tell + 16 <= total_bits) { + if (dec.dec_bit_logp(1) != 0) { + int qg, octave; + octave = (int) dec.dec_uint(6); + postfilter_pitch = (16 << octave) + dec.dec_bits(4 + octave) - 1; + qg = dec.dec_bits(3); + if (dec.tell() + 2 <= total_bits) { + postfilter_tapset = dec.dec_icdf(CeltTables.tapset_icdf, 2); + } + postfilter_gain = ((short) (0.5 + (.09375f) * ((1) << (15))))/*Inlines.QCONST16(.09375f, 15)*/ * (qg + 1); + } + tell = dec.tell(); + } + + if (LM > 0 && tell + 3 <= total_bits) { + isTransient = dec.dec_bit_logp(3); + tell = dec.tell(); + } else { + isTransient = 0; + } + + if (isTransient != 0) { + shortBlocks = M; + } else { + shortBlocks = 0; + } + + /* Decode the global flags (first symbols in the stream) */ + intra_ener = tell + 3 <= total_bits ? dec.dec_bit_logp(3) : 0; + /* Get band energies */ + QuantizeBands.unquant_coarse_energy(mode, start, end, oldBandE, + intra_ener, dec, C, LM); + + tf_res = new int[nbEBands]; + CeltCommon.tf_decode(start, end, isTransient, tf_res, LM, dec); + + tell = dec.tell(); + spread_decision = Spread.SPREAD_NORMAL; + if (tell + 4 <= total_bits) { + spread_decision = dec.dec_icdf(CeltTables.spread_icdf, 5); + } + + cap = new int[nbEBands]; + + CeltCommon.init_caps(mode, cap, LM, C); + + offsets = new int[nbEBands]; + + dynalloc_logp = 6; + total_bits <<= EntropyCoder.BITRES; + tell = dec.tell_frac(); + for (i = start; i < end; i++) { + int width, quanta; + int dynalloc_loop_logp; + int boost; + width = C * (eBands[i + 1] - eBands[i]) << LM; + /* quanta is 6 bits, but no more than 1 bit/sample + and no less than 1/8 bit/sample */ + quanta = Inlines.IMIN(width << EntropyCoder.BITRES, Inlines.IMAX(6 << EntropyCoder.BITRES, width)); + dynalloc_loop_logp = dynalloc_logp; + boost = 0; + while (tell + (dynalloc_loop_logp << EntropyCoder.BITRES) < total_bits && boost < cap[i]) { + int flag; + flag = dec.dec_bit_logp(dynalloc_loop_logp); + tell = dec.tell_frac(); + if (flag == 0) { + break; + } + boost += quanta; + total_bits -= quanta; + dynalloc_loop_logp = 1; + } + offsets[i] = boost; + /* Making dynalloc more likely */ + if (boost > 0) { + dynalloc_logp = Inlines.IMAX(2, dynalloc_logp - 1); + } + } + + fine_quant = new int[nbEBands]; + alloc_trim = tell + (6 << EntropyCoder.BITRES) <= total_bits + ? dec.dec_icdf(CeltTables.trim_icdf, 7) : 5; + + bits = ((len * 8) << EntropyCoder.BITRES) - dec.tell_frac() - 1; + anti_collapse_rsv = isTransient != 0 && LM >= 2 && bits >= ((LM + 2) << EntropyCoder.BITRES) ? (1 << EntropyCoder.BITRES) : 0; + bits -= anti_collapse_rsv; + + pulses = new int[nbEBands]; + fine_priority = new int[nbEBands]; + + BoxedValueInt boxed_intensity = new BoxedValueInt(intensity); + BoxedValueInt boxed_dual_stereo = new BoxedValueInt(dual_stereo); + BoxedValueInt boxed_balance = new BoxedValueInt(0); + codedBands = Rate.compute_allocation(mode, start, end, offsets, cap, + alloc_trim, boxed_intensity, boxed_dual_stereo, bits, boxed_balance, pulses, + fine_quant, fine_priority, C, LM, dec, 0, 0, 0); + intensity = boxed_intensity.Val; + dual_stereo = boxed_dual_stereo.Val; + balance = boxed_balance.Val; + + QuantizeBands.unquant_fine_energy(mode, start, end, oldBandE, fine_quant, dec, C); + + c = 0; + do { + Arrays.MemMove(decode_mem[c], N, 0, CeltConstants.DECODE_BUFFER_SIZE - N + overlap / 2); + } while (++c < CC); + + /* Decode fixed codebook */ + collapse_masks = new short[C * nbEBands]; + + X = Arrays.InitTwoDimensionalArrayInt(C, N); + /** + * < Interleaved normalised MDCTs + */ + + BoxedValueInt boxed_rng = new BoxedValueInt(this.rng); + Bands.quant_all_bands(0, mode, start, end, X[0], C == 2 ? X[1] : null, collapse_masks, + null, pulses, shortBlocks, spread_decision, dual_stereo, intensity, tf_res, + len * (8 << EntropyCoder.BITRES) - anti_collapse_rsv, balance, dec, LM, codedBands, boxed_rng); + this.rng = boxed_rng.Val; + + if (anti_collapse_rsv > 0) { + anti_collapse_on = dec.dec_bits(1); + } + + QuantizeBands.unquant_energy_finalise(mode, start, end, oldBandE, + fine_quant, fine_priority, len * 8 - dec.tell(), dec, C); + + if (anti_collapse_on != 0) { + Bands.anti_collapse(mode, X, collapse_masks, LM, C, N, + start, end, oldBandE, oldLogE, oldLogE2, pulses, this.rng); + } + + if (silence != 0) { + for (i = 0; i < C * nbEBands; i++) { + oldBandE[i] = -((short) (0.5 + (28.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + } + + CeltCommon.celt_synthesis(mode, X, out_syn, out_syn_ptrs, oldBandE, start, effEnd, + C, CC, isTransient, LM, this.downsample, silence); + + c = 0; + do { + this.postfilter_period = Inlines.IMAX(this.postfilter_period, CeltConstants.COMBFILTER_MINPERIOD); + this.postfilter_period_old = Inlines.IMAX(this.postfilter_period_old, CeltConstants.COMBFILTER_MINPERIOD); + CeltCommon.comb_filter(out_syn[c], out_syn_ptrs[c], out_syn[c], out_syn_ptrs[c], this.postfilter_period_old, this.postfilter_period, mode.shortMdctSize, + this.postfilter_gain_old, this.postfilter_gain, this.postfilter_tapset_old, this.postfilter_tapset, + mode.window, overlap); + if (LM != 0) { + CeltCommon.comb_filter( + out_syn[c], out_syn_ptrs[c] + (mode.shortMdctSize), + out_syn[c], out_syn_ptrs[c] + (mode.shortMdctSize), + this.postfilter_period, postfilter_pitch, N - mode.shortMdctSize, + this.postfilter_gain, postfilter_gain, this.postfilter_tapset, postfilter_tapset, + mode.window, overlap); + } + + } while (++c < CC); + this.postfilter_period_old = this.postfilter_period; + this.postfilter_gain_old = this.postfilter_gain; + this.postfilter_tapset_old = this.postfilter_tapset; + this.postfilter_period = postfilter_pitch; + this.postfilter_gain = postfilter_gain; + this.postfilter_tapset = postfilter_tapset; + if (LM != 0) { + this.postfilter_period_old = this.postfilter_period; + this.postfilter_gain_old = this.postfilter_gain; + this.postfilter_tapset_old = this.postfilter_tapset; + } + + if (C == 1) { + System.arraycopy(oldBandE, 0, oldBandE, nbEBands, nbEBands); + } + + /* In case start or end were to change */ + if (isTransient == 0) { + int max_background_increase; + System.arraycopy(oldLogE, 0, oldLogE2, 0, 2 * nbEBands); + System.arraycopy(oldBandE, 0, oldLogE, 0, 2 * nbEBands); + /* In normal circumstances, we only allow the noise floor to increase by + up to 2.4 dB/second, but when we're in DTX, we allow up to 6 dB + increase for each update.*/ + if (this.loss_count < 10) { + max_background_increase = M * ((short) (0.5 + (0.001f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.001f, CeltConstants.DB_SHIFT)*/; + } else { + max_background_increase = ((short) (0.5 + (1.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/; + } + for (i = 0; i < 2 * nbEBands; i++) { + backgroundLogE[i] = Inlines.MIN16(backgroundLogE[i] + max_background_increase, oldBandE[i]); + } + } else { + for (i = 0; i < 2 * nbEBands; i++) { + oldLogE[i] = Inlines.MIN16(oldLogE[i], oldBandE[i]); + } + } + c = 0; + do { + for (i = 0; i < start; i++) { + oldBandE[c * nbEBands + i] = 0; + oldLogE[c * nbEBands + i] = oldLogE2[c * nbEBands + i] = -((short) (0.5 + (28.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + for (i = end; i < nbEBands; i++) { + oldBandE[c * nbEBands + i] = 0; + oldLogE[c * nbEBands + i] = oldLogE2[c * nbEBands + i] = -((short) (0.5 + (28.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + } while (++c < 2); + this.rng = (int) dec.rng; + + CeltCommon.deemphasis(out_syn, out_syn_ptrs, pcm, pcm_ptr, N, CC, this.downsample, mode.preemph, this.preemph_memD, accum); + this.loss_count = 0; + + if (dec.tell() > 8 * len) { + return OpusError.OPUS_INTERNAL_ERROR; + } + if (dec.get_error() != 0) { + this.error = 1; + } + return frame_size / this.downsample; + } + + void SetStartBand(int value) { + if (value < 0 || value >= this.mode.nbEBands) { + throw new IllegalArgumentException("Start band above max number of ebands (or negative)"); + } + this.start = value; + } + + void SetEndBand(int value) { + if (value < 1 || value > this.mode.nbEBands) { + throw new IllegalArgumentException("End band above max number of ebands (or less than 1)"); + } + this.end = value; + } + + void SetChannels(int value) { + if (value < 1 || value > 2) { + throw new IllegalArgumentException("Channel count must be 1 or 2"); + } + this.stream_channels = value; + } + + int GetAndClearError() { + int returnVal = this.error; + this.error = 0; + return returnVal; + } + + public int GetLookahead() { + return this.overlap / this.downsample; + } + + public int GetPitch() { + return this.postfilter_period; + } + + public CeltMode GetMode() { + return this.mode; + } + + public void SetSignalling(int value) { + this.signalling = value; + } + + public int GetFinalRange() { + return this.rng; + } +} diff --git a/app/src/main/java/org/concentus/CeltEncoder.java b/app/src/main/java/org/concentus/CeltEncoder.java new file mode 100644 index 0000000000..5c06419081 --- /dev/null +++ b/app/src/main/java/org/concentus/CeltEncoder.java @@ -0,0 +1,1254 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class CeltEncoder { + + CeltMode mode = null; + /** + * < Mode used by the encoder. Without custom modes, this always refers to + * the same predefined struct + */ + int channels = 0; + int stream_channels = 0; + + int force_intra = 0; + int clip = 0; + int disable_pf = 0; + int complexity = 0; + int upsample = 0; + int start = 0; + int end = 0; + + int bitrate = 0; + int vbr = 0; + int signalling = 0; + + /* If zero, VBR can do whatever it likes with the rate */ + int constrained_vbr = 0; + int loss_rate = 0; + int lsb_depth = 0; + OpusFramesize variable_duration = OpusFramesize.OPUS_FRAMESIZE_UNKNOWN; + int lfe = 0; + + /* Everything beyond this point gets cleared on a reset */ + int rng = 0; + int spread_decision = 0; + int delayedIntra = 0; + int tonal_average = 0; + int lastCodedBands = 0; + int hf_average = 0; + int tapset_decision = 0; + + int prefilter_period = 0; + int prefilter_gain = 0; + int prefilter_tapset = 0; + int consec_transient = 0; + AnalysisInfo analysis = new AnalysisInfo(); + + final int[] preemph_memE = new int[2]; + final int[] preemph_memD = new int[2]; + + /* VBR-related parameters */ + int vbr_reservoir = 0; + int vbr_drift = 0; + int vbr_offset = 0; + int vbr_count = 0; + int overlap_max = 0; + int stereo_saving = 0; + int intensity = 0; + int[] energy_mask = null; + int spec_avg = 0; + + /// + /// The original C++ defined in_mem as a single float[1] which was the "caboose" + /// to the overall encoder struct, containing 5 separate variable-sized buffer + /// spaces of heterogeneous datatypes. I have laid them out into separate variables here, + /// but these were the original definitions: + /// val32 in_mem[], Size = channels*mode.overlap + /// val32 prefilter_mem[], Size = channels*COMBFILTER_MAXPERIOD + /// val16 oldBandE[], Size = channels*mode.nbEBands + /// val16 oldLogE[], Size = channels*mode.nbEBands + /// val16 oldLogE2[], Size = channels*mode.nbEBands + /// + int[][] in_mem = null; + int[][] prefilter_mem = null; + int[][] oldBandE = null; + int[][] oldLogE = null; + int[][] oldLogE2 = null; + + private void Reset() { + mode = null; + channels = 0; + stream_channels = 0; + force_intra = 0; + clip = 0; + disable_pf = 0; + complexity = 0; + upsample = 0; + start = 0; + end = 0; + bitrate = 0; + vbr = 0; + signalling = 0; + constrained_vbr = 0; + loss_rate = 0; + lsb_depth = 0; + variable_duration = OpusFramesize.OPUS_FRAMESIZE_UNKNOWN; + lfe = 0; + PartialReset(); + } + + private void PartialReset() { + rng = 0; + spread_decision = 0; + delayedIntra = 0; + tonal_average = 0; + lastCodedBands = 0; + hf_average = 0; + tapset_decision = 0; + prefilter_period = 0; + prefilter_gain = 0; + prefilter_tapset = 0; + consec_transient = 0; + analysis.Reset(); + preemph_memE[0] = 0; + preemph_memE[1] = 0; + preemph_memD[0] = 0; + preemph_memD[1] = 0; + vbr_reservoir = 0; + vbr_drift = 0; + vbr_offset = 0; + vbr_count = 0; + overlap_max = 0; + stereo_saving = 0; + intensity = 0; + energy_mask = null; + spec_avg = 0; + in_mem = null; + prefilter_mem = null; + oldBandE = null; + oldLogE = null; + oldLogE2 = null; + } + + void ResetState() { + int i; + + this.PartialReset(); + + // We have to reconstitute the dynamic buffers here. + this.in_mem = Arrays.InitTwoDimensionalArrayInt(this.channels, this.mode.overlap); + this.prefilter_mem = Arrays.InitTwoDimensionalArrayInt(this.channels, CeltConstants.COMBFILTER_MAXPERIOD); + this.oldBandE = Arrays.InitTwoDimensionalArrayInt(this.channels, this.mode.nbEBands); + this.oldLogE = Arrays.InitTwoDimensionalArrayInt(this.channels, this.mode.nbEBands); + this.oldLogE2 = Arrays.InitTwoDimensionalArrayInt(this.channels, this.mode.nbEBands); + + for (i = 0; i < this.mode.nbEBands; i++) { + this.oldLogE[0][i] = this.oldLogE2[0][i] = -((short) (0.5 + (28.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + if (this.channels == 2) { + for (i = 0; i < this.mode.nbEBands; i++) { + this.oldLogE[1][i] = this.oldLogE2[1][i] = -((short) (0.5 + (28.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + } + this.vbr_offset = 0; + this.delayedIntra = 1; + this.spread_decision = Spread.SPREAD_NORMAL; + this.tonal_average = 256; + this.hf_average = 0; + this.tapset_decision = 0; + } + + int opus_custom_encoder_init_arch(CeltMode mode, + int channels) { + if (channels < 0 || channels > 2) { + return OpusError.OPUS_BAD_ARG; + } + + if (this == null || mode == null) { + return OpusError.OPUS_ALLOC_FAIL; + } + + this.Reset(); + + this.mode = mode; + this.stream_channels = this.channels = channels; + + this.upsample = 1; + this.start = 0; + this.end = this.mode.effEBands; + this.signalling = 1; + + this.constrained_vbr = 1; + this.clip = 1; + + this.bitrate = OpusConstants.OPUS_BITRATE_MAX; + this.vbr = 0; + this.force_intra = 0; + this.complexity = 5; + this.lsb_depth = 24; + + //this.in_mem = new int[channels * mode.overlap); + //this.prefilter_mem = new int[channels * CeltConstants.COMBFILTER_MAXPERIOD); + //this.oldBandE = new int[channels * mode.nbEBands); + //this.oldLogE = new int[channels * mode.nbEBands); + //this.oldLogE2 = new int[channels * mode.nbEBands); + this.ResetState(); + + return OpusError.OPUS_OK; + } + + int celt_encoder_init(int sampling_rate, int channels) { + int ret; + ret = this.opus_custom_encoder_init_arch(CeltMode.mode48000_960_120, channels); + if (ret != OpusError.OPUS_OK) { + return ret; + } + this.upsample = CeltCommon.resampling_factor(sampling_rate); + return OpusError.OPUS_OK; + } + + int run_prefilter(int[][] input, int[][] prefilter_mem, int CC, int N, + int prefilter_tapset, BoxedValueInt pitch, BoxedValueInt gain, BoxedValueInt qgain, int enabled, int nbAvailableBytes) { + int c; + int[][] pre = new int[CC][]; + CeltMode mode; // [porting note] pointer + BoxedValueInt pitch_index = new BoxedValueInt(0); + int gain1; + int pf_threshold; + int pf_on; + int qg; + int overlap; + + mode = this.mode; + overlap = mode.overlap; + for (int z = 0; z < CC; z++) { + pre[z] = new int[N + CeltConstants.COMBFILTER_MAXPERIOD]; + } + + c = 0; + do { + System.arraycopy(prefilter_mem[c], 0, pre[c], 0, CeltConstants.COMBFILTER_MAXPERIOD); + System.arraycopy(input[c], overlap, pre[c], CeltConstants.COMBFILTER_MAXPERIOD, N); + } while (++c < CC); + + if (enabled != 0) { + int[] pitch_buf = new int[(CeltConstants.COMBFILTER_MAXPERIOD + N) >> 1]; + + Pitch.pitch_downsample(pre, pitch_buf, CeltConstants.COMBFILTER_MAXPERIOD + N, CC); + /* Don't search for the fir last 1.5 octave of the range because + there's too many false-positives due to short-term correlation */ + Pitch.pitch_search(pitch_buf, CeltConstants.COMBFILTER_MAXPERIOD >> 1, pitch_buf, N, + CeltConstants.COMBFILTER_MAXPERIOD - 3 * CeltConstants.COMBFILTER_MINPERIOD, pitch_index); + pitch_index.Val = CeltConstants.COMBFILTER_MAXPERIOD - pitch_index.Val; + gain1 = Pitch.remove_doubling(pitch_buf, CeltConstants.COMBFILTER_MAXPERIOD, CeltConstants.COMBFILTER_MINPERIOD, + N, pitch_index, this.prefilter_period, this.prefilter_gain); + if (pitch_index.Val > CeltConstants.COMBFILTER_MAXPERIOD - 2) { + pitch_index.Val = CeltConstants.COMBFILTER_MAXPERIOD - 2; + } + gain1 = Inlines.MULT16_16_Q15(((short) (0.5 + (.7f) * (((int) 1) << (15))))/*Inlines.QCONST16(.7f, 15)*/, gain1); + /*printf("%d %d %f %f\n", pitch_change, pitch_index, gain1, st.analysis.tonality);*/ + if (this.loss_rate > 2) { + gain1 = Inlines.HALF32(gain1); + } + if (this.loss_rate > 4) { + gain1 = Inlines.HALF32(gain1); + } + if (this.loss_rate > 8) { + gain1 = 0; + } + } else { + gain1 = 0; + pitch_index.Val = CeltConstants.COMBFILTER_MINPERIOD; + } + + /* Gain threshold for enabling the prefilter/postfilter */ + pf_threshold = ((short) (0.5 + (.2f) * (((int) 1) << (15))))/*Inlines.QCONST16(.2f, 15)*/; + + /* Adjusting the threshold based on rate and continuity */ + if (Inlines.abs(pitch_index.Val - this.prefilter_period) * 10 > pitch_index.Val) { + pf_threshold += ((short) (0.5 + (.2f) * (((int) 1) << (15))))/*Inlines.QCONST16(.2f, 15)*/; + } + if (nbAvailableBytes < 25) { + pf_threshold += ((short) (0.5 + (.1f) * (((int) 1) << (15))))/*Inlines.QCONST16(.1f, 15)*/; + } + if (nbAvailableBytes < 35) { + pf_threshold += ((short) (0.5 + (.1f) * (((int) 1) << (15))))/*Inlines.QCONST16(.1f, 15)*/; + } + if (this.prefilter_gain > ((short) (0.5 + (.4f) * (((int) 1) << (15))))/*Inlines.QCONST16(.4f, 15)*/) { + pf_threshold -= ((short) (0.5 + (.1f) * (((int) 1) << (15))))/*Inlines.QCONST16(.1f, 15)*/; + } + if (this.prefilter_gain > ((short) (0.5 + (.55f) * (((int) 1) << (15))))/*Inlines.QCONST16(.55f, 15)*/) { + pf_threshold -= ((short) (0.5 + (.1f) * (((int) 1) << (15))))/*Inlines.QCONST16(.1f, 15)*/; + } + + /* Hard threshold at 0.2 */ + pf_threshold = Inlines.MAX16(pf_threshold, ((short) (0.5 + (.2f) * (((int) 1) << (15))))/*Inlines.QCONST16(.2f, 15)*/); + + if (gain1 < pf_threshold) { + gain1 = 0; + pf_on = 0; + qg = 0; + } else { + /*This block is not gated by a total bits check only because + of the nbAvailableBytes check above.*/ + if (Inlines.ABS32(gain1 - this.prefilter_gain) < ((short) (0.5 + (.1f) * (((int) 1) << (15))))/*Inlines.QCONST16(.1f, 15)*/) { + gain1 = this.prefilter_gain; + } + + qg = ((gain1 + 1536) >> 10) / 3 - 1; + qg = Inlines.IMAX(0, Inlines.IMIN(7, qg)); + gain1 = ((short) (0.5 + (0.09375f) * (((int) 1) << (15))))/*Inlines.QCONST16(0.09375f, 15)*/ * (qg + 1); + pf_on = 1; + } + /*printf("%d %f\n", pitch_index, gain1);*/ + + c = 0; + do { + int offset = mode.shortMdctSize - overlap; + this.prefilter_period = Inlines.IMAX(this.prefilter_period, CeltConstants.COMBFILTER_MINPERIOD); + System.arraycopy(this.in_mem[c], 0, input[c], 0, overlap); + if (offset != 0) { + CeltCommon.comb_filter(input[c], (overlap), pre[c], (CeltConstants.COMBFILTER_MAXPERIOD), + this.prefilter_period, this.prefilter_period, offset, -this.prefilter_gain, -this.prefilter_gain, + this.prefilter_tapset, this.prefilter_tapset, null, 0); // opt: lots of pointer allocations here + } + + CeltCommon.comb_filter(input[c], (overlap + offset), pre[c], (CeltConstants.COMBFILTER_MAXPERIOD + offset), + this.prefilter_period, pitch_index.Val, N - offset, -this.prefilter_gain, -gain1, + this.prefilter_tapset, prefilter_tapset, mode.window, overlap); + System.arraycopy(input[c], N, this.in_mem[c], 0, overlap); + + if (N > CeltConstants.COMBFILTER_MAXPERIOD) { + System.arraycopy(pre[c], N, prefilter_mem[c], 0, CeltConstants.COMBFILTER_MAXPERIOD); + } else { + Arrays.MemMove(prefilter_mem[c], N, 0, CeltConstants.COMBFILTER_MAXPERIOD - N); + System.arraycopy(pre[c], CeltConstants.COMBFILTER_MAXPERIOD, prefilter_mem[c], CeltConstants.COMBFILTER_MAXPERIOD - N, N); + } + } while (++c < CC); + + gain.Val = gain1; + pitch.Val = pitch_index.Val; + qgain.Val = qg; + return pf_on; + } + + int celt_encode_with_ec(short[] pcm, int pcm_ptr, int frame_size, byte[] compressed, int compressed_ptr, int nbCompressedBytes, EntropyCoder enc) { + int i, c, N; + int bits; + int[][] input; + int[][] freq; + int[][] X; + int[][] bandE; + int[][] bandLogE; + int[][] bandLogE2; + int[] fine_quant; + int[][] error; + int[] pulses; + int[] cap; + int[] offsets; + int[] fine_priority; + int[] tf_res; + short[] collapse_masks; + int shortBlocks = 0; + int isTransient = 0; + int CC = this.channels; + int C = this.stream_channels; + int LM, M; + int tf_select; + int nbFilledBytes, nbAvailableBytes; + int start; + int end; + int effEnd; + int codedBands; + int tf_sum; + int alloc_trim; + int pitch_index = CeltConstants.COMBFILTER_MINPERIOD; + int gain1 = 0; + int dual_stereo = 0; + int effectiveBytes; + int dynalloc_logp; + int vbr_rate; + int total_bits; + int total_boost; + int balance; + int tell; + int prefilter_tapset = 0; + int pf_on; + int anti_collapse_rsv; + int anti_collapse_on = 0; + int silence = 0; + int tf_chan = 0; + int tf_estimate; + int pitch_change = 0; + int tot_boost; + int sample_max; + int maxDepth; + CeltMode mode; + int nbEBands; + int overlap; + short[] eBands; + int secondMdct; + int signalBandwidth; + int transient_got_disabled = 0; + int surround_masking = 0; + int temporal_vbr = 0; + int surround_trim = 0; + int equiv_rate = 510000; + int[] surround_dynalloc; + + mode = this.mode; + nbEBands = mode.nbEBands; + overlap = mode.overlap; + eBands = mode.eBands; + start = this.start; + end = this.end; + tf_estimate = 0; + if (nbCompressedBytes < 2 || pcm == null) { + return OpusError.OPUS_BAD_ARG; + } + + frame_size *= this.upsample; + for (LM = 0; LM <= mode.maxLM; LM++) { + if (mode.shortMdctSize << LM == frame_size) { + break; + } + } + if (LM > mode.maxLM) { + return OpusError.OPUS_BAD_ARG; + } + M = 1 << LM; + N = M * mode.shortMdctSize; + + if (enc == null) { + tell = 1; + nbFilledBytes = 0; + } else { + tell = enc.tell(); + nbFilledBytes = (tell + 4) >> 3; + } + + Inlines.OpusAssert(this.signalling == 0); + + /* Can't produce more than 1275 output bytes */ + nbCompressedBytes = Inlines.IMIN(nbCompressedBytes, 1275); + nbAvailableBytes = nbCompressedBytes - nbFilledBytes; + + if (this.vbr != 0 && this.bitrate != OpusConstants.OPUS_BITRATE_MAX) { + int den = mode.Fs >> EntropyCoder.BITRES; + vbr_rate = (this.bitrate * frame_size + (den >> 1)) / den; + effectiveBytes = vbr_rate >> (3 + EntropyCoder.BITRES); + } else { + int tmp; + vbr_rate = 0; + tmp = this.bitrate * frame_size; + if (tell > 1) { + tmp += tell; + } + if (this.bitrate != OpusConstants.OPUS_BITRATE_MAX) { + nbCompressedBytes = Inlines.IMAX(2, Inlines.IMIN(nbCompressedBytes, + (tmp + 4 * mode.Fs) / (8 * mode.Fs) - (this.signalling != 0 ? 1 : 0))); + } + effectiveBytes = nbCompressedBytes; + } + if (this.bitrate != OpusConstants.OPUS_BITRATE_MAX) { + equiv_rate = this.bitrate - (40 * C + 20) * ((400 >> LM) - 50); + } + + if (enc == null) { + enc = new EntropyCoder(); + enc.enc_init(compressed, compressed_ptr, nbCompressedBytes); + } + + if (vbr_rate > 0) { + /* Computes the max bit-rate allowed in VBR mode to avoid violating the + target rate and buffering. + We must do this up front so that bust-prevention logic triggers + correctly if we don't have enough bits. */ + if (this.constrained_vbr != 0) { + int vbr_bound; + int max_allowed; + /* We could use any multiple of vbr_rate as bound (depending on the + delay). + This is clamped to ensure we use at least two bytes if the encoder + was entirely empty, but to allow 0 in hybrid mode. */ + vbr_bound = vbr_rate; + max_allowed = Inlines.IMIN(Inlines.IMAX(tell == 1 ? 2 : 0, + (vbr_rate + vbr_bound - this.vbr_reservoir) >> (EntropyCoder.BITRES + 3)), + nbAvailableBytes); + if (max_allowed < nbAvailableBytes) { + nbCompressedBytes = nbFilledBytes + max_allowed; + nbAvailableBytes = max_allowed; + enc.enc_shrink(nbCompressedBytes); + } + } + } + total_bits = nbCompressedBytes * 8; + + effEnd = end; + if (effEnd > mode.effEBands) { + effEnd = mode.effEBands; + } + + input = Arrays.InitTwoDimensionalArrayInt(CC, N + overlap); + + sample_max = Inlines.MAX32(this.overlap_max, Inlines.celt_maxabs32(pcm, pcm_ptr, C * (N - overlap) / this.upsample)); + this.overlap_max = Inlines.celt_maxabs32(pcm, pcm_ptr + (C * (N - overlap) / this.upsample), C * overlap / this.upsample); + sample_max = Inlines.MAX32(sample_max, this.overlap_max); + silence = (sample_max == 0) ? 1 : 0; + if (tell == 1) { + enc.enc_bit_logp(silence, 15); + } else { + silence = 0; + } + if (silence != 0) { + /*In VBR mode there is no need to send more than the minimum. */ + if (vbr_rate > 0) { + effectiveBytes = nbCompressedBytes = Inlines.IMIN(nbCompressedBytes, nbFilledBytes + 2); + total_bits = nbCompressedBytes * 8; + nbAvailableBytes = 2; + enc.enc_shrink(nbCompressedBytes); + } + /* Pretend we've filled all the remaining bits with zeros + (that's what the initialiser did anyway) */ + tell = nbCompressedBytes * 8; + enc.nbits_total += tell - enc.tell(); + } + c = 0; + BoxedValueInt boxed_memE = new BoxedValueInt(0); + do { + int need_clip = 0; + boxed_memE.Val = this.preemph_memE[c]; + CeltCommon.celt_preemphasis(pcm, pcm_ptr + c, input[c], overlap, N, CC, this.upsample, + mode.preemph, boxed_memE, need_clip); + this.preemph_memE[c] = boxed_memE.Val; + } while (++c < CC); + + /* Find pitch period and gain */ + { + int enabled; + int qg; + enabled = (((this.lfe != 0 && nbAvailableBytes > 3) || nbAvailableBytes > 12 * C) && start == 0 && silence == 0 && this.disable_pf == 0 + && this.complexity >= 5 && !(this.consec_transient != 0 && LM != 3 && this.variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE)) ? 1 : 0; + + prefilter_tapset = this.tapset_decision; + BoxedValueInt boxed_pitch_index = new BoxedValueInt(0); + BoxedValueInt boxed_gain1 = new BoxedValueInt(0); + BoxedValueInt boxed_qg = new BoxedValueInt(0); + pf_on = this.run_prefilter(input, this.prefilter_mem, CC, N, prefilter_tapset, boxed_pitch_index, boxed_gain1, boxed_qg, enabled, nbAvailableBytes); + pitch_index = boxed_pitch_index.Val; + gain1 = boxed_gain1.Val; + qg = boxed_qg.Val; + + if ((gain1 > ((short) (0.5 + (.4f) * (((int) 1) << (15))))/*Inlines.QCONST16(.4f, 15)*/ || this.prefilter_gain > ((short) (0.5 + (.4f) * (((int) 1) << (15))))/*Inlines.QCONST16(.4f, 15)*/) && (this.analysis.valid == 0 || this.analysis.tonality > .3) + && (pitch_index > 1.26 * this.prefilter_period || pitch_index < .79 * this.prefilter_period)) { + pitch_change = 1; + } + if (pf_on == 0) { + if (start == 0 && tell + 16 <= total_bits) { + enc.enc_bit_logp(0, 1); + } + } else { + /*This block is not gated by a total bits check only because + of the nbAvailableBytes check above.*/ + int octave; + enc.enc_bit_logp(1, 1); + pitch_index += 1; + octave = Inlines.EC_ILOG(pitch_index) - 5; + enc.enc_uint(octave, 6); + enc.enc_bits((pitch_index - (16 << octave)), 4 + octave); + pitch_index -= 1; + enc.enc_bits(qg, 3); + enc.enc_icdf(prefilter_tapset, CeltTables.tapset_icdf, 2); + } + } + + isTransient = 0; + shortBlocks = 0; + if (this.complexity >= 1 && this.lfe == 0) { + BoxedValueInt boxed_tf_estimate = new BoxedValueInt(0); + BoxedValueInt boxed_tf_chan = new BoxedValueInt(0); + isTransient = CeltCommon.transient_analysis(input, N + overlap, CC, + boxed_tf_estimate, boxed_tf_chan); + tf_estimate = boxed_tf_estimate.Val; + tf_chan = boxed_tf_chan.Val; + } + + if (LM > 0 && enc.tell() + 3 <= total_bits) { + if (isTransient != 0) { + shortBlocks = M; + } + } else { + isTransient = 0; + transient_got_disabled = 1; + } + + freq = Arrays.InitTwoDimensionalArrayInt(CC, N); + /** + * < Interleaved signal MDCTs + */ + bandE = Arrays.InitTwoDimensionalArrayInt(CC, nbEBands); + bandLogE = Arrays.InitTwoDimensionalArrayInt(CC, nbEBands); + + secondMdct = (shortBlocks != 0 && this.complexity >= 8) ? 1 : 0; + bandLogE2 = Arrays.InitTwoDimensionalArrayInt(CC, nbEBands); + //Arrays.MemSet(bandLogE2, 0, C * nbEBands); // not explicitly needed + if (secondMdct != 0) { + CeltCommon.compute_mdcts(mode, 0, input, freq, C, CC, LM, this.upsample); + Bands.compute_band_energies(mode, freq, bandE, effEnd, C, LM); + QuantizeBands.amp2Log2(mode, effEnd, end, bandE, bandLogE2, C); + for (i = 0; i < nbEBands; i++) { + bandLogE2[0][i] += Inlines.HALF16(Inlines.SHL16(LM, CeltConstants.DB_SHIFT)); + } + if (C == 2) { + for (i = 0; i < nbEBands; i++) { + bandLogE2[1][i] += Inlines.HALF16(Inlines.SHL16(LM, CeltConstants.DB_SHIFT)); + } + } + } + + CeltCommon.compute_mdcts(mode, shortBlocks, input, freq, C, CC, LM, this.upsample); + if (CC == 2 && C == 1) { + tf_chan = 0; + } + Bands.compute_band_energies(mode, freq, bandE, effEnd, C, LM); + + if (this.lfe != 0) { + for (i = 2; i < end; i++) { + bandE[0][i] = Inlines.IMIN(bandE[0][i], Inlines.MULT16_32_Q15(((short) (0.5 + (1e-4f) * (((int) 1) << (15))))/*Inlines.QCONST16(1e-4f, 15)*/, bandE[0][0])); + bandE[0][i] = Inlines.MAX32(bandE[0][i], CeltConstants.EPSILON); + } + } + + QuantizeBands.amp2Log2(mode, effEnd, end, bandE, bandLogE, C); + + surround_dynalloc = new int[C * nbEBands]; + //Arrays.MemSet(surround_dynalloc, 0, end); // not strictly needed + /* This computes how much masking takes place between surround channels */ + if (start == 0 && this.energy_mask != null && this.lfe == 0) { + int mask_end; + int midband; + int count_dynalloc; + int mask_avg = 0; + int diff = 0; + int count = 0; + mask_end = Inlines.IMAX(2, this.lastCodedBands); + for (c = 0; c < C; c++) { + for (i = 0; i < mask_end; i++) { + int mask; + mask = Inlines.MAX16(Inlines.MIN16(this.energy_mask[nbEBands * c + i], + ((short) (0.5 + (.25f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.25f, CeltConstants.DB_SHIFT)*/), -((short) (0.5 + (2.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(2.0f, CeltConstants.DB_SHIFT)*/); + if (mask > 0) { + mask = Inlines.HALF16(mask); + } + mask_avg += Inlines.MULT16_16(mask, eBands[i + 1] - eBands[i]); + count += eBands[i + 1] - eBands[i]; + diff += Inlines.MULT16_16(mask, 1 + 2 * i - mask_end); + } + } + Inlines.OpusAssert(count > 0); + mask_avg = Inlines.DIV32_16(mask_avg, count); + mask_avg += ((short) (0.5 + (.2f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.2f, CeltConstants.DB_SHIFT)*/; + diff = diff * 6 / (C * (mask_end - 1) * (mask_end + 1) * mask_end); + /* Again, being conservative */ + diff = Inlines.HALF32(diff); + diff = Inlines.MAX32(Inlines.MIN32(diff, ((int) (0.5 + (.031f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST32(.031f, CeltConstants.DB_SHIFT)*/), 0 - ((int) (0.5 + (.031f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST32(.031f, CeltConstants.DB_SHIFT)*/); + /* Find the band that's in the middle of the coded spectrum */ + for (midband = 0; eBands[midband + 1] < eBands[mask_end] / 2; midband++) ; + count_dynalloc = 0; + for (i = 0; i < mask_end; i++) { + int lin; + int unmask; + lin = mask_avg + diff * (i - midband); + if (C == 2) { + unmask = Inlines.MAX16(this.energy_mask[i], this.energy_mask[nbEBands + i]); + } else { + unmask = this.energy_mask[i]; + } + unmask = Inlines.MIN16(unmask, ((short) (0.5 + (.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.0f, CeltConstants.DB_SHIFT)*/); + unmask -= lin; + if (unmask > ((short) (0.5 + (.25f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.25f, CeltConstants.DB_SHIFT)*/) { + surround_dynalloc[i] = unmask - ((short) (0.5 + (.25f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.25f, CeltConstants.DB_SHIFT)*/; + count_dynalloc++; + } + } + if (count_dynalloc >= 3) { + /* If we need dynalloc in many bands, it's probably because our + initial masking rate was too low. */ + mask_avg += ((short) (0.5 + (.25f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.25f, CeltConstants.DB_SHIFT)*/; + if (mask_avg > 0) { + /* Something went really wrong in the original calculations, + disabling masking. */ + mask_avg = 0; + diff = 0; + Arrays.MemSet(surround_dynalloc, 0, mask_end); + } else { + for (i = 0; i < mask_end; i++) { + surround_dynalloc[i] = Inlines.MAX16(0, surround_dynalloc[i] - ((short) (0.5 + (.25f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.25f, CeltConstants.DB_SHIFT)*/); + } + } + } + mask_avg += ((short) (0.5 + (.2f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.2f, CeltConstants.DB_SHIFT)*/; + /* Convert to 1/64th units used for the trim */ + surround_trim = 64 * diff; + /*printf("%d %d ", mask_avg, surround_trim);*/ + surround_masking = mask_avg; + } + /* Temporal VBR (but not for LFE) */ + if (this.lfe == 0) { + int follow = -((short) (0.5 + (10.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(10.0f, CeltConstants.DB_SHIFT)*/; + int frame_avg = 0; + int offset = shortBlocks != 0 ? Inlines.HALF16(Inlines.SHL16(LM, CeltConstants.DB_SHIFT)) : 0; + for (i = start; i < end; i++) { + follow = Inlines.MAX16(follow - ((short) (0.5 + (1.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/, bandLogE[0][i] - offset); + if (C == 2) { + follow = Inlines.MAX16(follow, bandLogE[1][i] - offset); + } + frame_avg += follow; + } + frame_avg /= (end - start); + temporal_vbr = Inlines.SUB16(frame_avg, this.spec_avg); + temporal_vbr = Inlines.MIN16(((short) (0.5 + (3.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(3.0f, CeltConstants.DB_SHIFT)*/, Inlines.MAX16(-((short) (0.5 + (1.5f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.5f, CeltConstants.DB_SHIFT)*/, temporal_vbr)); + this.spec_avg += (short) (Inlines.MULT16_16_Q15(((short) (0.5 + (.02f) * (((int) 1) << (15))))/*Inlines.QCONST16(.02f, 15)*/, temporal_vbr)); + } + /*for (i=0;i<21;i++) + printf("%f ", bandLogE[i]); + printf("\n");*/ + + if (secondMdct == 0) { + System.arraycopy(bandLogE[0], 0, bandLogE2[0], 0, nbEBands); + if (C == 2) { + System.arraycopy(bandLogE[1], 0, bandLogE2[1], 0, nbEBands); + } + } + + /* Last chance to catch any transient we might have missed in the + time-domain analysis */ + if (LM > 0 && enc.tell() + 3 <= total_bits && isTransient == 0 && this.complexity >= 5 && this.lfe == 0) { + if (CeltCommon.patch_transient_decision(bandLogE, this.oldBandE, nbEBands, start, end, C) != 0) { + isTransient = 1; + shortBlocks = M; + CeltCommon.compute_mdcts(mode, shortBlocks, input, freq, C, CC, LM, this.upsample); + Bands.compute_band_energies(mode, freq, bandE, effEnd, C, LM); + QuantizeBands.amp2Log2(mode, effEnd, end, bandE, bandLogE, C); + /* Compensate for the scaling of short vs long mdcts */ + for (i = 0; i < nbEBands; i++) { + bandLogE2[0][i] += Inlines.HALF16(Inlines.SHL16(LM, CeltConstants.DB_SHIFT)); + } + if (C == 2) { + for (i = 0; i < nbEBands; i++) { + bandLogE2[1][i] += Inlines.HALF16(Inlines.SHL16(LM, CeltConstants.DB_SHIFT)); + } + } + tf_estimate = ((short) (0.5 + (.2f) * (((int) 1) << (14))))/*Inlines.QCONST16(.2f, 14)*/; + } + } + + if (LM > 0 && enc.tell() + 3 <= total_bits) { + enc.enc_bit_logp(isTransient, 3); + } + + X = Arrays.InitTwoDimensionalArrayInt(C, N); + /** + * < Interleaved normalised MDCTs + */ + + /* Band normalisation */ + Bands.normalise_bands(mode, freq, X, bandE, effEnd, C, M); + + tf_res = new int[nbEBands]; + /* Disable variable tf resolution for hybrid and at very low bitrate */ + if (effectiveBytes >= 15 * C && start == 0 && this.complexity >= 2 && this.lfe == 0) { + int lambda; + if (effectiveBytes < 40) { + lambda = 12; + } else if (effectiveBytes < 60) { + lambda = 6; + } else if (effectiveBytes < 100) { + lambda = 4; + } else { + lambda = 3; + } + lambda *= 2; + BoxedValueInt boxed_tf_sum = new BoxedValueInt(0); + tf_select = CeltCommon.tf_analysis(mode, effEnd, isTransient, tf_res, lambda, X, N, LM, boxed_tf_sum, tf_estimate, tf_chan); + tf_sum = boxed_tf_sum.Val; + + for (i = effEnd; i < end; i++) { + tf_res[i] = tf_res[effEnd - 1]; + } + } else { + tf_sum = 0; + for (i = 0; i < end; i++) { + tf_res[i] = isTransient; + } + tf_select = 0; + } + + error = Arrays.InitTwoDimensionalArrayInt(C, nbEBands); + BoxedValueInt boxed_delayedintra = new BoxedValueInt(this.delayedIntra); + QuantizeBands.quant_coarse_energy(mode, start, end, effEnd, bandLogE, + this.oldBandE, total_bits, error, enc, + C, LM, nbAvailableBytes, this.force_intra, + boxed_delayedintra, this.complexity >= 4 ? 1 : 0, this.loss_rate, this.lfe); + this.delayedIntra = boxed_delayedintra.Val; + + CeltCommon.tf_encode(start, end, isTransient, tf_res, LM, tf_select, enc); + + if (enc.tell() + 4 <= total_bits) { + if (this.lfe != 0) { + this.tapset_decision = 0; + this.spread_decision = Spread.SPREAD_NORMAL; + } else if (shortBlocks != 0 || this.complexity < 3 || nbAvailableBytes < 10 * C || start != 0) { + if (this.complexity == 0) { + this.spread_decision = Spread.SPREAD_NONE; + } else { + this.spread_decision = Spread.SPREAD_NORMAL; + } + } else { + BoxedValueInt boxed_tonal_average = new BoxedValueInt(this.tonal_average); + BoxedValueInt boxed_tapset_decision = new BoxedValueInt(this.tapset_decision); + BoxedValueInt boxed_hf_average = new BoxedValueInt(this.hf_average); + this.spread_decision = Bands.spreading_decision(mode, X, + boxed_tonal_average, this.spread_decision, boxed_hf_average, + boxed_tapset_decision, (pf_on != 0 && shortBlocks == 0) ? 1 : 0, effEnd, C, M); + this.tonal_average = boxed_tonal_average.Val; + this.tapset_decision = boxed_tapset_decision.Val; + this.hf_average = boxed_hf_average.Val; + + /*printf("%d %d\n", st.tapset_decision, st.spread_decision);*/ + /*printf("%f %d %f %d\n\n", st.analysis.tonality, st.spread_decision, st.analysis.tonality_slope, st.tapset_decision);*/ + } + enc.enc_icdf(this.spread_decision, CeltTables.spread_icdf, 5); + } + + offsets = new int[nbEBands]; + + BoxedValueInt boxed_tot_boost = new BoxedValueInt(0); + maxDepth = CeltCommon.dynalloc_analysis(bandLogE, bandLogE2, nbEBands, start, end, C, offsets, + this.lsb_depth, mode.logN, isTransient, this.vbr, this.constrained_vbr, + eBands, LM, effectiveBytes, boxed_tot_boost, this.lfe, surround_dynalloc); + tot_boost = boxed_tot_boost.Val; + + /* For LFE, everything interesting is in the first band */ + if (this.lfe != 0) { + offsets[0] = Inlines.IMIN(8, effectiveBytes / 3); + } + cap = new int[nbEBands]; + CeltCommon.init_caps(mode, cap, LM, C); + + dynalloc_logp = 6; + total_bits <<= EntropyCoder.BITRES; + total_boost = 0; + tell = (int) enc.tell_frac(); + for (i = start; i < end; i++) { + int width, quanta; + int dynalloc_loop_logp; + int boost; + int j; + width = C * (eBands[i + 1] - eBands[i]) << LM; + /* quanta is 6 bits, but no more than 1 bit/sample + and no less than 1/8 bit/sample */ + quanta = Inlines.IMIN(width << EntropyCoder.BITRES, Inlines.IMAX(6 << EntropyCoder.BITRES, width)); + dynalloc_loop_logp = dynalloc_logp; + boost = 0; + for (j = 0; tell + (dynalloc_loop_logp << EntropyCoder.BITRES) < total_bits - total_boost + && boost < cap[i]; j++) { + int flag; + flag = j < offsets[i] ? 1 : 0; + enc.enc_bit_logp(flag, dynalloc_loop_logp); + tell = (int) enc.tell_frac(); + if (flag == 0) { + break; + } + boost += quanta; + total_boost += quanta; + dynalloc_loop_logp = 1; + } + /* Making dynalloc more likely */ + if (j != 0) { + dynalloc_logp = Inlines.IMAX(2, dynalloc_logp - 1); + } + offsets[i] = boost; + } + + if (C == 2) { + /* Always use MS for 2.5 ms frames until we can do a better analysis */ + if (LM != 0) { + dual_stereo = CeltCommon.stereo_analysis(mode, X, LM); + } + + this.intensity = Bands.hysteresis_decision((int) (equiv_rate / 1000), + CeltTables.intensity_thresholds, CeltTables.intensity_histeresis, 21, this.intensity); + this.intensity = Inlines.IMIN(end, Inlines.IMAX(start, this.intensity)); + } + + alloc_trim = 5; + if (tell + (6 << EntropyCoder.BITRES) <= total_bits - total_boost) { + if (this.lfe != 0) { + alloc_trim = 5; + } else { + BoxedValueInt boxed_stereo_saving = new BoxedValueInt(this.stereo_saving); + alloc_trim = CeltCommon.alloc_trim_analysis(mode, X, bandLogE, + end, LM, C, this.analysis, boxed_stereo_saving, tf_estimate, + this.intensity, surround_trim); + this.stereo_saving = boxed_stereo_saving.Val; + } + enc.enc_icdf(alloc_trim, CeltTables.trim_icdf, 7); + tell = (int) enc.tell_frac(); + } + + /* Variable bitrate */ + if (vbr_rate > 0) { + int alpha; + int delta; + /* The target rate in 8th bits per frame */ + int target, base_target; + int min_allowed; + int lm_diff = mode.maxLM - LM; + + /* Don't attempt to use more than 510 kb/s, even for frames smaller than 20 ms. + The CELT allocator will just not be able to use more than that anyway. */ + nbCompressedBytes = Inlines.IMIN(nbCompressedBytes, 1275 >> (3 - LM)); + base_target = vbr_rate - ((40 * C + 20) << EntropyCoder.BITRES); + + if (this.constrained_vbr != 0) { + base_target += (this.vbr_offset >> lm_diff); + } + + target = CeltCommon.compute_vbr(mode, this.analysis, base_target, LM, equiv_rate, + this.lastCodedBands, C, this.intensity, this.constrained_vbr, + this.stereo_saving, tot_boost, tf_estimate, pitch_change, maxDepth, + this.variable_duration, this.lfe, this.energy_mask != null ? 1 : 0, surround_masking, + temporal_vbr); + + /* The current offset is removed from the target and the space used + so far is added*/ + target = target + tell; + /* In VBR mode the frame size must not be reduced so much that it would + result in the encoder running out of bits. + The margin of 2 bytes ensures that none of the bust-prevention logic + in the decoder will have triggered so far. */ + min_allowed = ((tell + total_boost + (1 << (EntropyCoder.BITRES + 3)) - 1) >> (EntropyCoder.BITRES + 3)) + 2 - nbFilledBytes; + + nbAvailableBytes = (target + (1 << (EntropyCoder.BITRES + 2))) >> (EntropyCoder.BITRES + 3); + nbAvailableBytes = Inlines.IMAX(min_allowed, nbAvailableBytes); + nbAvailableBytes = Inlines.IMIN(nbCompressedBytes, nbAvailableBytes + nbFilledBytes) - nbFilledBytes; + + /* By how much did we "miss" the target on that frame */ + delta = target - vbr_rate; + + target = nbAvailableBytes << (EntropyCoder.BITRES + 3); + + /*If the frame is silent we don't adjust our drift, otherwise + the encoder will shoot to very high rates after hitting a + span of silence, but we do allow the EntropyCoder.BITRES to refill. + This means that we'll undershoot our target in CVBR/VBR modes + on files with lots of silence. */ + if (silence != 0) { + nbAvailableBytes = 2; + target = 2 * 8 << EntropyCoder.BITRES; + delta = 0; + } + + if (this.vbr_count < 970) { + this.vbr_count++; + alpha = Inlines.celt_rcp(Inlines.SHL32((this.vbr_count + 20), 16)); + } else { + alpha = ((short) (0.5 + (.001f) * (((int) 1) << (15))))/*Inlines.QCONST16(.001f, 15)*/; + } + /* How many bits have we used in excess of what we're allowed */ + if (this.constrained_vbr != 0) { + this.vbr_reservoir += target - vbr_rate; + } + /*printf ("%d\n", st.vbr_reservoir);*/ + + /* Compute the offset we need to apply in order to reach the target */ + if (this.constrained_vbr != 0) { + this.vbr_drift += (int) Inlines.MULT16_32_Q15(alpha, (delta * (1 << lm_diff)) - this.vbr_offset - this.vbr_drift); + this.vbr_offset = -this.vbr_drift; + } + /*printf ("%d\n", st.vbr_drift);*/ + + if (this.constrained_vbr != 0 && this.vbr_reservoir < 0) { + /* We're under the min value -- increase rate */ + int adjust = (-this.vbr_reservoir) / (8 << EntropyCoder.BITRES); + /* Unless we're just coding silence */ + nbAvailableBytes += silence != 0 ? 0 : adjust; + this.vbr_reservoir = 0; + /*printf ("+%d\n", adjust);*/ + } + nbCompressedBytes = Inlines.IMIN(nbCompressedBytes, nbAvailableBytes + nbFilledBytes); + /*printf("%d\n", nbCompressedBytes*50*8);*/ + /* This moves the raw bits to take into account the new compressed size */ + enc.enc_shrink(nbCompressedBytes); + } + + /* Bit allocation */ + fine_quant = new int[nbEBands]; + pulses = new int[nbEBands]; + fine_priority = new int[nbEBands]; + + /* bits = packet size - where we are - safety*/ + bits = (((int) nbCompressedBytes * 8) << EntropyCoder.BITRES) - (int) enc.tell_frac() - 1; + anti_collapse_rsv = isTransient != 0 && LM >= 2 && bits >= ((LM + 2) << EntropyCoder.BITRES) ? (1 << EntropyCoder.BITRES) : 0; + bits -= anti_collapse_rsv; + signalBandwidth = end - 1; + + if (this.analysis.enabled && this.analysis.valid != 0) { + int min_bandwidth; + if (equiv_rate < (int) 32000 * C) { + min_bandwidth = 13; + } else if (equiv_rate < (int) 48000 * C) { + min_bandwidth = 16; + } else if (equiv_rate < (int) 60000 * C) { + min_bandwidth = 18; + } else if (equiv_rate < (int) 80000 * C) { + min_bandwidth = 19; + } else { + min_bandwidth = 20; + } + signalBandwidth = Inlines.IMAX(this.analysis.bandwidth, min_bandwidth); + } + + if (this.lfe != 0) { + signalBandwidth = 1; + } + + BoxedValueInt boxed_intensity = new BoxedValueInt(this.intensity); + BoxedValueInt boxed_balance = new BoxedValueInt(0); + BoxedValueInt boxed_dual_stereo = new BoxedValueInt(dual_stereo); + codedBands = Rate.compute_allocation(mode, start, end, offsets, cap, + alloc_trim, boxed_intensity, boxed_dual_stereo, bits, boxed_balance, pulses, + fine_quant, fine_priority, C, LM, enc, 1, this.lastCodedBands, signalBandwidth); + this.intensity = boxed_intensity.Val; + balance = boxed_balance.Val; + dual_stereo = boxed_dual_stereo.Val; + + if (this.lastCodedBands != 0) { + this.lastCodedBands = Inlines.IMIN(this.lastCodedBands + 1, Inlines.IMAX(this.lastCodedBands - 1, codedBands)); + } else { + this.lastCodedBands = codedBands; + } + + QuantizeBands.quant_fine_energy(mode, start, end, this.oldBandE, error, fine_quant, enc, C); + + /* Residual quantisation */ + collapse_masks = new short[C * nbEBands]; + BoxedValueInt boxed_rng = new BoxedValueInt(this.rng); + Bands.quant_all_bands(1, mode, start, end, X[0], C == 2 ? X[1] : null, collapse_masks, + bandE, pulses, shortBlocks, this.spread_decision, + dual_stereo, this.intensity, tf_res, nbCompressedBytes * (8 << EntropyCoder.BITRES) - anti_collapse_rsv, + balance, enc, LM, codedBands, boxed_rng); + this.rng = boxed_rng.Val; + + if (anti_collapse_rsv > 0) { + anti_collapse_on = (this.consec_transient < 2) ? 1 : 0; + enc.enc_bits(anti_collapse_on, 1); + } + + QuantizeBands.quant_energy_finalise(mode, start, end, this.oldBandE, error, fine_quant, fine_priority, nbCompressedBytes * 8 - (int) enc.tell(), enc, C); + + if (silence != 0) { + for (i = 0; i < nbEBands; i++) { + this.oldBandE[0][i] = -((short) (0.5 + (28.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + if (C == 2) { + for (i = 0; i < nbEBands; i++) { + this.oldBandE[1][i] = -((short) (0.5 + (28.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + } + } + + this.prefilter_period = pitch_index; + this.prefilter_gain = gain1; + this.prefilter_tapset = prefilter_tapset; + + if (CC == 2 && C == 1) { + System.arraycopy(oldBandE[0], 0, oldBandE[1], 0, nbEBands); + } + + if (isTransient == 0) { + System.arraycopy(oldLogE[0], 0, oldLogE2[0], 0, nbEBands); + System.arraycopy(oldBandE[0], 0, oldLogE[0], 0, nbEBands); + if (CC == 2) { + System.arraycopy(oldLogE[1], 0, oldLogE2[1], 0, nbEBands); + System.arraycopy(oldBandE[1], 0, oldLogE[1], 0, nbEBands); + } + } else { + for (i = 0; i < nbEBands; i++) { + oldLogE[0][i] = Inlines.MIN16(oldLogE[0][i], oldBandE[0][i]); + } + if (CC == 2) { + for (i = 0; i < nbEBands; i++) { + oldLogE[1][i] = Inlines.MIN16(oldLogE[1][i], oldBandE[1][i]); + } + } + } + + /* In case start or end were to change */ + c = 0; + do { + for (i = 0; i < start; i++) { + oldBandE[c][i] = 0; + oldLogE[c][i] = oldLogE2[c][i] = -((short) (0.5 + (28.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + for (i = end; i < nbEBands; i++) { + oldBandE[c][i] = 0; + oldLogE[c][i] = oldLogE2[c][i] = -((short) (0.5 + (28.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + } while (++c < CC); + + if (isTransient != 0 || transient_got_disabled != 0) { + this.consec_transient++; + } else { + this.consec_transient = 0; + } + this.rng = (int) enc.rng; + + /* If there's any room left (can only happen for very high rates), + it's already filled with zeros */ + enc.enc_done(); + + if (enc.get_error() != 0) { + return OpusError.OPUS_INTERNAL_ERROR; + } else { + return nbCompressedBytes; + } + } + + void SetComplexity(int value) { + if (value < 0 || value > 10) { + throw new IllegalArgumentException("Complexity must be between 0 and 10 inclusive"); + } + this.complexity = value; + } + + void SetStartBand(int value) { + if (value < 0 || value >= this.mode.nbEBands) { + throw new IllegalArgumentException("Start band above max number of ebands (or negative)"); + } + this.start = value; + } + + void SetEndBand(int value) { + if (value < 1 || value > this.mode.nbEBands) { + throw new IllegalArgumentException("End band above max number of ebands (or less than 1)"); + } + this.end = value; + } + + void SetPacketLossPercent(int value) { + if (value < 0 || value > 100) { + throw new IllegalArgumentException("Packet loss must be between 0 and 100"); + } + this.loss_rate = value; + } + + void SetPrediction(int value) { + if (value < 0 || value > 2) { + throw new IllegalArgumentException("CELT prediction mode must be 0, 1, or 2"); + } + this.disable_pf = (value <= 1) ? 1 : 0; + this.force_intra = (value == 0) ? 1 : 0; + } + + void SetVBRConstraint(boolean value) { + this.constrained_vbr = value ? 1 : 0; + } + + void SetVBR(boolean value) { + this.vbr = value ? 1 : 0; + } + + void SetBitrate(int value) { + if (value <= 500 && value != OpusConstants.OPUS_BITRATE_MAX) { + throw new IllegalArgumentException("Bitrate out of range"); + } + value = Inlines.IMIN(value, 260000 * this.channels); + this.bitrate = value; + } + + void SetChannels(int value) { + if (value < 1 || value > 2) { + throw new IllegalArgumentException("Channel count must be 1 or 2"); + } + this.stream_channels = value; + } + + void SetLSBDepth(int value) { + if (value < 8 || value > 24) { + throw new IllegalArgumentException("Bit depth must be between 8 and 24"); + } + this.lsb_depth = value; + } + + int GetLSBDepth() { + return this.lsb_depth; + } + + void SetExpertFrameDuration(OpusFramesize value) { + this.variable_duration = value; + } + + void SetSignalling(int value) { + this.signalling = value; + } + + void SetAnalysis(AnalysisInfo value) { + if (value == null) { + throw new IllegalArgumentException("AnalysisInfo"); + } + this.analysis.Assign(value); + } + + CeltMode GetMode() { + return this.mode; + } + + int GetFinalRange() { + return this.rng; + } + + void SetLFE(int value) { + this.lfe = value; + } + + void SetEnergyMask(int[] value) { + this.energy_mask = value; + } + +} diff --git a/app/src/main/java/org/concentus/CeltLPC.java b/app/src/main/java/org/concentus/CeltLPC.java new file mode 100644 index 0000000000..a3aa0c876b --- /dev/null +++ b/app/src/main/java/org/concentus/CeltLPC.java @@ -0,0 +1,153 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class CeltLPC { + + static void celt_lpc( + int[] _lpc, /* out: [0...p-1] LPC coefficients */ + int[] ac, /* in: [0...p] autocorrelation values */ + int p) { + int i, j; + int r; + int error = ac[0]; + int[] lpc = new int[p]; + + //Arrays.MemSet(lpc, 0, p); strictly, this is not necessary since the runtime zeroes memory for us + if (ac[0] != 0) { + for (i = 0; i < p; i++) { + /* Sum up this iteration's reflection coefficient */ + int rr = 0; + for (j = 0; j < i; j++) { + rr += Inlines.MULT32_32_Q31(lpc[j], ac[i - j]); + } + rr += Inlines.SHR32(ac[i + 1], 3); + r = 0 - Inlines.frac_div32(Inlines.SHL32(rr, 3), error); + /* Update LPC coefficients and total error */ + lpc[i] = Inlines.SHR32(r, 3); + + for (j = 0; j < (i + 1) >> 1; j++) { + int tmp1, tmp2; + tmp1 = lpc[j]; + tmp2 = lpc[i - 1 - j]; + lpc[j] = tmp1 + Inlines.MULT32_32_Q31(r, tmp2); + lpc[i - 1 - j] = tmp2 + Inlines.MULT32_32_Q31(r, tmp1); + } + + error = error - Inlines.MULT32_32_Q31(Inlines.MULT32_32_Q31(r, r), error); + + /* Bail out once we get 30 dB gain */ + if (error < Inlines.SHR32(ac[0], 10)) { + break; + } + } + } + + for (i = 0; i < p; i++) { + _lpc[i] = Inlines.ROUND16((lpc[i]), 16); + } + } + + static void celt_iir( + int[] _x, + int _x_ptr, + int[] den, + int[] _y, + int _y_ptr, + int N, + int ord, + int[] mem) { + int i, j; + int[] rden = new int[ord]; + int[] y = new int[N + ord]; + Inlines.OpusAssert((ord & 3) == 0); + + BoxedValueInt _sum0 = new BoxedValueInt(0); + BoxedValueInt _sum1 = new BoxedValueInt(0); + BoxedValueInt _sum2 = new BoxedValueInt(0); + BoxedValueInt _sum3 = new BoxedValueInt(0); + int sum0, sum1, sum2, sum3; + + for (i = 0; i < ord; i++) { + rden[i] = den[ord - i - 1]; + } + for (i = 0; i < ord; i++) { + y[i] = (0 - mem[ord - i - 1]); + } + for (; i < N + ord; i++) { + y[i] = 0; + } + for (i = 0; i < N - 3; i += 4) { + /* Unroll by 4 as if it were an FIR filter */ + _sum0.Val = _x[_x_ptr + i]; + _sum1.Val = _x[_x_ptr + i + 1]; + _sum2.Val = _x[_x_ptr + i + 2]; + _sum3.Val = _x[_x_ptr + i + 3]; + Kernels.xcorr_kernel(rden, y, i, _sum0, _sum1, _sum2, _sum3, ord); + sum0 = _sum0.Val; + sum1 = _sum1.Val; + sum2 = _sum2.Val; + sum3 = _sum3.Val; + + /* Patch up the result to compensate for the fact that this is an IIR */ + y[i + ord] = (0 - Inlines.ROUND16((sum0), CeltConstants.SIG_SHIFT)); + _y[_y_ptr + i] = sum0; + sum1 = Inlines.MAC16_16(sum1, y[i + ord], den[0]); + y[i + ord + 1] = (0 - Inlines.ROUND16((sum1), CeltConstants.SIG_SHIFT)); + _y[_y_ptr + i + 1] = sum1; + sum2 = Inlines.MAC16_16(sum2, y[i + ord + 1], den[0]); + sum2 = Inlines.MAC16_16(sum2, y[i + ord], den[1]); + y[i + ord + 2] = (0 - Inlines.ROUND16((sum2), CeltConstants.SIG_SHIFT)); + _y[_y_ptr + i + 2] = sum2; + + sum3 = Inlines.MAC16_16(sum3, y[i + ord + 2], den[0]); + sum3 = Inlines.MAC16_16(sum3, y[i + ord + 1], den[1]); + sum3 = Inlines.MAC16_16(sum3, y[i + ord], den[2]); + y[i + ord + 3] = (0 - Inlines.ROUND16((sum3), CeltConstants.SIG_SHIFT)); + _y[_y_ptr + i + 3] = sum3; + } + for (; i < N; i++) { + int sum = _x[_x_ptr + i]; + for (j = 0; j < ord; j++) { + sum -= Inlines.MULT16_16(rden[j], y[i + j]); + } + y[i + ord] = Inlines.ROUND16((sum), CeltConstants.SIG_SHIFT); + _y[_y_ptr + i] = sum; + } + for (i = 0; i < ord; i++) { + mem[i] = (_y[_y_ptr + N - i - 1]); + } + } +} diff --git a/app/src/main/java/org/concentus/CeltMode.java b/app/src/main/java/org/concentus/CeltMode.java new file mode 100644 index 0000000000..921e3b7007 --- /dev/null +++ b/app/src/main/java/org/concentus/CeltMode.java @@ -0,0 +1,107 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class CeltMode { + + int Fs = 0; + int overlap = 0; + + int nbEBands = 0; + int effEBands = 0; + int[] preemph = {0, 0, 0, 0}; + + /// + /// Definition for each "pseudo-critical band" + /// + short[] eBands = null; + + int maxLM = 0; + int nbShortMdcts = 0; + int shortMdctSize = 0; + + /// + /// Number of lines in allocVectors + /// + int nbAllocVectors = 0; + + /// + /// Number of bits in each band for several rates + /// + short[] allocVectors = null; + short[] logN = null; + + int[] window = null; + MDCTLookup mdct = new MDCTLookup(); + PulseCache cache = new PulseCache(); + + private CeltMode() { + } + + static final CeltMode mode48000_960_120 = new CeltMode(); + + static { + mode48000_960_120.Fs = 48000; + mode48000_960_120.overlap = 120; + mode48000_960_120.nbEBands = 21; + mode48000_960_120.effEBands = 21; + mode48000_960_120.preemph = new int[]{27853, 0, 4096, 8192}; + mode48000_960_120.eBands = CeltTables.eband5ms; + mode48000_960_120.maxLM = 3; + mode48000_960_120.nbShortMdcts = 8; + mode48000_960_120.shortMdctSize = 120; + mode48000_960_120.nbAllocVectors = 11; + mode48000_960_120.allocVectors = CeltTables.band_allocation; + mode48000_960_120.logN = CeltTables.logN400; + mode48000_960_120.window = CeltTables.window120; + mode48000_960_120.mdct = new MDCTLookup(); + + mode48000_960_120.mdct.n = 1920; + mode48000_960_120.mdct.maxshift = 3; + mode48000_960_120.mdct.kfft = new FFTState[]{ + CeltTables.fft_state48000_960_0, + CeltTables.fft_state48000_960_1, + CeltTables.fft_state48000_960_2, + CeltTables.fft_state48000_960_3,}; + + mode48000_960_120.mdct.trig = CeltTables.mdct_twiddles960; + mode48000_960_120.cache = new PulseCache(); + mode48000_960_120.cache.size = 392; + mode48000_960_120.cache.index = CeltTables.cache_index50; + mode48000_960_120.cache.bits = CeltTables.cache_bits50; + mode48000_960_120.cache.caps = CeltTables.cache_caps50; + } +; +} diff --git a/app/src/main/java/org/concentus/CeltPitchXCorr.java b/app/src/main/java/org/concentus/CeltPitchXCorr.java new file mode 100644 index 0000000000..a39672512f --- /dev/null +++ b/app/src/main/java/org/concentus/CeltPitchXCorr.java @@ -0,0 +1,153 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class CeltPitchXCorr { + + static int pitch_xcorr( + int[] _x, + int[] _y, + int[] xcorr, + int len, + int max_pitch) { + int i; + int maxcorr = 1; + Inlines.OpusAssert(max_pitch > 0); + BoxedValueInt sum0 = new BoxedValueInt(0); + BoxedValueInt sum1 = new BoxedValueInt(0); + BoxedValueInt sum2 = new BoxedValueInt(0); + BoxedValueInt sum3 = new BoxedValueInt(0); + for (i = 0; i < max_pitch - 3; i += 4) { + sum0.Val = 0; + sum1.Val = 0; + sum2.Val = 0; + sum3.Val = 0; + Kernels.xcorr_kernel(_x, _y, i, sum0, sum1, sum2, sum3, len); + xcorr[i] = sum0.Val; + xcorr[i + 1] = sum1.Val; + xcorr[i + 2] = sum2.Val; + xcorr[i + 3] = sum3.Val; + sum0.Val = Inlines.MAX32(sum0.Val, sum1.Val); + sum2.Val = Inlines.MAX32(sum2.Val, sum3.Val); + sum0.Val = Inlines.MAX32(sum0.Val, sum2.Val); + maxcorr = Inlines.MAX32(maxcorr, sum0.Val); + } + /* In case max_pitch isn't a multiple of 4, do non-unrolled version. */ + for (; i < max_pitch; i++) { + int inner_sum = Kernels.celt_inner_prod(_x, 0, _y, i, len); + xcorr[i] = inner_sum; + maxcorr = Inlines.MAX32(maxcorr, inner_sum); + } + return maxcorr; + } + + static int pitch_xcorr( + short[] _x, + int _x_ptr, + short[] _y, + int _y_ptr, + int[] xcorr, + int len, + int max_pitch) { + int i; + int maxcorr = 1; + Inlines.OpusAssert(max_pitch > 0); + BoxedValueInt sum0 = new BoxedValueInt(0); + BoxedValueInt sum1 = new BoxedValueInt(0); + BoxedValueInt sum2 = new BoxedValueInt(0); + BoxedValueInt sum3 = new BoxedValueInt(0); + for (i = 0; i < max_pitch - 3; i += 4) { + sum0.Val = 0; + sum1.Val = 0; + sum2.Val = 0; + sum3.Val = 0; + Kernels.xcorr_kernel(_x, _x_ptr, _y, _y_ptr + i, sum0, sum1, sum2, sum3, len); + + xcorr[i] = sum0.Val; + xcorr[i + 1] = sum1.Val; + xcorr[i + 2] = sum2.Val; + xcorr[i + 3] = sum3.Val; + sum0.Val = Inlines.MAX32(sum0.Val, sum1.Val); + sum2.Val = Inlines.MAX32(sum2.Val, sum3.Val); + sum0.Val = Inlines.MAX32(sum0.Val, sum2.Val); + maxcorr = Inlines.MAX32(maxcorr, sum0.Val); + } + /* In case max_pitch isn't a multiple of 4, do non-unrolled version. */ + for (; i < max_pitch; i++) { + int inner_sum = Kernels.celt_inner_prod(_x, _x_ptr, _y, _y_ptr + i, len); + xcorr[i] = inner_sum; + maxcorr = Inlines.MAX32(maxcorr, inner_sum); + } + return maxcorr; + } + + static int pitch_xcorr( + short[] _x, + short[] _y, + int[] xcorr, + int len, + int max_pitch) { + int i; + int maxcorr = 1; + Inlines.OpusAssert(max_pitch > 0); + BoxedValueInt sum0 = new BoxedValueInt(0); + BoxedValueInt sum1 = new BoxedValueInt(0); + BoxedValueInt sum2 = new BoxedValueInt(0); + BoxedValueInt sum3 = new BoxedValueInt(0); + for (i = 0; i < max_pitch - 3; i += 4) { + sum0.Val = 0; + sum1.Val = 0; + sum2.Val = 0; + sum3.Val = 0; + Kernels.xcorr_kernel(_x, 0, _y, i, sum0, sum1, sum2, sum3, len); + + xcorr[i] = sum0.Val; + xcorr[i + 1] = sum1.Val; + xcorr[i + 2] = sum2.Val; + xcorr[i + 3] = sum3.Val; + sum0.Val = Inlines.MAX32(sum0.Val, sum1.Val); + sum2.Val = Inlines.MAX32(sum2.Val, sum3.Val); + sum0.Val = Inlines.MAX32(sum0.Val, sum2.Val); + maxcorr = Inlines.MAX32(maxcorr, sum0.Val); + } + /* In case max_pitch isn't a multiple of 4, do non-unrolled version. */ + for (; i < max_pitch; i++) { + int inner_sum = Kernels.celt_inner_prod(_x, _y, i, len); + xcorr[i] = inner_sum; + maxcorr = Inlines.MAX32(maxcorr, inner_sum); + } + return maxcorr; + } +} diff --git a/app/src/main/java/org/concentus/CeltTables.java b/app/src/main/java/org/concentus/CeltTables.java new file mode 100644 index 0000000000..90c8500f12 --- /dev/null +++ b/app/src/main/java/org/concentus/CeltTables.java @@ -0,0 +1,1116 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class CeltTables { + + /* Mean energy in each band quantized in Q4 */ + static final byte[] eMeans/*[25]*/ = { + 103, 100, 92, 85, 81, + 77, 72, 70, 78, 75, + 73, 71, 78, 74, 69, + 72, 70, 74, 76, 71, + 60, 60, 60, 60, 60 + }; + + /* Indexing table for converting from natural Hadamard to ordery Hadamard + This is essentially a bit-reversed Gray, on top of which we've added + an inversion of the order because we want the DC at the end rather than + the beginning. The lines are for N=2, 4, 8, 16 */ + static final int[] ordery_table = { + 1, 0, + 3, 0, 2, 1, + 7, 0, 4, 3, 6, 1, 5, 2, + 15, 0, 8, 7, 12, 3, 11, 4, 14, 1, 9, 6, 13, 2, 10, 5,}; + + static final short[] eband5ms = { + /*0 200 400 600 800 1k 1.2 1.4 1.6 2k 2.4 2.8 3.2 4k 4.8 5.6 6.8 8k 9.6 12k 15.6 */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 34, 40, 48, 60, 78, 100 + }; + + /*Parameters of the Laplace-like probability models used for the coarse energy. + There is one pair of parameters for each frame size, prediction type + (inter/intra), and band number. + The first number of each pair is the probability of 0, and the second is the + decay rate, both in Q8 precision.*/ + static final short[][][] e_prob_model = { + /*120 sample frames.*/ + new short[][]{ + /*Inter*/ + new short[]{ + 72, 127, 65, 129, 66, 128, 65, 128, 64, 128, 62, 128, 64, 128, + 64, 128, 92, 78, 92, 79, 92, 78, 90, 79, 116, 41, 115, 40, + 114, 40, 132, 26, 132, 26, 145, 17, 161, 12, 176, 10, 177, 11 + }, + /*Intra*/ + new short[]{ + 24, 179, 48, 138, 54, 135, 54, 132, 53, 134, 56, 133, 55, 132, + 55, 132, 61, 114, 70, 96, 74, 88, 75, 88, 87, 74, 89, 66, + 91, 67, 100, 59, 108, 50, 120, 40, 122, 37, 97, 43, 78, 50 + } + }, + /*240 sample frames.*/ + new short[][]{ + /*Inter*/ + new short[]{ + 83, 78, 84, 81, 88, 75, 86, 74, 87, 71, 90, 73, 93, 74, + 93, 74, 109, 40, 114, 36, 117, 34, 117, 34, 143, 17, 145, 18, + 146, 19, 162, 12, 165, 10, 178, 7, 189, 6, 190, 8, 177, 9 + }, + /*Intra*/ + new short[]{ + 23, 178, 54, 115, 63, 102, 66, 98, 69, 99, 74, 89, 71, 91, + 73, 91, 78, 89, 86, 80, 92, 66, 93, 64, 102, 59, 103, 60, + 104, 60, 117, 52, 123, 44, 138, 35, 133, 31, 97, 38, 77, 45 + } + }, + /*480 sample frames.*/ + new short[][]{ + /*Inter*/ + new short[]{ + 61, 90, 93, 60, 105, 42, 107, 41, 110, 45, 116, 38, 113, 38, + 112, 38, 124, 26, 132, 27, 136, 19, 140, 20, 155, 14, 159, 16, + 158, 18, 170, 13, 177, 10, 187, 8, 192, 6, 175, 9, 159, 10 + }, + /*Intra*/ + new short[]{ + 21, 178, 59, 110, 71, 86, 75, 85, 84, 83, 91, 66, 88, 73, + 87, 72, 92, 75, 98, 72, 105, 58, 107, 54, 115, 52, 114, 55, + 112, 56, 129, 51, 132, 40, 150, 33, 140, 29, 98, 35, 77, 42 + } + }, + /*960 sample frames.*/ + new short[][]{ + /*Inter*/ + new short[]{ + 42, 121, 96, 66, 108, 43, 111, 40, 117, 44, 123, 32, 120, 36, + 119, 33, 127, 33, 134, 34, 139, 21, 147, 23, 152, 20, 158, 25, + 154, 26, 166, 21, 173, 16, 184, 13, 184, 10, 150, 13, 139, 15 + }, + /*Intra*/ + new short[]{ + 22, 178, 63, 114, 74, 82, 84, 83, 92, 82, 103, 62, 96, 72, + 96, 67, 101, 73, 107, 72, 113, 55, 118, 52, 125, 52, 118, 52, + 117, 55, 135, 49, 137, 39, 157, 32, 145, 29, 97, 33, 77, 40 + } + } + }; + + static final byte[][] tf_select_table = { + new byte[]{0, -1, 0, -1, 0, -1, 0, -1}, + new byte[]{0, -1, 0, -2, 1, 0, 1, -1}, + new byte[]{0, -2, 0, -3, 2, 0, 1, -1}, + new byte[]{0, -2, 0, -3, 3, 0, 1, -1},}; + + static final short[] trim_icdf = {126, 124, 119, 109, 87, 41, 19, 9, 4, 2, 0}; + /* Probs: NONE: 21.875%, LIGHT: 6.25%, NORMAL: 65.625%, AGGRESSIVE: 6.25% */ + static final short[] spread_icdf = {25, 23, 2, 0}; + + static final short[] tapset_icdf = {2, 1, 0}; + + /* Bit allocation table in units of 1/32 bit/sample (0.1875 dB SNR) */ + static final short[] band_allocation = { + /*0 200 400 600 800 1k 1.2 1.4 1.6 2k 2.4 2.8 3.2 4k 4.8 5.6 6.8 8k 9.6 12k 15.6 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 80, 75, 69, 63, 56, 49, 40, 34, 29, 20, 18, 10, 0, 0, 0, 0, 0, 0, 0, 0, + 110, 100, 90, 84, 78, 71, 65, 58, 51, 45, 39, 32, 26, 20, 12, 0, 0, 0, 0, 0, 0, + 118, 110, 103, 93, 86, 80, 75, 70, 65, 59, 53, 47, 40, 31, 23, 15, 4, 0, 0, 0, 0, + 126, 119, 112, 104, 95, 89, 83, 78, 72, 66, 60, 54, 47, 39, 32, 25, 17, 12, 1, 0, 0, + 134, 127, 120, 114, 103, 97, 91, 85, 78, 72, 66, 60, 54, 47, 41, 35, 29, 23, 16, 10, 1, + 144, 137, 130, 124, 113, 107, 101, 95, 88, 82, 76, 70, 64, 57, 51, 45, 39, 33, 26, 15, 1, + 152, 145, 138, 132, 123, 117, 111, 105, 98, 92, 86, 80, 74, 67, 61, 55, 49, 43, 36, 20, 1, + 162, 155, 148, 142, 133, 127, 121, 115, 108, 102, 96, 90, 84, 77, 71, 65, 59, 53, 46, 30, 1, + 172, 165, 158, 152, 143, 137, 131, 125, 118, 112, 106, 100, 94, 87, 81, 75, 69, 63, 56, 45, 20, + 200, 200, 200, 200, 200, 200, 200, 200, 198, 193, 188, 183, 178, 173, 168, 163, 158, 153, 148, 129, 104,}; + + /*For each V(N,K) supported, we will access element U(min(N,K+1),max(N,K+1)). + Thus, the number of entries in row I is the larger of the maximum number of + pulses we will ever allocate for a given N=I (K=128, or however many fit in + 32 bits, whichever is smaller), plus one, and the maximum N for which + K=I-1 pulses fit in 32 bits. + The largest band size in an Opus Custom mode is 208. + Otherwise, we can limit things to the set of N which can be achieved by + splitting a band from a standard Opus mode: 176, 144, 96, 88, 72, 64, 48, + 44, 36, 32, 24, 22, 18, 16, 8, 4, 2).*/ + static final long[] CELT_PVQ_U_DATA = { + /*N=0, K=0...176:*/ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /*N=1, K=1...176:*/ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + /*N=2, K=2...176:*/ + 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, + 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, + 81, 83, 85, 87, 89, 91, 93, 95, 97, 99, 101, 103, 105, 107, 109, 111, 113, + 115, 117, 119, 121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 141, 143, + 145, 147, 149, 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, + 175, 177, 179, 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, + 205, 207, 209, 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, + 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 257, 259, 261, 263, + 265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 285, 287, 289, 291, 293, + 295, 297, 299, 301, 303, 305, 307, 309, 311, 313, 315, 317, 319, 321, 323, + 325, 327, 329, 331, 333, 335, 337, 339, 341, 343, 345, 347, 349, 351, + /*N=3, K=3...176:*/ + 13, 25, 41, 61, 85, 113, 145, 181, 221, 265, 313, 365, 421, 481, 545, 613, + 685, 761, 841, 925, 1013, 1105, 1201, 1301, 1405, 1513, 1625, 1741, 1861, + 1985, 2113, 2245, 2381, 2521, 2665, 2813, 2965, 3121, 3281, 3445, 3613, 3785, + 3961, 4141, 4325, 4513, 4705, 4901, 5101, 5305, 5513, 5725, 5941, 6161, 6385, + 6613, 6845, 7081, 7321, 7565, 7813, 8065, 8321, 8581, 8845, 9113, 9385, 9661, + 9941, 10225, 10513, 10805, 11101, 11401, 11705, 12013, 12325, 12641, 12961, + 13285, 13613, 13945, 14281, 14621, 14965, 15313, 15665, 16021, 16381, 16745, + 17113, 17485, 17861, 18241, 18625, 19013, 19405, 19801, 20201, 20605, 21013, + 21425, 21841, 22261, 22685, 23113, 23545, 23981, 24421, 24865, 25313, 25765, + 26221, 26681, 27145, 27613, 28085, 28561, 29041, 29525, 30013, 30505, 31001, + 31501, 32005, 32513, 33025, 33541, 34061, 34585, 35113, 35645, 36181, 36721, + 37265, 37813, 38365, 38921, 39481, 40045, 40613, 41185, 41761, 42341, 42925, + 43513, 44105, 44701, 45301, 45905, 46513, 47125, 47741, 48361, 48985, 49613, + 50245, 50881, 51521, 52165, 52813, 53465, 54121, 54781, 55445, 56113, 56785, + 57461, 58141, 58825, 59513, 60205, 60901, 61601, + /*N=4, K=4...176:*/ + 63, 129, 231, 377, 575, 833, 1159, 1561, 2047, 2625, 3303, 4089, 4991, 6017, + 7175, 8473, 9919, 11521, 13287, 15225, 17343, 19649, 22151, 24857, 27775, + 30913, 34279, 37881, 41727, 45825, 50183, 54809, 59711, 64897, 70375, 76153, + 82239, 88641, 95367, 102425, 109823, 117569, 125671, 134137, 142975, 152193, + 161799, 171801, 182207, 193025, 204263, 215929, 228031, 240577, 253575, + 267033, 280959, 295361, 310247, 325625, 341503, 357889, 374791, 392217, + 410175, 428673, 447719, 467321, 487487, 508225, 529543, 551449, 573951, + 597057, 620775, 645113, 670079, 695681, 721927, 748825, 776383, 804609, + 833511, 863097, 893375, 924353, 956039, 988441, 1021567, 1055425, 1090023, + 1125369, 1161471, 1198337, 1235975, 1274393, 1313599, 1353601, 1394407, + 1436025, 1478463, 1521729, 1565831, 1610777, 1656575, 1703233, 1750759, + 1799161, 1848447, 1898625, 1949703, 2001689, 2054591, 2108417, 2163175, + 2218873, 2275519, 2333121, 2391687, 2451225, 2511743, 2573249, 2635751, + 2699257, 2763775, 2829313, 2895879, 2963481, 3032127, 3101825, 3172583, + 3244409, 3317311, 3391297, 3466375, 3542553, 3619839, 3698241, 3777767, + 3858425, 3940223, 4023169, 4107271, 4192537, 4278975, 4366593, 4455399, + 4545401, 4636607, 4729025, 4822663, 4917529, 5013631, 5110977, 5209575, + 5309433, 5410559, 5512961, 5616647, 5721625, 5827903, 5935489, 6044391, + 6154617, 6266175, 6379073, 6493319, 6608921, 6725887, 6844225, 6963943, + 7085049, 7207551, + /*N=5, K=5...176:*/ + 321, 681, 1289, 2241, 3649, 5641, 8361, 11969, 16641, 22569, 29961, 39041, + 50049, 63241, 78889, 97281, 118721, 143529, 172041, 204609, 241601, 283401, + 330409, 383041, 441729, 506921, 579081, 658689, 746241, 842249, 947241, + 1061761, 1186369, 1321641, 1468169, 1626561, 1797441, 1981449, 2179241, + 2391489, 2618881, 2862121, 3121929, 3399041, 3694209, 4008201, 4341801, + 4695809, 5071041, 5468329, 5888521, 6332481, 6801089, 7295241, 7815849, + 8363841, 8940161, 9545769, 10181641, 10848769, 11548161, 12280841, 13047849, + 13850241, 14689089, 15565481, 16480521, 17435329, 18431041, 19468809, + 20549801, 21675201, 22846209, 24064041, 25329929, 26645121, 28010881, + 29428489, 30899241, 32424449, 34005441, 35643561, 37340169, 39096641, + 40914369, 42794761, 44739241, 46749249, 48826241, 50971689, 53187081, + 55473921, 57833729, 60268041, 62778409, 65366401, 68033601, 70781609, + 73612041, 76526529, 79526721, 82614281, 85790889, 89058241, 92418049, + 95872041, 99421961, 103069569, 106816641, 110664969, 114616361, 118672641, + 122835649, 127107241, 131489289, 135983681, 140592321, 145317129, 150160041, + 155123009, 160208001, 165417001, 170752009, 176215041, 181808129, 187533321, + 193392681, 199388289, 205522241, 211796649, 218213641, 224775361, 231483969, + 238341641, 245350569, 252512961, 259831041, 267307049, 274943241, 282741889, + 290705281, 298835721, 307135529, 315607041, 324252609, 333074601, 342075401, + 351257409, 360623041, 370174729, 379914921, 389846081, 399970689, 410291241, + 420810249, 431530241, 442453761, 453583369, 464921641, 476471169, 488234561, + 500214441, 512413449, 524834241, 537479489, 550351881, 563454121, 576788929, + 590359041, 604167209, 618216201, 632508801, + /*N=6, K=6...96:*/ + 1683, 3653, 7183, 13073, 22363, 36365, 56695, 85305, 124515, 177045, 246047, + 335137, 448427, 590557, 766727, 982729, 1244979, 1560549, 1937199, 2383409, + 2908411, 3522221, 4235671, 5060441, 6009091, 7095093, 8332863, 9737793, + 11326283, 13115773, 15124775, 17372905, 19880915, 22670725, 25765455, + 29189457, 32968347, 37129037, 41699767, 46710137, 52191139, 58175189, + 64696159, 71789409, 79491819, 87841821, 96879431, 106646281, 117185651, + 128542501, 140763503, 153897073, 167993403, 183104493, 199284183, 216588185, + 235074115, 254801525, 275831935, 298228865, 322057867, 347386557, 374284647, + 402823977, 433078547, 465124549, 499040399, 534906769, 572806619, 612825229, + 655050231, 699571641, 746481891, 795875861, 847850911, 902506913, 959946283, + 1020274013, 1083597703, 1150027593, 1219676595, 1292660325, 1369097135, + 1449108145, 1532817275, 1620351277, 1711839767, 1807415257, 1907213187, + 2011371957, 2120032959, + /*N=7, K=7...54*/ + 8989, 19825, 40081, 75517, 134245, 227305, 369305, 579125, 880685, 1303777, + 1884961, 2668525, 3707509, 5064793, 6814249, 9041957, 11847485, 15345233, + 19665841, 24957661, 31388293, 39146185, 48442297, 59511829, 72616013, + 88043969, 106114625, 127178701, 151620757, 179861305, 212358985, 249612805, + 292164445, 340600625, 395555537, 457713341, 527810725, 606639529, 695049433, + 793950709, 904317037, 1027188385, 1163673953, 1314955181, 1482288821, + 1667010073, 1870535785, 2094367717, + /*N=8, K=8...37*/ + 48639, 108545, 224143, 433905, 795455, 1392065, 2340495, 3800305, 5984767, + 9173505, 13726991, 20103025, 28875327, 40754369, 56610575, 77500017, + 104692735, 139703809, 184327311, 240673265, 311207743, 398796225, 506750351, + 638878193, 799538175, 993696769, 1226990095, 1505789553, 1837271615, + 2229491905L, + /*N=9, K=9...28:*/ + 265729, 598417, 1256465, 2485825, 4673345, 8405905, 14546705, 24331777, + 39490049, 62390545, 96220561, 145198913, 214828609, 312193553, 446304145, + 628496897L, 872893441L, 1196924561L, 1621925137L, 2173806145L, + /*N=10, K=10...24:*/ + 1462563, 3317445, 7059735, 14218905, 27298155, 50250765, 89129247, 152951073, + 254831667L, 413442773L, 654862247L, 1014889769L, 1541911931L, 2300409629L, + 3375210671L, + /*N=11, K=11...19:*/ + 8097453, 18474633, 39753273, 81270333, 158819253, 298199265, 540279585, + 948062325, 1616336765, + /*N=12, K=12...18:*/ + 45046719, 103274625, 224298231, 464387817, 921406335, 1759885185, + 3248227095L, + /*N=13, K=13...16:*/ + 251595969L, 579168825L, 1267854873L, 2653649025L, + /*N=14, K=14:*/ + 1409933619 + }; + + static final int[] window120 = { + 2, 20, 55, 108, 178, + 266, 372, 494, 635, 792, + 966, 1157, 1365, 1590, 1831, + 2089, 2362, 2651, 2956, 3276, + 3611, 3961, 4325, 4703, 5094, + 5499, 5916, 6346, 6788, 7241, + 7705, 8179, 8663, 9156, 9657, + 10167, 10684, 11207, 11736, 12271, + 12810, 13353, 13899, 14447, 14997, + 15547, 16098, 16648, 17197, 17744, + 18287, 18827, 19363, 19893, 20418, + 20936, 21447, 21950, 22445, 22931, + 23407, 23874, 24330, 24774, 25208, + 25629, 26039, 26435, 26819, 27190, + 27548, 27893, 28224, 28541, 28845, + 29135, 29411, 29674, 29924, 30160, + 30384, 30594, 30792, 30977, 31151, + 31313, 31463, 31602, 31731, 31849, + 31958, 32057, 32148, 32229, 32303, + 32370, 32429, 32481, 32528, 32568, + 32604, 32634, 32661, 32683, 32701, + 32717, 32729, 32740, 32748, 32754, + 32758, 32762, 32764, 32766, 32767, + 32767, 32767, 32767, 32767, 32767,}; + + static final short[] logN400 + = {0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 16, 16, 16, 21, 21, 24, 29, 34, 36,}; + + static final short[] cache_index50 = { + -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 41, 41, 41, + 82, 82, 123, 164, 200, 222, 0, 0, 0, 0, 0, 0, 0, 0, 41, + 41, 41, 41, 123, 123, 123, 164, 164, 240, 266, 283, 295, 41, 41, 41, + 41, 41, 41, 41, 41, 123, 123, 123, 123, 240, 240, 240, 266, 266, 305, + 318, 328, 336, 123, 123, 123, 123, 123, 123, 123, 123, 240, 240, 240, 240, + 305, 305, 305, 318, 318, 343, 351, 358, 364, 240, 240, 240, 240, 240, 240, + 240, 240, 305, 305, 305, 305, 343, 343, 343, 351, 351, 370, 376, 382, 387,}; + + static final short[] cache_bits50 = { + 40, 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, 40, 15, 23, 28, + 31, 34, 36, 38, 39, 41, 42, 43, 44, 45, 46, 47, 47, 49, 50, + 51, 52, 53, 54, 55, 55, 57, 58, 59, 60, 61, 62, 63, 63, 65, + 66, 67, 68, 69, 70, 71, 71, 40, 20, 33, 41, 48, 53, 57, 61, + 64, 66, 69, 71, 73, 75, 76, 78, 80, 82, 85, 87, 89, 91, 92, + 94, 96, 98, 101, 103, 105, 107, 108, 110, 112, 114, 117, 119, 121, 123, + 124, 126, 128, 40, 23, 39, 51, 60, 67, 73, 79, 83, 87, 91, 94, + 97, 100, 102, 105, 107, 111, 115, 118, 121, 124, 126, 129, 131, 135, 139, + 142, 145, 148, 150, 153, 155, 159, 163, 166, 169, 172, 174, 177, 179, 35, + 28, 49, 65, 78, 89, 99, 107, 114, 120, 126, 132, 136, 141, 145, 149, + 153, 159, 165, 171, 176, 180, 185, 189, 192, 199, 205, 211, 216, 220, 225, + 229, 232, 239, 245, 251, 21, 33, 58, 79, 97, 112, 125, 137, 148, 157, + 166, 174, 182, 189, 195, 201, 207, 217, 227, 235, 243, 251, 17, 35, 63, + 86, 106, 123, 139, 152, 165, 177, 187, 197, 206, 214, 222, 230, 237, 250, + 25, 31, 55, 75, 91, 105, 117, 128, 138, 146, 154, 161, 168, 174, 180, + 185, 190, 200, 208, 215, 222, 229, 235, 240, 245, 255, 16, 36, 65, 89, + 110, 128, 144, 159, 173, 185, 196, 207, 217, 226, 234, 242, 250, 11, 41, + 74, 103, 128, 151, 172, 191, 209, 225, 241, 255, 9, 43, 79, 110, 138, + 163, 186, 207, 227, 246, 12, 39, 71, 99, 123, 144, 164, 182, 198, 214, + 228, 241, 253, 9, 44, 81, 113, 142, 168, 192, 214, 235, 255, 7, 49, + 90, 127, 160, 191, 220, 247, 6, 51, 95, 134, 170, 203, 234, 7, 47, + 87, 123, 155, 184, 212, 237, 6, 52, 97, 137, 174, 208, 240, 5, 57, + 106, 151, 192, 231, 5, 59, 111, 158, 202, 243, 5, 55, 103, 147, 187, + 224, 5, 60, 113, 161, 206, 248, 4, 65, 122, 175, 224, 4, 67, 127, + 182, 234,}; + + static final short[] cache_caps50 = { + 224, 224, 224, 224, 224, 224, 224, 224, 160, 160, 160, 160, 185, 185, 185, + 178, 178, 168, 134, 61, 37, 224, 224, 224, 224, 224, 224, 224, 224, 240, + 240, 240, 240, 207, 207, 207, 198, 198, 183, 144, 66, 40, 160, 160, 160, + 160, 160, 160, 160, 160, 185, 185, 185, 185, 193, 193, 193, 183, 183, 172, + 138, 64, 38, 240, 240, 240, 240, 240, 240, 240, 240, 207, 207, 207, 207, + 204, 204, 204, 193, 193, 180, 143, 66, 40, 185, 185, 185, 185, 185, 185, + 185, 185, 193, 193, 193, 193, 193, 193, 193, 183, 183, 172, 138, 65, 39, + 207, 207, 207, 207, 207, 207, 207, 207, 204, 204, 204, 204, 201, 201, 201, + 188, 188, 176, 141, 66, 40, 193, 193, 193, 193, 193, 193, 193, 193, 193, + 193, 193, 193, 194, 194, 194, 184, 184, 173, 139, 65, 39, 204, 204, 204, + 204, 204, 204, 204, 204, 201, 201, 201, 201, 198, 198, 198, 187, 187, 175, + 140, 66, 40,}; + + static final short[] fft_twiddles48000_960 + = new short[]{ + 32767, 0, 32766, -429, + 32757, -858, 32743, -1287, + 32724, -1715, 32698, -2143, + 32667, -2570, 32631, -2998, + 32588, -3425, 32541, -3851, + 32488, -4277, 32429, -4701, + 32364, -5125, 32295, -5548, + 32219, -5971, 32138, -6393, + 32051, -6813, 31960, -7231, + 31863, -7650, 31760, -8067, + 31652, -8481, 31539, -8895, + 31419, -9306, 31294, -9716, + 31165, -10126, 31030, -10532, + 30889, -10937, 30743, -11340, + 30592, -11741, 30436, -12141, + 30274, -12540, 30107, -12935, + 29936, -13328, 29758, -13718, + 29577, -14107, 29390, -14493, + 29197, -14875, 29000, -15257, + 28797, -15635, 28590, -16010, + 28379, -16384, 28162, -16753, + 27940, -17119, 27714, -17484, + 27482, -17845, 27246, -18205, + 27006, -18560, 26760, -18911, + 26510, -19260, 26257, -19606, + 25997, -19947, 25734, -20286, + 25466, -20621, 25194, -20952, + 24918, -21281, 24637, -21605, + 24353, -21926, 24063, -22242, + 23770, -22555, 23473, -22865, + 23171, -23171, 22866, -23472, + 22557, -23769, 22244, -24063, + 21927, -24352, 21606, -24636, + 21282, -24917, 20954, -25194, + 20622, -25465, 20288, -25733, + 19949, -25997, 19607, -26255, + 19261, -26509, 18914, -26760, + 18561, -27004, 18205, -27246, + 17846, -27481, 17485, -27713, + 17122, -27940, 16755, -28162, + 16385, -28378, 16012, -28590, + 15636, -28797, 15258, -28999, + 14878, -29197, 14494, -29389, + 14108, -29576, 13720, -29757, + 13329, -29934, 12937, -30107, + 12540, -30274, 12142, -30435, + 11744, -30592, 11342, -30743, + 10939, -30889, 10534, -31030, + 10127, -31164, 9718, -31294, + 9307, -31418, 8895, -31537, + 8482, -31652, 8067, -31759, + 7650, -31862, 7233, -31960, + 6815, -32051, 6393, -32138, + 5973, -32219, 5549, -32294, + 5127, -32364, 4703, -32429, + 4278, -32487, 3852, -32541, + 3426, -32588, 2999, -32630, + 2572, -32667, 2144, -32698, + 1716, -32724, 1287, -32742, + 860, -32757, 430, -32766, + 0, -32767, -429, -32766, + -858, -32757, -1287, -32743, + -1715, -32724, -2143, -32698, + -2570, -32667, -2998, -32631, + -3425, -32588, -3851, -32541, + -4277, -32488, -4701, -32429, + -5125, -32364, -5548, -32295, + -5971, -32219, -6393, -32138, + -6813, -32051, -7231, -31960, + -7650, -31863, -8067, -31760, + -8481, -31652, -8895, -31539, + -9306, -31419, -9716, -31294, + -10126, -31165, -10532, -31030, + -10937, -30889, -11340, -30743, + -11741, -30592, -12141, -30436, + -12540, -30274, -12935, -30107, + -13328, -29936, -13718, -29758, + -14107, -29577, -14493, -29390, + -14875, -29197, -15257, -29000, + -15635, -28797, -16010, -28590, + -16384, -28379, -16753, -28162, + -17119, -27940, -17484, -27714, + -17845, -27482, -18205, -27246, + -18560, -27006, -18911, -26760, + -19260, -26510, -19606, -26257, + -19947, -25997, -20286, -25734, + -20621, -25466, -20952, -25194, + -21281, -24918, -21605, -24637, + -21926, -24353, -22242, -24063, + -22555, -23770, -22865, -23473, + -23171, -23171, -23472, -22866, + -23769, -22557, -24063, -22244, + -24352, -21927, -24636, -21606, + -24917, -21282, -25194, -20954, + -25465, -20622, -25733, -20288, + -25997, -19949, -26255, -19607, + -26509, -19261, -26760, -18914, + -27004, -18561, -27246, -18205, + -27481, -17846, -27713, -17485, + -27940, -17122, -28162, -16755, + -28378, -16385, -28590, -16012, + -28797, -15636, -28999, -15258, + -29197, -14878, -29389, -14494, + -29576, -14108, -29757, -13720, + -29934, -13329, -30107, -12937, + -30274, -12540, -30435, -12142, + -30592, -11744, -30743, -11342, + -30889, -10939, -31030, -10534, + -31164, -10127, -31294, -9718, + -31418, -9307, -31537, -8895, + -31652, -8482, -31759, -8067, + -31862, -7650, -31960, -7233, + -32051, -6815, -32138, -6393, + -32219, -5973, -32294, -5549, + -32364, -5127, -32429, -4703, + -32487, -4278, -32541, -3852, + -32588, -3426, -32630, -2999, + -32667, -2572, -32698, -2144, + -32724, -1716, -32742, -1287, + -32757, -860, -32766, -430, + -32767, 0, -32766, 429, + -32757, 858, -32743, 1287, + -32724, 1715, -32698, 2143, + -32667, 2570, -32631, 2998, + -32588, 3425, -32541, 3851, + -32488, 4277, -32429, 4701, + -32364, 5125, -32295, 5548, + -32219, 5971, -32138, 6393, + -32051, 6813, -31960, 7231, + -31863, 7650, -31760, 8067, + -31652, 8481, -31539, 8895, + -31419, 9306, -31294, 9716, + -31165, 10126, -31030, 10532, + -30889, 10937, -30743, 11340, + -30592, 11741, -30436, 12141, + -30274, 12540, -30107, 12935, + -29936, 13328, -29758, 13718, + -29577, 14107, -29390, 14493, + -29197, 14875, -29000, 15257, + -28797, 15635, -28590, 16010, + -28379, 16384, -28162, 16753, + -27940, 17119, -27714, 17484, + -27482, 17845, -27246, 18205, + -27006, 18560, -26760, 18911, + -26510, 19260, -26257, 19606, + -25997, 19947, -25734, 20286, + -25466, 20621, -25194, 20952, + -24918, 21281, -24637, 21605, + -24353, 21926, -24063, 22242, + -23770, 22555, -23473, 22865, + -23171, 23171, -22866, 23472, + -22557, 23769, -22244, 24063, + -21927, 24352, -21606, 24636, + -21282, 24917, -20954, 25194, + -20622, 25465, -20288, 25733, + -19949, 25997, -19607, 26255, + -19261, 26509, -18914, 26760, + -18561, 27004, -18205, 27246, + -17846, 27481, -17485, 27713, + -17122, 27940, -16755, 28162, + -16385, 28378, -16012, 28590, + -15636, 28797, -15258, 28999, + -14878, 29197, -14494, 29389, + -14108, 29576, -13720, 29757, + -13329, 29934, -12937, 30107, + -12540, 30274, -12142, 30435, + -11744, 30592, -11342, 30743, + -10939, 30889, -10534, 31030, + -10127, 31164, -9718, 31294, + -9307, 31418, -8895, 31537, + -8482, 31652, -8067, 31759, + -7650, 31862, -7233, 31960, + -6815, 32051, -6393, 32138, + -5973, 32219, -5549, 32294, + -5127, 32364, -4703, 32429, + -4278, 32487, -3852, 32541, + -3426, 32588, -2999, 32630, + -2572, 32667, -2144, 32698, + -1716, 32724, -1287, 32742, + -860, 32757, -430, 32766, + 0, 32767, 429, 32766, + 858, 32757, 1287, 32743, + 1715, 32724, 2143, 32698, + 2570, 32667, 2998, 32631, + 3425, 32588, 3851, 32541, + 4277, 32488, 4701, 32429, + 5125, 32364, 5548, 32295, + 5971, 32219, 6393, 32138, + 6813, 32051, 7231, 31960, + 7650, 31863, 8067, 31760, + 8481, 31652, 8895, 31539, + 9306, 31419, 9716, 31294, + 10126, 31165, 10532, 31030, + 10937, 30889, 11340, 30743, + 11741, 30592, 12141, 30436, + 12540, 30274, 12935, 30107, + 13328, 29936, 13718, 29758, + 14107, 29577, 14493, 29390, + 14875, 29197, 15257, 29000, + 15635, 28797, 16010, 28590, + 16384, 28379, 16753, 28162, + 17119, 27940, 17484, 27714, + 17845, 27482, 18205, 27246, + 18560, 27006, 18911, 26760, + 19260, 26510, 19606, 26257, + 19947, 25997, 20286, 25734, + 20621, 25466, 20952, 25194, + 21281, 24918, 21605, 24637, + 21926, 24353, 22242, 24063, + 22555, 23770, 22865, 23473, + 23171, 23171, 23472, 22866, + 23769, 22557, 24063, 22244, + 24352, 21927, 24636, 21606, + 24917, 21282, 25194, 20954, + 25465, 20622, 25733, 20288, + 25997, 19949, 26255, 19607, + 26509, 19261, 26760, 18914, + 27004, 18561, 27246, 18205, + 27481, 17846, 27713, 17485, + 27940, 17122, 28162, 16755, + 28378, 16385, 28590, 16012, + 28797, 15636, 28999, 15258, + 29197, 14878, 29389, 14494, + 29576, 14108, 29757, 13720, + 29934, 13329, 30107, 12937, + 30274, 12540, 30435, 12142, + 30592, 11744, 30743, 11342, + 30889, 10939, 31030, 10534, + 31164, 10127, 31294, 9718, + 31418, 9307, 31537, 8895, + 31652, 8482, 31759, 8067, + 31862, 7650, 31960, 7233, + 32051, 6815, 32138, 6393, + 32219, 5973, 32294, 5549, + 32364, 5127, 32429, 4703, + 32487, 4278, 32541, 3852, + 32588, 3426, 32630, 2999, + 32667, 2572, 32698, 2144, + 32724, 1716, 32742, 1287, + 32757, 860, 32766, 430 + }; + + static final short[] fft_bitrev480 = { + 0, 96, 192, 288, 384, 32, 128, 224, 320, 416, 64, 160, 256, 352, 448, + 8, 104, 200, 296, 392, 40, 136, 232, 328, 424, 72, 168, 264, 360, 456, + 16, 112, 208, 304, 400, 48, 144, 240, 336, 432, 80, 176, 272, 368, 464, + 24, 120, 216, 312, 408, 56, 152, 248, 344, 440, 88, 184, 280, 376, 472, + 4, 100, 196, 292, 388, 36, 132, 228, 324, 420, 68, 164, 260, 356, 452, + 12, 108, 204, 300, 396, 44, 140, 236, 332, 428, 76, 172, 268, 364, 460, + 20, 116, 212, 308, 404, 52, 148, 244, 340, 436, 84, 180, 276, 372, 468, + 28, 124, 220, 316, 412, 60, 156, 252, 348, 444, 92, 188, 284, 380, 476, + 1, 97, 193, 289, 385, 33, 129, 225, 321, 417, 65, 161, 257, 353, 449, + 9, 105, 201, 297, 393, 41, 137, 233, 329, 425, 73, 169, 265, 361, 457, + 17, 113, 209, 305, 401, 49, 145, 241, 337, 433, 81, 177, 273, 369, 465, + 25, 121, 217, 313, 409, 57, 153, 249, 345, 441, 89, 185, 281, 377, 473, + 5, 101, 197, 293, 389, 37, 133, 229, 325, 421, 69, 165, 261, 357, 453, + 13, 109, 205, 301, 397, 45, 141, 237, 333, 429, 77, 173, 269, 365, 461, + 21, 117, 213, 309, 405, 53, 149, 245, 341, 437, 85, 181, 277, 373, 469, + 29, 125, 221, 317, 413, 61, 157, 253, 349, 445, 93, 189, 285, 381, 477, + 2, 98, 194, 290, 386, 34, 130, 226, 322, 418, 66, 162, 258, 354, 450, + 10, 106, 202, 298, 394, 42, 138, 234, 330, 426, 74, 170, 266, 362, 458, + 18, 114, 210, 306, 402, 50, 146, 242, 338, 434, 82, 178, 274, 370, 466, + 26, 122, 218, 314, 410, 58, 154, 250, 346, 442, 90, 186, 282, 378, 474, + 6, 102, 198, 294, 390, 38, 134, 230, 326, 422, 70, 166, 262, 358, 454, + 14, 110, 206, 302, 398, 46, 142, 238, 334, 430, 78, 174, 270, 366, 462, + 22, 118, 214, 310, 406, 54, 150, 246, 342, 438, 86, 182, 278, 374, 470, + 30, 126, 222, 318, 414, 62, 158, 254, 350, 446, 94, 190, 286, 382, 478, + 3, 99, 195, 291, 387, 35, 131, 227, 323, 419, 67, 163, 259, 355, 451, + 11, 107, 203, 299, 395, 43, 139, 235, 331, 427, 75, 171, 267, 363, 459, + 19, 115, 211, 307, 403, 51, 147, 243, 339, 435, 83, 179, 275, 371, 467, + 27, 123, 219, 315, 411, 59, 155, 251, 347, 443, 91, 187, 283, 379, 475, + 7, 103, 199, 295, 391, 39, 135, 231, 327, 423, 71, 167, 263, 359, 455, + 15, 111, 207, 303, 399, 47, 143, 239, 335, 431, 79, 175, 271, 367, 463, + 23, 119, 215, 311, 407, 55, 151, 247, 343, 439, 87, 183, 279, 375, 471, + 31, 127, 223, 319, 415, 63, 159, 255, 351, 447, 95, 191, 287, 383, 479,}; + + static final short[] fft_bitrev240 = { + 0, 48, 96, 144, 192, 16, 64, 112, 160, 208, 32, 80, 128, 176, 224, + 4, 52, 100, 148, 196, 20, 68, 116, 164, 212, 36, 84, 132, 180, 228, + 8, 56, 104, 152, 200, 24, 72, 120, 168, 216, 40, 88, 136, 184, 232, + 12, 60, 108, 156, 204, 28, 76, 124, 172, 220, 44, 92, 140, 188, 236, + 1, 49, 97, 145, 193, 17, 65, 113, 161, 209, 33, 81, 129, 177, 225, + 5, 53, 101, 149, 197, 21, 69, 117, 165, 213, 37, 85, 133, 181, 229, + 9, 57, 105, 153, 201, 25, 73, 121, 169, 217, 41, 89, 137, 185, 233, + 13, 61, 109, 157, 205, 29, 77, 125, 173, 221, 45, 93, 141, 189, 237, + 2, 50, 98, 146, 194, 18, 66, 114, 162, 210, 34, 82, 130, 178, 226, + 6, 54, 102, 150, 198, 22, 70, 118, 166, 214, 38, 86, 134, 182, 230, + 10, 58, 106, 154, 202, 26, 74, 122, 170, 218, 42, 90, 138, 186, 234, + 14, 62, 110, 158, 206, 30, 78, 126, 174, 222, 46, 94, 142, 190, 238, + 3, 51, 99, 147, 195, 19, 67, 115, 163, 211, 35, 83, 131, 179, 227, + 7, 55, 103, 151, 199, 23, 71, 119, 167, 215, 39, 87, 135, 183, 231, + 11, 59, 107, 155, 203, 27, 75, 123, 171, 219, 43, 91, 139, 187, 235, + 15, 63, 111, 159, 207, 31, 79, 127, 175, 223, 47, 95, 143, 191, 239,}; + + static final short[] fft_bitrev120 = { + 0, 24, 48, 72, 96, 8, 32, 56, 80, 104, 16, 40, 64, 88, 112, + 4, 28, 52, 76, 100, 12, 36, 60, 84, 108, 20, 44, 68, 92, 116, + 1, 25, 49, 73, 97, 9, 33, 57, 81, 105, 17, 41, 65, 89, 113, + 5, 29, 53, 77, 101, 13, 37, 61, 85, 109, 21, 45, 69, 93, 117, + 2, 26, 50, 74, 98, 10, 34, 58, 82, 106, 18, 42, 66, 90, 114, + 6, 30, 54, 78, 102, 14, 38, 62, 86, 110, 22, 46, 70, 94, 118, + 3, 27, 51, 75, 99, 11, 35, 59, 83, 107, 19, 43, 67, 91, 115, + 7, 31, 55, 79, 103, 15, 39, 63, 87, 111, 23, 47, 71, 95, 119,}; + + static final short[] fft_bitrev60 = { + 0, 12, 24, 36, 48, 4, 16, 28, 40, 52, 8, 20, 32, 44, 56, + 1, 13, 25, 37, 49, 5, 17, 29, 41, 53, 9, 21, 33, 45, 57, + 2, 14, 26, 38, 50, 6, 18, 30, 42, 54, 10, 22, 34, 46, 58, + 3, 15, 27, 39, 51, 7, 19, 31, 43, 55, 11, 23, 35, 47, 59,}; + + static final FFTState fft_state48000_960_0 = new FFTState(); + + static { + fft_state48000_960_0.nfft = 480; + fft_state48000_960_0.scale = 17476; + fft_state48000_960_0.scale_shift = 8; + fft_state48000_960_0.shift = -1; + fft_state48000_960_0.factors = new short[]{5, 96, 3, 32, 4, 8, 2, 4, 4, 1, 0, 0, 0, 0, 0, 0,}; + fft_state48000_960_0.bitrev = fft_bitrev480; + fft_state48000_960_0.twiddles = fft_twiddles48000_960; + } + ; + + static final FFTState fft_state48000_960_1 = new FFTState(); + + static { + fft_state48000_960_1.nfft = 240; + fft_state48000_960_1.scale = 17476; + fft_state48000_960_1.scale_shift = 7; + fft_state48000_960_1.shift = 1; + fft_state48000_960_1.factors = new short[]{5, 48, 3, 16, 4, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0,}; + fft_state48000_960_1.bitrev = fft_bitrev240; + fft_state48000_960_1.twiddles = fft_twiddles48000_960; + } + ; + + static final FFTState fft_state48000_960_2 = new FFTState(); + + static { + fft_state48000_960_2.nfft = 120; + fft_state48000_960_2.scale = 17476; + fft_state48000_960_2.scale_shift = 6; + fft_state48000_960_2.shift = 2; + fft_state48000_960_2.factors = new short[]{5, 24, 3, 8, 2, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0,}; + fft_state48000_960_2.bitrev = fft_bitrev120; + fft_state48000_960_2.twiddles = fft_twiddles48000_960; + } + ; + + static final FFTState fft_state48000_960_3 = new FFTState(); + + static { + fft_state48000_960_3.nfft = 60; + fft_state48000_960_3.scale = 17476; + fft_state48000_960_3.scale_shift = 5; + fft_state48000_960_3.shift = 3; + fft_state48000_960_3.factors = new short[]{5, 12, 3, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}; + fft_state48000_960_3.bitrev = fft_bitrev60; + fft_state48000_960_3.twiddles = fft_twiddles48000_960; + } + ; + + static final short[] mdct_twiddles960 /*1800*/ = { + 32767, 32767, 32767, 32766, 32765, + 32763, 32761, 32759, 32756, 32753, + 32750, 32746, 32742, 32738, 32733, + 32728, 32722, 32717, 32710, 32704, + 32697, 32690, 32682, 32674, 32666, + 32657, 32648, 32639, 32629, 32619, + 32609, 32598, 32587, 32576, 32564, + 32552, 32539, 32526, 32513, 32500, + 32486, 32472, 32457, 32442, 32427, + 32411, 32395, 32379, 32362, 32345, + 32328, 32310, 32292, 32274, 32255, + 32236, 32217, 32197, 32177, 32157, + 32136, 32115, 32093, 32071, 32049, + 32027, 32004, 31981, 31957, 31933, + 31909, 31884, 31859, 31834, 31809, + 31783, 31756, 31730, 31703, 31676, + 31648, 31620, 31592, 31563, 31534, + 31505, 31475, 31445, 31415, 31384, + 31353, 31322, 31290, 31258, 31226, + 31193, 31160, 31127, 31093, 31059, + 31025, 30990, 30955, 30920, 30884, + 30848, 30812, 30775, 30738, 30701, + 30663, 30625, 30587, 30548, 30509, + 30470, 30430, 30390, 30350, 30309, + 30269, 30227, 30186, 30144, 30102, + 30059, 30016, 29973, 29930, 29886, + 29842, 29797, 29752, 29707, 29662, + 29616, 29570, 29524, 29477, 29430, + 29383, 29335, 29287, 29239, 29190, + 29142, 29092, 29043, 28993, 28943, + 28892, 28842, 28791, 28739, 28688, + 28636, 28583, 28531, 28478, 28425, + 28371, 28317, 28263, 28209, 28154, + 28099, 28044, 27988, 27932, 27876, + 27820, 27763, 27706, 27648, 27591, + 27533, 27474, 27416, 27357, 27298, + 27238, 27178, 27118, 27058, 26997, + 26936, 26875, 26814, 26752, 26690, + 26628, 26565, 26502, 26439, 26375, + 26312, 26247, 26183, 26119, 26054, + 25988, 25923, 25857, 25791, 25725, + 25658, 25592, 25524, 25457, 25389, + 25322, 25253, 25185, 25116, 25047, + 24978, 24908, 24838, 24768, 24698, + 24627, 24557, 24485, 24414, 24342, + 24270, 24198, 24126, 24053, 23980, + 23907, 23834, 23760, 23686, 23612, + 23537, 23462, 23387, 23312, 23237, + 23161, 23085, 23009, 22932, 22856, + 22779, 22701, 22624, 22546, 22468, + 22390, 22312, 22233, 22154, 22075, + 21996, 21916, 21836, 21756, 21676, + 21595, 21515, 21434, 21352, 21271, + 21189, 21107, 21025, 20943, 20860, + 20777, 20694, 20611, 20528, 20444, + 20360, 20276, 20192, 20107, 20022, + 19937, 19852, 19767, 19681, 19595, + 19509, 19423, 19336, 19250, 19163, + 19076, 18988, 18901, 18813, 18725, + 18637, 18549, 18460, 18372, 18283, + 18194, 18104, 18015, 17925, 17835, + 17745, 17655, 17565, 17474, 17383, + 17292, 17201, 17110, 17018, 16927, + 16835, 16743, 16650, 16558, 16465, + 16372, 16279, 16186, 16093, 15999, + 15906, 15812, 15718, 15624, 15529, + 15435, 15340, 15245, 15150, 15055, + 14960, 14864, 14769, 14673, 14577, + 14481, 14385, 14288, 14192, 14095, + 13998, 13901, 13804, 13706, 13609, + 13511, 13414, 13316, 13218, 13119, + 13021, 12923, 12824, 12725, 12626, + 12527, 12428, 12329, 12230, 12130, + 12030, 11930, 11831, 11730, 11630, + 11530, 11430, 11329, 11228, 11128, + 11027, 10926, 10824, 10723, 10622, + 10520, 10419, 10317, 10215, 10113, + 10011, 9909, 9807, 9704, 9602, + 9499, 9397, 9294, 9191, 9088, + 8985, 8882, 8778, 8675, 8572, + 8468, 8364, 8261, 8157, 8053, + 7949, 7845, 7741, 7637, 7532, + 7428, 7323, 7219, 7114, 7009, + 6905, 6800, 6695, 6590, 6485, + 6380, 6274, 6169, 6064, 5958, + 5853, 5747, 5642, 5536, 5430, + 5325, 5219, 5113, 5007, 4901, + 4795, 4689, 4583, 4476, 4370, + 4264, 4157, 4051, 3945, 3838, + 3732, 3625, 3518, 3412, 3305, + 3198, 3092, 2985, 2878, 2771, + 2664, 2558, 2451, 2344, 2237, + 2130, 2023, 1916, 1809, 1702, + 1594, 1487, 1380, 1273, 1166, + 1059, 952, 844, 737, 630, + 523, 416, 308, 201, 94, + -13, -121, -228, -335, -442, + -550, -657, -764, -871, -978, + -1086, -1193, -1300, -1407, -1514, + -1621, -1728, -1835, -1942, -2049, + -2157, -2263, -2370, -2477, -2584, + -2691, -2798, -2905, -3012, -3118, + -3225, -3332, -3439, -3545, -3652, + -3758, -3865, -3971, -4078, -4184, + -4290, -4397, -4503, -4609, -4715, + -4821, -4927, -5033, -5139, -5245, + -5351, -5457, -5562, -5668, -5774, + -5879, -5985, -6090, -6195, -6301, + -6406, -6511, -6616, -6721, -6826, + -6931, -7036, -7140, -7245, -7349, + -7454, -7558, -7663, -7767, -7871, + -7975, -8079, -8183, -8287, -8390, + -8494, -8597, -8701, -8804, -8907, + -9011, -9114, -9217, -9319, -9422, + -9525, -9627, -9730, -9832, -9934, + -10037, -10139, -10241, -10342, -10444, + -10546, -10647, -10748, -10850, -10951, + -11052, -11153, -11253, -11354, -11455, + -11555, -11655, -11756, -11856, -11955, + -12055, -12155, -12254, -12354, -12453, + -12552, -12651, -12750, -12849, -12947, + -13046, -13144, -13242, -13340, -13438, + -13536, -13633, -13731, -13828, -13925, + -14022, -14119, -14216, -14312, -14409, + -14505, -14601, -14697, -14793, -14888, + -14984, -15079, -15174, -15269, -15364, + -15459, -15553, -15647, -15741, -15835, + -15929, -16023, -16116, -16210, -16303, + -16396, -16488, -16581, -16673, -16766, + -16858, -16949, -17041, -17133, -17224, + -17315, -17406, -17497, -17587, -17678, + -17768, -17858, -17948, -18037, -18127, + -18216, -18305, -18394, -18483, -18571, + -18659, -18747, -18835, -18923, -19010, + -19098, -19185, -19271, -19358, -19444, + -19531, -19617, -19702, -19788, -19873, + -19959, -20043, -20128, -20213, -20297, + -20381, -20465, -20549, -20632, -20715, + -20798, -20881, -20963, -21046, -21128, + -21210, -21291, -21373, -21454, -21535, + -21616, -21696, -21776, -21856, -21936, + -22016, -22095, -22174, -22253, -22331, + -22410, -22488, -22566, -22643, -22721, + -22798, -22875, -22951, -23028, -23104, + -23180, -23256, -23331, -23406, -23481, + -23556, -23630, -23704, -23778, -23852, + -23925, -23998, -24071, -24144, -24216, + -24288, -24360, -24432, -24503, -24574, + -24645, -24716, -24786, -24856, -24926, + -24995, -25064, -25133, -25202, -25270, + -25339, -25406, -25474, -25541, -25608, + -25675, -25742, -25808, -25874, -25939, + -26005, -26070, -26135, -26199, -26264, + -26327, -26391, -26455, -26518, -26581, + -26643, -26705, -26767, -26829, -26891, + -26952, -27013, -27073, -27133, -27193, + -27253, -27312, -27372, -27430, -27489, + -27547, -27605, -27663, -27720, -27777, + -27834, -27890, -27946, -28002, -28058, + -28113, -28168, -28223, -28277, -28331, + -28385, -28438, -28491, -28544, -28596, + -28649, -28701, -28752, -28803, -28854, + -28905, -28955, -29006, -29055, -29105, + -29154, -29203, -29251, -29299, -29347, + -29395, -29442, -29489, -29535, -29582, + -29628, -29673, -29719, -29764, -29808, + -29853, -29897, -29941, -29984, -30027, + -30070, -30112, -30154, -30196, -30238, + -30279, -30320, -30360, -30400, -30440, + -30480, -30519, -30558, -30596, -30635, + -30672, -30710, -30747, -30784, -30821, + -30857, -30893, -30929, -30964, -30999, + -31033, -31068, -31102, -31135, -31168, + -31201, -31234, -31266, -31298, -31330, + -31361, -31392, -31422, -31453, -31483, + -31512, -31541, -31570, -31599, -31627, + -31655, -31682, -31710, -31737, -31763, + -31789, -31815, -31841, -31866, -31891, + -31915, -31939, -31963, -31986, -32010, + -32032, -32055, -32077, -32099, -32120, + -32141, -32162, -32182, -32202, -32222, + -32241, -32260, -32279, -32297, -32315, + -32333, -32350, -32367, -32383, -32399, + -32415, -32431, -32446, -32461, -32475, + -32489, -32503, -32517, -32530, -32542, + -32555, -32567, -32579, -32590, -32601, + -32612, -32622, -32632, -32641, -32651, + -32659, -32668, -32676, -32684, -32692, + -32699, -32706, -32712, -32718, -32724, + -32729, -32734, -32739, -32743, -32747, + -32751, -32754, -32757, -32760, -32762, + -32764, -32765, -32767, -32767, -32767, + 32767, 32767, 32765, 32761, 32756, + 32750, 32742, 32732, 32722, 32710, + 32696, 32681, 32665, 32647, 32628, + 32608, 32586, 32562, 32538, 32512, + 32484, 32455, 32425, 32393, 32360, + 32326, 32290, 32253, 32214, 32174, + 32133, 32090, 32046, 32001, 31954, + 31906, 31856, 31805, 31753, 31700, + 31645, 31588, 31530, 31471, 31411, + 31349, 31286, 31222, 31156, 31089, + 31020, 30951, 30880, 30807, 30733, + 30658, 30582, 30504, 30425, 30345, + 30263, 30181, 30096, 30011, 29924, + 29836, 29747, 29656, 29564, 29471, + 29377, 29281, 29184, 29086, 28987, + 28886, 28784, 28681, 28577, 28471, + 28365, 28257, 28147, 28037, 27925, + 27812, 27698, 27583, 27467, 27349, + 27231, 27111, 26990, 26868, 26744, + 26620, 26494, 26367, 26239, 26110, + 25980, 25849, 25717, 25583, 25449, + 25313, 25176, 25038, 24900, 24760, + 24619, 24477, 24333, 24189, 24044, + 23898, 23751, 23602, 23453, 23303, + 23152, 22999, 22846, 22692, 22537, + 22380, 22223, 22065, 21906, 21746, + 21585, 21423, 21261, 21097, 20933, + 20767, 20601, 20434, 20265, 20096, + 19927, 19756, 19584, 19412, 19239, + 19065, 18890, 18714, 18538, 18361, + 18183, 18004, 17824, 17644, 17463, + 17281, 17098, 16915, 16731, 16546, + 16361, 16175, 15988, 15800, 15612, + 15423, 15234, 15043, 14852, 14661, + 14469, 14276, 14083, 13889, 13694, + 13499, 13303, 13107, 12910, 12713, + 12515, 12317, 12118, 11918, 11718, + 11517, 11316, 11115, 10913, 10710, + 10508, 10304, 10100, 9896, 9691, + 9486, 9281, 9075, 8869, 8662, + 8455, 8248, 8040, 7832, 7623, + 7415, 7206, 6996, 6787, 6577, + 6366, 6156, 5945, 5734, 5523, + 5311, 5100, 4888, 4675, 4463, + 4251, 4038, 3825, 3612, 3399, + 3185, 2972, 2758, 2544, 2330, + 2116, 1902, 1688, 1474, 1260, + 1045, 831, 617, 402, 188, + -27, -241, -456, -670, -885, + -1099, -1313, -1528, -1742, -1956, + -2170, -2384, -2598, -2811, -3025, + -3239, -3452, -3665, -3878, -4091, + -4304, -4516, -4728, -4941, -5153, + -5364, -5576, -5787, -5998, -6209, + -6419, -6629, -6839, -7049, -7258, + -7467, -7676, -7884, -8092, -8300, + -8507, -8714, -8920, -9127, -9332, + -9538, -9743, -9947, -10151, -10355, + -10558, -10761, -10963, -11165, -11367, + -11568, -11768, -11968, -12167, -12366, + -12565, -12762, -12960, -13156, -13352, + -13548, -13743, -13937, -14131, -14324, + -14517, -14709, -14900, -15091, -15281, + -15470, -15659, -15847, -16035, -16221, + -16407, -16593, -16777, -16961, -17144, + -17326, -17508, -17689, -17869, -18049, + -18227, -18405, -18582, -18758, -18934, + -19108, -19282, -19455, -19627, -19799, + -19969, -20139, -20308, -20475, -20642, + -20809, -20974, -21138, -21301, -21464, + -21626, -21786, -21946, -22105, -22263, + -22420, -22575, -22730, -22884, -23037, + -23189, -23340, -23490, -23640, -23788, + -23935, -24080, -24225, -24369, -24512, + -24654, -24795, -24934, -25073, -25211, + -25347, -25482, -25617, -25750, -25882, + -26013, -26143, -26272, -26399, -26526, + -26651, -26775, -26898, -27020, -27141, + -27260, -27379, -27496, -27612, -27727, + -27841, -27953, -28065, -28175, -28284, + -28391, -28498, -28603, -28707, -28810, + -28911, -29012, -29111, -29209, -29305, + -29401, -29495, -29587, -29679, -29769, + -29858, -29946, -30032, -30118, -30201, + -30284, -30365, -30445, -30524, -30601, + -30677, -30752, -30825, -30897, -30968, + -31038, -31106, -31172, -31238, -31302, + -31365, -31426, -31486, -31545, -31602, + -31658, -31713, -31766, -31818, -31869, + -31918, -31966, -32012, -32058, -32101, + -32144, -32185, -32224, -32262, -32299, + -32335, -32369, -32401, -32433, -32463, + -32491, -32518, -32544, -32568, -32591, + -32613, -32633, -32652, -32669, -32685, + -32700, -32713, -32724, -32735, -32744, + -32751, -32757, -32762, -32766, -32767, + 32767, 32764, 32755, 32741, 32720, + 32694, 32663, 32626, 32583, 32535, + 32481, 32421, 32356, 32286, 32209, + 32128, 32041, 31948, 31850, 31747, + 31638, 31523, 31403, 31278, 31148, + 31012, 30871, 30724, 30572, 30415, + 30253, 30086, 29913, 29736, 29553, + 29365, 29172, 28974, 28771, 28564, + 28351, 28134, 27911, 27684, 27452, + 27216, 26975, 26729, 26478, 26223, + 25964, 25700, 25432, 25159, 24882, + 24601, 24315, 24026, 23732, 23434, + 23133, 22827, 22517, 22204, 21886, + 21565, 21240, 20912, 20580, 20244, + 19905, 19563, 19217, 18868, 18516, + 18160, 17802, 17440, 17075, 16708, + 16338, 15964, 15588, 15210, 14829, + 14445, 14059, 13670, 13279, 12886, + 12490, 12093, 11693, 11291, 10888, + 10482, 10075, 9666, 9255, 8843, + 8429, 8014, 7597, 7180, 6760, + 6340, 5919, 5496, 5073, 4649, + 4224, 3798, 3372, 2945, 2517, + 2090, 1661, 1233, 804, 375, + -54, -483, -911, -1340, -1768, + -2197, -2624, -3052, -3479, -3905, + -4330, -4755, -5179, -5602, -6024, + -6445, -6865, -7284, -7702, -8118, + -8533, -8946, -9358, -9768, -10177, + -10584, -10989, -11392, -11793, -12192, + -12589, -12984, -13377, -13767, -14155, + -14541, -14924, -15305, -15683, -16058, + -16430, -16800, -17167, -17531, -17892, + -18249, -18604, -18956, -19304, -19649, + -19990, -20329, -20663, -20994, -21322, + -21646, -21966, -22282, -22595, -22904, + -23208, -23509, -23806, -24099, -24387, + -24672, -24952, -25228, -25499, -25766, + -26029, -26288, -26541, -26791, -27035, + -27275, -27511, -27741, -27967, -28188, + -28405, -28616, -28823, -29024, -29221, + -29412, -29599, -29780, -29957, -30128, + -30294, -30455, -30611, -30761, -30906, + -31046, -31181, -31310, -31434, -31552, + -31665, -31773, -31875, -31972, -32063, + -32149, -32229, -32304, -32373, -32437, + -32495, -32547, -32594, -32635, -32671, + -32701, -32726, -32745, -32758, -32766, + 32767, 32754, 32717, 32658, 32577, + 32473, 32348, 32200, 32029, 31837, + 31624, 31388, 31131, 30853, 30553, + 30232, 29891, 29530, 29148, 28746, + 28324, 27883, 27423, 26944, 26447, + 25931, 25398, 24847, 24279, 23695, + 23095, 22478, 21846, 21199, 20538, + 19863, 19174, 18472, 17757, 17030, + 16291, 15541, 14781, 14010, 13230, + 12441, 11643, 10837, 10024, 9204, + 8377, 7545, 6708, 5866, 5020, + 4171, 3319, 2464, 1608, 751, + -107, -965, -1822, -2678, -3532, + -4383, -5232, -6077, -6918, -7754, + -8585, -9409, -10228, -11039, -11843, + -12639, -13426, -14204, -14972, -15730, + -16477, -17213, -17937, -18648, -19347, + -20033, -20705, -21363, -22006, -22634, + -23246, -23843, -24423, -24986, -25533, + -26062, -26573, -27066, -27540, -27995, + -28431, -28848, -29245, -29622, -29979, + -30315, -30630, -30924, -31197, -31449, + -31679, -31887, -32074, -32239, -32381, + -32501, -32600, -32675, -32729, -32759,}; + + static final int[] intensity_thresholds + = /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 off*/ {1, 2, 3, 4, 5, 6, 7, 8, 16, 24, 36, 44, 50, 56, 62, 67, 72, 79, 88, 106, 134}; + static final int[] intensity_histeresis + = {1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 5, 6, 8, 8}; +} diff --git a/app/src/main/java/org/concentus/ChannelLayout.java b/app/src/main/java/org/concentus/ChannelLayout.java new file mode 100644 index 0000000000..f7b580aa9c --- /dev/null +++ b/app/src/main/java/org/concentus/ChannelLayout.java @@ -0,0 +1,50 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class ChannelLayout { + + int nb_channels; + int nb_streams; + int nb_coupled_streams; + final short[] mapping = new short[256]; + + void Reset() { + nb_channels = 0; + nb_streams = 0; + nb_coupled_streams = 0; + Arrays.MemSet(mapping, (short) 0); + } +} diff --git a/app/src/main/java/org/concentus/CodeSigns.java b/app/src/main/java/org/concentus/CodeSigns.java new file mode 100644 index 0000000000..0cc94005b2 --- /dev/null +++ b/app/src/main/java/org/concentus/CodeSigns.java @@ -0,0 +1,130 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class CodeSigns { + + private static int silk_enc_map(int a) { + return (Inlines.silk_RSHIFT((a), 15) + 1); + } + + private static int silk_dec_map(int a) { + return (Inlines.silk_LSHIFT((a), 1) - 1); + } + + /// + /// Encodes signs of excitation + /// + /// I/O Compressor data structure + /// I pulse signal + /// I length of input + /// I Signal type + /// I Quantization offset type + /// I Sum of absolute pulses per block [MAX_NB_SHELL_BLOCKS] + static void silk_encode_signs( + EntropyCoder psRangeEnc, + byte[] pulses, + int length, + int signalType, + int quantOffsetType, + int[] sum_pulses) { + int i, j, p; + short[] icdf = new short[2]; + int q_ptr; + short[] sign_icdf = SilkTables.silk_sign_iCDF; + int icdf_ptr; + + icdf[1] = 0; + q_ptr = 0; + i = Inlines.silk_SMULBB(7, Inlines.silk_ADD_LSHIFT(quantOffsetType, signalType, 1)); + icdf_ptr = i; + length = Inlines.silk_RSHIFT(length + (SilkConstants.SHELL_CODEC_FRAME_LENGTH / 2), SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH); + for (i = 0; i < length; i++) { + p = sum_pulses[i]; + if (p > 0) { + icdf[0] = sign_icdf[icdf_ptr + Inlines.silk_min(p & 0x1F, 6)]; + for (j = q_ptr; j < q_ptr + SilkConstants.SHELL_CODEC_FRAME_LENGTH; j++) { + if (pulses[j] != 0) { + psRangeEnc.enc_icdf(silk_enc_map(pulses[j]), icdf, 8); + } + } + } + + q_ptr += SilkConstants.SHELL_CODEC_FRAME_LENGTH; + } + } + + /// + /// Decodes signs of excitation + /// + /// I/O Compressor data structure + /// I/O pulse signal + /// I length of input + /// I Signal type + /// I Quantization offset type + /// I Sum of absolute pulses per block [MAX_NB_SHELL_BLOCKS] + static void silk_decode_signs( + EntropyCoder psRangeDec, + short[] pulses, + int length, + int signalType, + int quantOffsetType, + int[] sum_pulses) { + int i, j, p; + short[] icdf = new short[2]; + int q_ptr; + short[] icdf_table = SilkTables.silk_sign_iCDF; + int icdf_ptr; + + icdf[1] = 0; + q_ptr = 0; + i = Inlines.silk_SMULBB(7, Inlines.silk_ADD_LSHIFT(quantOffsetType, signalType, 1)); + icdf_ptr = i; + length = Inlines.silk_RSHIFT(length + SilkConstants.SHELL_CODEC_FRAME_LENGTH / 2, SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH); + + for (i = 0; i < length; i++) { + p = sum_pulses[i]; + + if (p > 0) { + icdf[0] = icdf_table[icdf_ptr + Inlines.silk_min(p & 0x1F, 6)]; + for (j = 0; j < SilkConstants.SHELL_CODEC_FRAME_LENGTH; j++) { + if (pulses[q_ptr + j] > 0) { + /* attach sign */ + pulses[q_ptr + j] *= (short) (silk_dec_map(psRangeDec.dec_icdf(icdf, 8))); + } + } + } + + q_ptr += SilkConstants.SHELL_CODEC_FRAME_LENGTH; + } + } +} diff --git a/app/src/main/java/org/concentus/CodecHelpers.java b/app/src/main/java/org/concentus/CodecHelpers.java new file mode 100644 index 0000000000..84d4846ffe --- /dev/null +++ b/app/src/main/java/org/concentus/CodecHelpers.java @@ -0,0 +1,631 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +public class CodecHelpers { + + static byte gen_toc(OpusMode mode, int framerate, OpusBandwidth bandwidth, int channels) { + int period; + short toc; + period = 0; + while (framerate < 400) { + framerate <<= 1; + period++; + } + if (mode == OpusMode.MODE_SILK_ONLY) { + toc = (short) ((OpusBandwidthHelpers.GetOrdinal(bandwidth) - OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND)) << 5); + toc |= (short) ((period - 2) << 3); + } else if (mode == OpusMode.MODE_CELT_ONLY) { + int tmp = OpusBandwidthHelpers.GetOrdinal(bandwidth) - OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND); + if (tmp < 0) { + tmp = 0; + } + toc = 0x80; + toc |= (short) (tmp << 5); + toc |= (short) (period << 3); + } else /* Hybrid */ { + toc = 0x60; + toc |= (short) ((OpusBandwidthHelpers.GetOrdinal(bandwidth) - OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND)) << 4); + toc |= (short) ((period - 2) << 3); + } + toc |= (short) ((channels == 2 ? 1 : 0) << 2); + return (byte) (0xFF & toc); + } + + static void hp_cutoff(short[] input, int input_ptr, int cutoff_Hz, short[] output, int output_ptr, int[] hp_mem, int len, int channels, int Fs) { + int[] B_Q28 = new int[3]; + int[] A_Q28 = new int[2]; + int Fc_Q19, r_Q28, r_Q22; + + Inlines.OpusAssert(cutoff_Hz <= Integer.MAX_VALUE / ((int) ((1.5f * 3.14159f / 1000) * ((long) 1 << (19)) + 0.5))/*Inlines.SILK_CONST(1.5f * 3.14159f / 1000, 19)*/); + Fc_Q19 = Inlines.silk_DIV32_16(Inlines.silk_SMULBB(((int) ((1.5f * 3.14159f / 1000) * ((long) 1 << (19)) + 0.5))/*Inlines.SILK_CONST(1.5f * 3.14159f / 1000, 19)*/, cutoff_Hz), Fs / 1000); + Inlines.OpusAssert(Fc_Q19 > 0 && Fc_Q19 < 32768); + + r_Q28 = ((int) ((1.0f) * ((long) 1 << (28)) + 0.5))/*Inlines.SILK_CONST(1.0f, 28)*/ - Inlines.silk_MUL(((int) ((0.92f) * ((long) 1 << (9)) + 0.5))/*Inlines.SILK_CONST(0.92f, 9)*/, Fc_Q19); + + /* b = r * [ 1; -2; 1 ]; */ + /* a = [ 1; -2 * r * ( 1 - 0.5 * Fc^2 ); r^2 ]; */ + B_Q28[0] = r_Q28; + B_Q28[1] = Inlines.silk_LSHIFT(-r_Q28, 1); + B_Q28[2] = r_Q28; + + /* -r * ( 2 - Fc * Fc ); */ + r_Q22 = Inlines.silk_RSHIFT(r_Q28, 6); + A_Q28[0] = Inlines.silk_SMULWW(r_Q22, Inlines.silk_SMULWW(Fc_Q19, Fc_Q19) - ((int) ((2.0f) * ((long) 1 << (22)) + 0.5))/*Inlines.SILK_CONST(2.0f, 22)*/); + A_Q28[1] = Inlines.silk_SMULWW(r_Q22, r_Q22); + + Filters.silk_biquad_alt(input, input_ptr, B_Q28, A_Q28, hp_mem, 0, output, output_ptr, len, channels); + if (channels == 2) { + Filters.silk_biquad_alt(input, input_ptr + 1, B_Q28, A_Q28, hp_mem, 2, output, output_ptr + 1, len, channels); + } + } + + static void dc_reject(short[] input, int input_ptr, int cutoff_Hz, short[] output, int output_ptr, int[] hp_mem, int len, int channels, int Fs) { + int c, i; + int shift; + + /* Approximates -round(log2(4.*cutoff_Hz/Fs)) */ + shift = Inlines.celt_ilog2(Fs / (cutoff_Hz * 3)); + for (c = 0; c < channels; c++) { + for (i = 0; i < len; i++) { + int x, tmp, y; + x = Inlines.SHL32(Inlines.EXTEND32(input[channels * i + c + input_ptr]), 15); + /* First stage */ + tmp = x - hp_mem[2 * c]; + hp_mem[2 * c] = hp_mem[2 * c] + Inlines.PSHR32(x - hp_mem[2 * c], shift); + /* Second stage */ + y = tmp - hp_mem[2 * c + 1]; + hp_mem[2 * c + 1] = hp_mem[2 * c + 1] + Inlines.PSHR32(tmp - hp_mem[2 * c + 1], shift); + output[channels * i + c + output_ptr] = Inlines.EXTRACT16(Inlines.SATURATE(Inlines.PSHR32(y, 15), 32767)); + } + } + } + + static void stereo_fade( + short[] pcm_buf, + int g1, + int g2, + int overlap48, + int frame_size, + int channels, + int[] window, + int Fs) { + int i; + int overlap; + int inc; + inc = 48000 / Fs; + overlap = overlap48 / inc; + g1 = CeltConstants.Q15ONE - g1; + g2 = CeltConstants.Q15ONE - g2; + for (i = 0; i < overlap; i++) { + int diff; + int g, w; + w = Inlines.MULT16_16_Q15(window[i * inc], window[i * inc]); + g = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(w, g2), + CeltConstants.Q15ONE - w, g1), 15); + diff = Inlines.EXTRACT16(Inlines.HALF32((int) pcm_buf[i * channels] - (int) pcm_buf[i * channels + 1])); + diff = Inlines.MULT16_16_Q15(g, diff); + pcm_buf[i * channels] = (short) (pcm_buf[i * channels] - diff); + pcm_buf[i * channels + 1] = (short) (pcm_buf[i * channels + 1] + diff); + } + for (; i < frame_size; i++) { + int diff; + diff = Inlines.EXTRACT16(Inlines.HALF32((int) pcm_buf[i * channels] - (int) pcm_buf[i * channels + 1])); + diff = Inlines.MULT16_16_Q15(g2, diff); + pcm_buf[i * channels] = (short) (pcm_buf[i * channels] - diff); + pcm_buf[i * channels + 1] = (short) (pcm_buf[i * channels + 1] + diff); + } + } + + static void gain_fade(short[] buffer, int buf_ptr, int g1, int g2, + int overlap48, int frame_size, int channels, int[] window, int Fs) { + int i; + int inc; + int overlap; + int c; + inc = 48000 / Fs; + overlap = overlap48 / inc; + if (channels == 1) { + for (i = 0; i < overlap; i++) { + int g, w; + w = Inlines.MULT16_16_Q15(window[i * inc], window[i * inc]); + g = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(w, g2), + CeltConstants.Q15ONE - w, g1), 15); + buffer[buf_ptr + i] = (short) Inlines.MULT16_16_Q15(g, buffer[buf_ptr + i]); + } + } else { + for (i = 0; i < overlap; i++) { + int g, w; + w = Inlines.MULT16_16_Q15(window[i * inc], window[i * inc]); + g = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(w, g2), + CeltConstants.Q15ONE - w, g1), 15); + buffer[buf_ptr + i * 2] = (short) Inlines.MULT16_16_Q15(g, buffer[buf_ptr + i * 2]); + buffer[buf_ptr + i * 2 + 1] = (short) Inlines.MULT16_16_Q15(g, buffer[buf_ptr + i * 2 + 1]); + } + } + c = 0; + do { + for (i = overlap; i < frame_size; i++) { + buffer[buf_ptr + i * channels + c] = (short) Inlines.MULT16_16_Q15(g2, buffer[buf_ptr + i * channels + c]); + } + } while (++c < channels); + } + + /* Don't use more than 60 ms for the frame size analysis */ + private final static int MAX_DYNAMIC_FRAMESIZE = 24; + + /* Estimates how much the bitrate will be boosted based on the sub-frame energy */ + static float transient_boost(float[] E, int E_ptr, float[] E_1, int LM, int maxM) { + int i; + int M; + float sumE = 0, sumE_1 = 0; + float metric; + + M = Inlines.IMIN(maxM, (1 << LM) + 1); + for (i = E_ptr; i < M + E_ptr; i++) { + sumE += E[i]; + sumE_1 += E_1[i]; + } + metric = sumE * sumE_1 / (M * M); + /*if (LM==3) + printf("%f\n", metric);*/ + /*return metric>10 ? 1 : 0;*/ + /*return Inlines.MAX16(0,1-exp(-.25*(metric-2.)));*/ + return Inlines.MIN16(1, (float) Math.sqrt(Inlines.MAX16(0, .05f * (metric - 2)))); + } + + /* Viterbi decoding trying to find the best frame size combination using look-ahead + + State numbering: + 0: unused + 1: 2.5 ms + 2: 5 ms (#1) + 3: 5 ms (#2) + 4: 10 ms (#1) + 5: 10 ms (#2) + 6: 10 ms (#3) + 7: 10 ms (#4) + 8: 20 ms (#1) + 9: 20 ms (#2) + 10: 20 ms (#3) + 11: 20 ms (#4) + 12: 20 ms (#5) + 13: 20 ms (#6) + 14: 20 ms (#7) + 15: 20 ms (#8) + */ + static int transient_viterbi(float[] E, float[] E_1, int N, int frame_cost, int rate) { + int i; + float[][] cost = Arrays.InitTwoDimensionalArrayFloat(MAX_DYNAMIC_FRAMESIZE, 16); + int[][] states = Arrays.InitTwoDimensionalArrayInt(MAX_DYNAMIC_FRAMESIZE, 16); + float best_cost; + int best_state; + float factor; + /* Take into account that we damp VBR in the 32 kb/s to 64 kb/s range. */ + if (rate < 80) { + factor = 0; + } else if (rate > 160) { + factor = 1; + } else { + factor = (rate - 80.0f) / 80.0f; + } + /* Makes variable framesize less aggressive at lower bitrates, but I can't + find any valid theoretical justification for this (other than it seems + to help) */ + for (i = 0; i < 16; i++) { + /* Impossible state */ + states[0][i] = -1; + cost[0][i] = 1e10f; + } + for (i = 0; i < 4; i++) { + cost[0][1 << i] = (frame_cost + rate * (1 << i)) * (1 + factor * transient_boost(E, 0, E_1, i, N + 1)); + states[0][1 << i] = i; + } + for (i = 1; i < N; i++) { + int j; + + /* Follow continuations */ + for (j = 2; j < 16; j++) { + cost[i][j] = cost[i - 1][j - 1]; + states[i][j] = j - 1; + } + + /* New frames */ + for (j = 0; j < 4; j++) { + int k; + float min_cost; + float curr_cost; + states[i][1 << j] = 1; + min_cost = cost[i - 1][1]; + for (k = 1; k < 4; k++) { + float tmp = cost[i - 1][(1 << (k + 1)) - 1]; + if (tmp < min_cost) { + states[i][1 << j] = (1 << (k + 1)) - 1; + min_cost = tmp; + } + } + curr_cost = (frame_cost + rate * (1 << j)) * (1 + factor * transient_boost(E, i, E_1, j, N - i + 1)); + cost[i][1 << j] = min_cost; + /* If part of the frame is outside the analysis window, only count part of the cost */ + if (N - i < (1 << j)) { + cost[i][1 << j] += curr_cost * (float) (N - i) / (1 << j); + } else { + cost[i][1 << j] += curr_cost; + } + } + } + + best_state = 1; + best_cost = cost[N - 1][1]; + /* Find best end state (doesn't force a frame to end at N-1) */ + for (i = 2; i < 16; i++) { + if (cost[N - 1][i] < best_cost) { + best_cost = cost[N - 1][i]; + best_state = i; + } + } + + /* Follow transitions back */ + for (i = N - 1; i >= 0; i--) { + /*printf("%d ", best_state);*/ + best_state = states[i][best_state]; + } + /*printf("%d\n", best_state);*/ + return best_state; + } + + static int optimize_framesize(short[] x, int x_ptr, int len, int C, int Fs, + int bitrate, int tonality, float[] mem, int buffering) { + int N; + int i; + float[] e = new float[MAX_DYNAMIC_FRAMESIZE + 4]; + float[] e_1 = new float[MAX_DYNAMIC_FRAMESIZE + 3]; + int memx; + int bestLM = 0; + int subframe; + int pos; + int offset; + int[] sub; + + subframe = Fs / 400; + sub = new int[subframe]; + e[0] = mem[0]; + e_1[0] = 1.0f / (CeltConstants.EPSILON + mem[0]); + if (buffering != 0) { + /* Consider the CELT delay when not in restricted-lowdelay */ + /* We assume the buffering is between 2.5 and 5 ms */ + offset = 2 * subframe - buffering; + Inlines.OpusAssert(offset >= 0 && offset <= subframe); + len -= offset; + e[1] = mem[1]; + e_1[1] = 1.0f / (CeltConstants.EPSILON + mem[1]); + e[2] = mem[2]; + e_1[2] = 1.0f / (CeltConstants.EPSILON + mem[2]); + pos = 3; + } else { + pos = 1; + offset = 0; + } + N = Inlines.IMIN(len / subframe, MAX_DYNAMIC_FRAMESIZE); + /* Just silencing a warning, it's really initialized later */ + memx = 0; + for (i = 0; i < N; i++) { + float tmp; + int tmpx; + int j; + tmp = CeltConstants.EPSILON; + + Downmix.downmix_int(x, x_ptr, sub, 0, subframe, i * subframe + offset, 0, -2, C); + if (i == 0) { + memx = sub[0]; + } + for (j = 0; j < subframe; j++) { + tmpx = sub[j]; + tmp += (tmpx - memx) * (float) (tmpx - memx); + memx = tmpx; + } + e[i + pos] = tmp; + e_1[i + pos] = 1.0f / tmp; + } + /* Hack to get 20 ms working with APPLICATION_AUDIO + The real problem is that the corresponding memory needs to use 1.5 ms + from this frame and 1 ms from the next frame */ + e[i + pos] = e[i + pos - 1]; + if (buffering != 0) { + N = Inlines.IMIN(MAX_DYNAMIC_FRAMESIZE, N + 2); + } + bestLM = transient_viterbi(e, e_1, N, (int) ((1.0f + .5f * tonality) * (60 * C + 40)), bitrate / 400); + mem[0] = e[1 << bestLM]; + if (buffering != 0) { + mem[1] = e[(1 << bestLM) + 1]; + mem[2] = e[(1 << bestLM) + 2]; + } + return bestLM; + } + + static int frame_size_select(int frame_size, OpusFramesize variable_duration, int Fs) { + int new_size; + if (frame_size < Fs / 400) { + return -1; + } + if (variable_duration == OpusFramesize.OPUS_FRAMESIZE_ARG) { + new_size = frame_size; + } else if (variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE) { + new_size = Fs / 50; + } else if (OpusFramesizeHelpers.GetOrdinal(variable_duration) >= OpusFramesizeHelpers.GetOrdinal(OpusFramesize.OPUS_FRAMESIZE_2_5_MS) + && OpusFramesizeHelpers.GetOrdinal(variable_duration) <= OpusFramesizeHelpers.GetOrdinal(OpusFramesize.OPUS_FRAMESIZE_60_MS)) { + new_size = Inlines.IMIN(3 * Fs / 50, (Fs / 400) << (OpusFramesizeHelpers.GetOrdinal(variable_duration) - OpusFramesizeHelpers.GetOrdinal(OpusFramesize.OPUS_FRAMESIZE_2_5_MS))); + } else { + return -1; + } + if (new_size > frame_size) { + return -1; + } + if (400 * new_size != Fs && 200 * new_size != Fs && 100 * new_size != Fs + && 50 * new_size != Fs && 25 * new_size != Fs && 50 * new_size != 3 * Fs) { + return -1; + } + return new_size; + } + + static int compute_frame_size(short[] analysis_pcm, int analysis_pcm_ptr, int frame_size, + OpusFramesize variable_duration, int C, int Fs, int bitrate_bps, + int delay_compensation, float[] subframe_mem, boolean analysis_enabled + ) { + + if (analysis_enabled && variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE && frame_size >= Fs / 200) { + int LM = 3; + LM = optimize_framesize(analysis_pcm, analysis_pcm_ptr, frame_size, C, Fs, bitrate_bps, + 0, subframe_mem, delay_compensation); + while ((Fs / 400 << LM) > frame_size) { + LM--; + } + frame_size = (Fs / 400 << LM); + } else { + frame_size = frame_size_select(frame_size, variable_duration, Fs); + } + + if (frame_size < 0) { + return -1; + } + return frame_size; + } + + static int compute_stereo_width(short[] pcm, int pcm_ptr, int frame_size, int Fs, StereoWidthState mem) { + int corr; + int ldiff; + int width; + int xx, xy, yy; + int sqrt_xx, sqrt_yy; + int qrrt_xx, qrrt_yy; + int frame_rate; + int i; + int short_alpha; + + frame_rate = Fs / frame_size; + short_alpha = CeltConstants.Q15ONE - (25 * CeltConstants.Q15ONE / Inlines.IMAX(50, frame_rate)); + xx = xy = yy = 0; + for (i = 0; i < frame_size - 3; i += 4) { + int pxx = 0; + int pxy = 0; + int pyy = 0; + int x, y; + int p2i = pcm_ptr + (2 * i); + x = pcm[p2i]; + y = pcm[p2i + 1]; + pxx = Inlines.SHR32(Inlines.MULT16_16(x, x), 2); + pxy = Inlines.SHR32(Inlines.MULT16_16(x, y), 2); + pyy = Inlines.SHR32(Inlines.MULT16_16(y, y), 2); + x = pcm[p2i + 2]; + y = pcm[p2i + 3]; + pxx += Inlines.SHR32(Inlines.MULT16_16(x, x), 2); + pxy += Inlines.SHR32(Inlines.MULT16_16(x, y), 2); + pyy += Inlines.SHR32(Inlines.MULT16_16(y, y), 2); + x = pcm[p2i + 4]; + y = pcm[p2i + 5]; + pxx += Inlines.SHR32(Inlines.MULT16_16(x, x), 2); + pxy += Inlines.SHR32(Inlines.MULT16_16(x, y), 2); + pyy += Inlines.SHR32(Inlines.MULT16_16(y, y), 2); + x = pcm[p2i + 6]; + y = pcm[p2i + 7]; + pxx += Inlines.SHR32(Inlines.MULT16_16(x, x), 2); + pxy += Inlines.SHR32(Inlines.MULT16_16(x, y), 2); + pyy += Inlines.SHR32(Inlines.MULT16_16(y, y), 2); + + xx += Inlines.SHR32(pxx, 10); + xy += Inlines.SHR32(pxy, 10); + yy += Inlines.SHR32(pyy, 10); + } + + mem.XX += Inlines.MULT16_32_Q15(short_alpha, xx - mem.XX); + mem.XY += Inlines.MULT16_32_Q15(short_alpha, xy - mem.XY); + mem.YY += Inlines.MULT16_32_Q15(short_alpha, yy - mem.YY); + mem.XX = Inlines.MAX32(0, mem.XX); + mem.XY = Inlines.MAX32(0, mem.XY); + mem.YY = Inlines.MAX32(0, mem.YY); + if (Inlines.MAX32(mem.XX, mem.YY) > ((short) (0.5 + (8e-4f) * (((int) 1) << (18))))/*Inlines.QCONST16(8e-4f, 18)*/) { + sqrt_xx = Inlines.celt_sqrt(mem.XX); + sqrt_yy = Inlines.celt_sqrt(mem.YY); + qrrt_xx = Inlines.celt_sqrt(sqrt_xx); + qrrt_yy = Inlines.celt_sqrt(sqrt_yy); + /* Inter-channel correlation */ + mem.XY = Inlines.MIN32(mem.XY, sqrt_xx * sqrt_yy); + corr = Inlines.SHR32(Inlines.frac_div32(mem.XY, CeltConstants.EPSILON + Inlines.MULT16_16(sqrt_xx, sqrt_yy)), 16); + /* Approximate loudness difference */ + ldiff = CeltConstants.Q15ONE * Inlines.ABS16(qrrt_xx - qrrt_yy) / (CeltConstants.EPSILON + qrrt_xx + qrrt_yy); + width = Inlines.MULT16_16_Q15(Inlines.celt_sqrt(((int) (0.5 + (1.0f) * (((int) 1) << (30))))/*Inlines.QCONST32(1.0f, 30)*/ - Inlines.MULT16_16(corr, corr)), ldiff); + /* Smoothing over one second */ + mem.smoothed_width += (width - mem.smoothed_width) / frame_rate; + /* Peak follower */ + mem.max_follower = Inlines.MAX16(mem.max_follower - ((short) (0.5 + (.02f) * (((int) 1) << (15))))/*Inlines.QCONST16(.02f, 15)*/ / frame_rate, mem.smoothed_width); + } else { + width = 0; + corr = CeltConstants.Q15ONE; + ldiff = 0; + } + /*printf("%f %f %f %f %f ", corr/(float)1.0f, ldiff/(float)1.0f, width/(float)1.0f, mem.smoothed_width/(float)1.0f, mem.max_follower/(float)1.0f);*/ + return Inlines.EXTRACT16(Inlines.MIN32(CeltConstants.Q15ONE, 20 * mem.max_follower)); + } + + static void smooth_fade(short[] in1, int in1_ptr, short[] in2, int in2_ptr, + short[] output, int output_ptr, int overlap, int channels, + int[] window, int Fs) { + int i, c; + int inc = 48000 / Fs; + for (c = 0; c < channels; c++) { + for (i = 0; i < overlap; i++) { + int w = Inlines.MULT16_16_Q15(window[i * inc], window[i * inc]); + output[output_ptr + (i * channels) + c] = (short) (Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(w, in2[in2_ptr + (i * channels) + c]), + CeltConstants.Q15ONE - w, in1[in1_ptr + (i * channels) + c]), 15)); + } + } + } + + //static void opus_pcm_soft_clip(Pointer _x, int N, int C, Pointer declip_mem) + //{ + // int c; + // int i; + // Pointer x; + // if (C < 1 || N < 1 || _x == null || declip_mem == null) return; + // /* First thing: saturate everything to +/- 2 which is the highest level our + // non-linearity can handle. At the point where the signal reaches +/-2, + // the derivative will be zero anyway, so this doesn't introduce any + // discontinuity in the derivative. */ + // for (i = 0; i < N * C; i++) + // _x[i] = Inlines.MAX16(-2.0f, Inlines.MIN16(2.0f, _x[i])); + // for (c = 0; c < C; c++) + // { + // float a; + // float x0; + // int curr; + // x = _x.Point(c); + // a = declip_mem[c]; + // /* Continue applying the non-linearity from the previous frame to avoid + // any discontinuity. */ + // for (i = 0; i < N; i++) + // { + // if (x[i * C] * a >= 0) + // break; + // x[i * C] = x[i * C] + a * x[i * C] * x[i * C]; + // } + // curr = 0; + // x0 = x[0]; + // while (true) + // { + // int start, end; + // float maxval; + // int special = 0; + // int peak_pos; + // for (i = curr; i < N; i++) + // { + // if (x[i * C] > 1 || x[i * C] < -1) + // break; + // } + // if (i == N) + // { + // a = 0; + // break; + // } + // peak_pos = i; + // start = end = i; + // maxval = Inlines.ABS16(x[i * C]); + // /* Look for first zero crossing before clipping */ + // while (start > 0 && x[i * C] * x[(start - 1) * C] >= 0) + // start--; + // /* Look for first zero crossing after clipping */ + // while (end < N && x[i * C] * x[end * C] >= 0) + // { + // /* Look for other peaks until the next zero-crossing. */ + // if (Inlines.ABS16(x[end * C]) > maxval) + // { + // maxval = Inlines.ABS16(x[end * C]); + // peak_pos = end; + // } + // end++; + // } + // /* Detect the special case where we clip before the first zero crossing */ + // special = (start == 0 && x[i * C] * x[0] >= 0) ? 1 : 0; + // /* Compute a such that maxval + a*maxval^2 = 1 */ + // a = (maxval - 1) / (maxval * maxval); + // if (x[i * C] > 0) + // a = -a; + // /* Apply soft clipping */ + // for (i = start; i < end; i++) + // x[i * C] = x[i * C] + a * x[i * C] * x[i * C]; + // if (special != 0 && peak_pos >= 2) + // { + // /* Add a linear ramp from the first sample to the signal peak. + // This avoids a discontinuity at the beginning of the frame. */ + // float delta; + // float offset = x0 - x[0]; + // delta = offset / peak_pos; + // for (i = curr; i < peak_pos; i++) + // { + // offset -= delta; + // x[i * C] += offset; + // x[i * C] = Inlines.MAX16(-1.0f, Inlines.MIN16(1.0f, x[i * C])); + // } + // } + // curr = end; + // if (curr == N) + // { + // break; + // } + // } + // declip_mem[c] = a; + // } + //} + public static String opus_strerror(int error) { + String[] error_strings = { + "success", + "invalid argument", + "buffer too small", + "error", + "corrupted stream", + "request not implemented", + "invalid state", + "memory allocation failed" + }; + if (error > 0 || error < -7) { + return "unknown error"; + } else { + return error_strings[-error]; + } + } + + public static String GetVersionString() { + return "concentus 1.0a-java-fixed"; + } +} diff --git a/app/src/main/java/org/concentus/CorrelateMatrix.java b/app/src/main/java/org/concentus/CorrelateMatrix.java new file mode 100644 index 0000000000..425cf63ea7 --- /dev/null +++ b/app/src/main/java/org/concentus/CorrelateMatrix.java @@ -0,0 +1,175 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/** + * ******************************************************************** + * Correlation Matrix Computations for LS estimate. + ********************************************************************* + */ +class CorrelateMatrix { + + /* Calculates correlation vector X'*t */ + static void silk_corrVector( + short[] x, /* I x vector [L + order - 1] used to form data matrix X */ + int x_ptr, + short[] t, /* I Target vector [L] */ + int t_ptr, + int L, /* I Length of vectors */ + int order, /* I Max lag for correlation */ + int[] Xt, /* O Pointer to X'*t correlation vector [order] */ + int rshifts /* I Right shifts of correlations */ + ) { + int lag, i; + int ptr1; + int ptr2; + int inner_prod; + + ptr1 = x_ptr + order - 1; + /* Points to first sample of column 0 of X: X[:,0] */ + ptr2 = t_ptr; + /* Calculate X'*t */ + if (rshifts > 0) { + /* Right shifting used */ + for (lag = 0; lag < order; lag++) { + inner_prod = 0; + for (i = 0; i < L; i++) { + inner_prod += Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + i], t[ptr2 + i]), rshifts); + } + Xt[lag] = inner_prod; + /* X[:,lag]'*t */ + ptr1--; + /* Go to next column of X */ + } + } else { + Inlines.OpusAssert(rshifts == 0); + for (lag = 0; lag < order; lag++) { + Xt[lag] = Inlines.silk_inner_prod(x, ptr1, t, ptr2, L); + /* X[:,lag]'*t */ + ptr1--; + /* Go to next column of X */ + } + } + } + + /* Calculates correlation matrix X'*X */ + static void silk_corrMatrix( + short[] x, /* I x vector [L + order - 1] used to form data matrix X */ + int x_ptr, + int L, /* I Length of vectors */ + int order, /* I Max lag for correlation */ + int head_room, /* I Desired headroom */ + int[] XX, /* O Pointer to X'*X correlation matrix [ order x order ] */ + int XX_ptr, + BoxedValueInt rshifts /* I/O Right shifts of correlations */ + ) { + int i, j, lag, head_room_rshifts; + int energy, rshifts_local; + int ptr1, ptr2; + + /* Calculate energy to find shift used to fit in 32 bits */ + BoxedValueInt boxed_energy = new BoxedValueInt(0); + BoxedValueInt boxed_rshifts_local = new BoxedValueInt(0); + SumSqrShift.silk_sum_sqr_shift(boxed_energy, boxed_rshifts_local, x, x_ptr, L + order - 1); + energy = boxed_energy.Val; + rshifts_local = boxed_rshifts_local.Val; + + /* Add shifts to get the desired head room */ + head_room_rshifts = Inlines.silk_max(head_room - Inlines.silk_CLZ32(energy), 0); + + energy = Inlines.silk_RSHIFT32(energy, head_room_rshifts); + rshifts_local += head_room_rshifts; + + /* Calculate energy of first column (0) of X: X[:,0]'*X[:,0] */ + /* Remove contribution of first order - 1 samples */ + for (i = x_ptr; i < x_ptr + order - 1; i++) { + energy -= Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[i], x[i]), rshifts_local); + } + if (rshifts_local < rshifts.Val) { + /* Adjust energy */ + energy = Inlines.silk_RSHIFT32(energy, rshifts.Val - rshifts_local); + rshifts_local = rshifts.Val; + } + + /* Calculate energy of remaining columns of X: X[:,j]'*X[:,j] */ + /* Fill out the diagonal of the correlation matrix */ + Inlines.MatrixSet(XX, XX_ptr, 0, 0, order, energy); + ptr1 = x_ptr + order - 1; + /* First sample of column 0 of X */ + for (j = 1; j < order; j++) { + energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr1 + L - j]), rshifts_local)); + energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 - j], x[ptr1 - j]), rshifts_local)); + Inlines.MatrixSet(XX, XX_ptr, j, j, order, energy); + } + + ptr2 = x_ptr + order - 2; + /* First sample of column 1 of X */ + /* Calculate the remaining elements of the correlation matrix */ + if (rshifts_local > 0) { + /* Right shifting used */ + for (lag = 1; lag < order; lag++) { + /* Inner product of column 0 and column lag: X[:,0]'*X[:,lag] */ + energy = 0; + for (i = 0; i < L; i++) { + energy += Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + i], x[ptr2 + i]), rshifts_local); + } + /* Calculate remaining off diagonal: X[:,j]'*X[:,j + lag] */ + Inlines.MatrixSet(XX, XX_ptr, lag, 0, order, energy); + Inlines.MatrixSet(XX, XX_ptr, 0, lag, order, energy); + for (j = 1; j < (order - lag); j++) { + energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr2 + L - j]), rshifts_local)); + energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 - j], x[ptr2 - j]), rshifts_local)); + Inlines.MatrixSet(XX, XX_ptr, lag + j, j, order, energy); + Inlines.MatrixSet(XX, XX_ptr, j, lag + j, order, energy); + } + ptr2--; + /* Update pointer to first sample of next column (lag) in X */ + } + } else { + for (lag = 1; lag < order; lag++) { + /* Inner product of column 0 and column lag: X[:,0]'*X[:,lag] */ + energy = Inlines.silk_inner_prod(x, ptr1, x, ptr2, L); + Inlines.MatrixSet(XX, XX_ptr, lag, 0, order, energy); + Inlines.MatrixSet(XX, XX_ptr, 0, lag, order, energy); + /* Calculate remaining off diagonal: X[:,j]'*X[:,j + lag] */ + for (j = 1; j < (order - lag); j++) { + energy = Inlines.silk_SUB32(energy, Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr2 + L - j])); + energy = Inlines.silk_SMLABB(energy, x[ptr1 - j], x[ptr2 - j]); + Inlines.MatrixSet(XX, XX_ptr, lag + j, j, order, energy); + Inlines.MatrixSet(XX, XX_ptr, j, lag + j, order, energy); + } + ptr2--;/* Update pointer to first sample of next column (lag) in X */ + } + } + rshifts.Val = rshifts_local; + } +} diff --git a/app/src/main/java/org/concentus/DecControlState.java b/app/src/main/java/org/concentus/DecControlState.java new file mode 100644 index 0000000000..3419e81a96 --- /dev/null +++ b/app/src/main/java/org/concentus/DecControlState.java @@ -0,0 +1,65 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Structure for controlling decoder operation and reading decoder status +/// +class DecControlState { + + /* I: Number of channels; 1/2 */ + int nChannelsAPI = 0; + + /* I: Number of channels; 1/2 */ + int nChannelsInternal = 0; + + /* I: Output signal sampling rate in Hertz; 8000/12000/16000/24000/32000/44100/48000 */ + int API_sampleRate = 0; + + /* I: Internal sampling rate used, in Hertz; 8000/12000/16000 */ + int internalSampleRate = 0; + + /* I: Number of samples per packet in milliseconds; 10/20/40/60 */ + int payloadSize_ms = 0; + + /* O: Pitch lag of previous frame (0 if unvoiced), measured in samples at 48 kHz */ + int prevPitchLag = 0; + + void Reset() { + nChannelsAPI = 0; + nChannelsInternal = 0; + API_sampleRate = 0; + internalSampleRate = 0; + payloadSize_ms = 0; + prevPitchLag = 0; + } +} diff --git a/app/src/main/java/org/concentus/DecodeAPI.java b/app/src/main/java/org/concentus/DecodeAPI.java new file mode 100644 index 0000000000..89acade7c2 --- /dev/null +++ b/app/src/main/java/org/concentus/DecodeAPI.java @@ -0,0 +1,374 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class DecodeAPI { + + /// + /// Reset decoder state + /// + /// I/O Stat + /// Returns error code + static int silk_InitDecoder(SilkDecoder decState) { + /* Reset decoder */ + decState.Reset(); + + int n, ret = SilkError.SILK_NO_ERROR; + SilkChannelDecoder[] channel_states = decState.channel_state; + + for (n = 0; n < SilkConstants.DECODER_NUM_CHANNELS; n++) { + ret = channel_states[n].silk_init_decoder(); + } + + decState.sStereo.Reset(); + + /* Not strictly needed, but it's cleaner that way */ + decState.prev_decode_only_middle = 0; + + return ret; + } + + /* Decode a frame */ + static int silk_Decode( /* O Returns error code */ + SilkDecoder psDec, /* I/O State */ + DecControlState decControl, /* I/O Control Structure */ + int lostFlag, /* I 0: no loss, 1 loss, 2 decode fec */ + int newPacketFlag, /* I Indicates first decoder call for this packet */ + EntropyCoder psRangeDec, /* I/O Compressor data structure */ + short[] samplesOut, /* O Decoded output speech vector */ + int samplesOut_ptr, + BoxedValueInt nSamplesOut /* O Number of samples decoded */ + ) { + int i, n, decode_only_middle = 0, ret = SilkError.SILK_NO_ERROR; + int LBRR_symbol; + BoxedValueInt nSamplesOutDec = new BoxedValueInt(0); + short[] samplesOut_tmp; + int[] samplesOut_tmp_ptrs = new int[2]; + short[] samplesOut1_tmp_storage1; + short[] samplesOut1_tmp_storage2; + short[] samplesOut2_tmp; + int[] MS_pred_Q13 = new int[]{0, 0}; + short[] resample_out; + int resample_out_ptr; + SilkChannelDecoder[] channel_state = psDec.channel_state; + int has_side; + int stereo_to_mono; + int delay_stack_alloc; + nSamplesOut.Val = 0; + + Inlines.OpusAssert(decControl.nChannelsInternal == 1 || decControl.nChannelsInternal == 2); + + /** + * ******************************* + */ + /* Test if first frame in payload */ + /** + * ******************************* + */ + if (newPacketFlag != 0) { + for (n = 0; n < decControl.nChannelsInternal; n++) { + channel_state[n].nFramesDecoded = 0; + /* Used to count frames in packet */ + } + } + + /* If Mono . Stereo transition in bitstream: init state of second channel */ + if (decControl.nChannelsInternal > psDec.nChannelsInternal) { + ret += channel_state[1].silk_init_decoder(); + } + + stereo_to_mono = (decControl.nChannelsInternal == 1 && psDec.nChannelsInternal == 2 + && (decControl.internalSampleRate == 1000 * channel_state[0].fs_kHz)) ? 1 : 0; + + if (channel_state[0].nFramesDecoded == 0) { + for (n = 0; n < decControl.nChannelsInternal; n++) { + int fs_kHz_dec; + if (decControl.payloadSize_ms == 0) { + /* Assuming packet loss, use 10 ms */ + channel_state[n].nFramesPerPacket = 1; + channel_state[n].nb_subfr = 2; + } else if (decControl.payloadSize_ms == 10) { + channel_state[n].nFramesPerPacket = 1; + channel_state[n].nb_subfr = 2; + } else if (decControl.payloadSize_ms == 20) { + channel_state[n].nFramesPerPacket = 1; + channel_state[n].nb_subfr = 4; + } else if (decControl.payloadSize_ms == 40) { + channel_state[n].nFramesPerPacket = 2; + channel_state[n].nb_subfr = 4; + } else if (decControl.payloadSize_ms == 60) { + channel_state[n].nFramesPerPacket = 3; + channel_state[n].nb_subfr = 4; + } else { + Inlines.OpusAssert(false); + return SilkError.SILK_DEC_INVALID_FRAME_SIZE; + } + fs_kHz_dec = (decControl.internalSampleRate >> 10) + 1; + if (fs_kHz_dec != 8 && fs_kHz_dec != 12 && fs_kHz_dec != 16) { + Inlines.OpusAssert(false); + return SilkError.SILK_DEC_INVALID_SAMPLING_FREQUENCY; + } + ret += channel_state[n].silk_decoder_set_fs(fs_kHz_dec, decControl.API_sampleRate); + } + } + + if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 2 && (psDec.nChannelsAPI == 1 || psDec.nChannelsInternal == 1)) { + Arrays.MemSet(psDec.sStereo.pred_prev_Q13, (short) 0, 2); + Arrays.MemSet(psDec.sStereo.sSide, (short) 0, 2); + channel_state[1].resampler_state.Assign(channel_state[0].resampler_state); + } + psDec.nChannelsAPI = decControl.nChannelsAPI; + psDec.nChannelsInternal = decControl.nChannelsInternal; + + if (decControl.API_sampleRate > (int) SilkConstants.MAX_API_FS_KHZ * 1000 || decControl.API_sampleRate < 8000) { + ret = SilkError.SILK_DEC_INVALID_SAMPLING_FREQUENCY; + return (ret); + } + + if (lostFlag != DecoderAPIFlag.FLAG_PACKET_LOST && channel_state[0].nFramesDecoded == 0) { + /* First decoder call for this payload */ + /* Decode VAD flags and LBRR flag */ + for (n = 0; n < decControl.nChannelsInternal; n++) { + for (i = 0; i < channel_state[n].nFramesPerPacket; i++) { + channel_state[n].VAD_flags[i] = psRangeDec.dec_bit_logp(1); + } + channel_state[n].LBRR_flag = psRangeDec.dec_bit_logp(1); + } + /* Decode LBRR flags */ + for (n = 0; n < decControl.nChannelsInternal; n++) { + Arrays.MemSet(channel_state[n].LBRR_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET); + if (channel_state[n].LBRR_flag != 0) { + if (channel_state[n].nFramesPerPacket == 1) { + channel_state[n].LBRR_flags[0] = 1; + } else { + LBRR_symbol = psRangeDec.dec_icdf(SilkTables.silk_LBRR_flags_iCDF_ptr[channel_state[n].nFramesPerPacket - 2], 8) + 1; + for (i = 0; i < channel_state[n].nFramesPerPacket; i++) { + channel_state[n].LBRR_flags[i] = Inlines.silk_RSHIFT(LBRR_symbol, i) & 1; + } + } + } + } + + if (lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL) { + /* Regular decoding: skip all LBRR data */ + for (i = 0; i < channel_state[0].nFramesPerPacket; i++) { + for (n = 0; n < decControl.nChannelsInternal; n++) { + if (channel_state[n].LBRR_flags[i] != 0) { + short[] pulses = new short[SilkConstants.MAX_FRAME_LENGTH]; + int condCoding; + + if (decControl.nChannelsInternal == 2 && n == 0) { + Stereo.silk_stereo_decode_pred(psRangeDec, MS_pred_Q13); + if (channel_state[1].LBRR_flags[i] == 0) { + BoxedValueInt decodeOnlyMiddleBoxed = new BoxedValueInt(decode_only_middle); + Stereo.silk_stereo_decode_mid_only(psRangeDec, decodeOnlyMiddleBoxed); + decode_only_middle = decodeOnlyMiddleBoxed.Val; + } + } + /* Use conditional coding if previous frame available */ + if (i > 0 && (channel_state[n].LBRR_flags[i - 1] != 0)) { + condCoding = SilkConstants.CODE_CONDITIONALLY; + } else { + condCoding = SilkConstants.CODE_INDEPENDENTLY; + } + DecodeIndices.silk_decode_indices(channel_state[n], psRangeDec, i, 1, condCoding); + DecodePulses.silk_decode_pulses(psRangeDec, pulses, channel_state[n].indices.signalType, + channel_state[n].indices.quantOffsetType, channel_state[n].frame_length); + } + } + } + } + } + + /* Get MS predictor index */ + if (decControl.nChannelsInternal == 2) { + if (lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL + || (lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR && channel_state[0].LBRR_flags[channel_state[0].nFramesDecoded] == 1)) { + Stereo.silk_stereo_decode_pred(psRangeDec, MS_pred_Q13); + /* For LBRR data, decode mid-only flag only if side-channel's LBRR flag is false */ + if ((lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL && channel_state[1].VAD_flags[channel_state[0].nFramesDecoded] == 0) + || (lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR && channel_state[1].LBRR_flags[channel_state[0].nFramesDecoded] == 0)) { + BoxedValueInt decodeOnlyMiddleBoxed = new BoxedValueInt(decode_only_middle); + Stereo.silk_stereo_decode_mid_only(psRangeDec, decodeOnlyMiddleBoxed); + decode_only_middle = decodeOnlyMiddleBoxed.Val; + } else { + decode_only_middle = 0; + } + } else { + for (n = 0; n < 2; n++) { + MS_pred_Q13[n] = psDec.sStereo.pred_prev_Q13[n]; + } + } + } + + /* Reset side channel decoder prediction memory for first frame with side coding */ + if (decControl.nChannelsInternal == 2 && decode_only_middle == 0 && psDec.prev_decode_only_middle == 1) { + Arrays.MemSet(psDec.channel_state[1].outBuf, (short) 0, SilkConstants.MAX_FRAME_LENGTH + 2 * SilkConstants.MAX_SUB_FRAME_LENGTH); + Arrays.MemSet(psDec.channel_state[1].sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER); + psDec.channel_state[1].lagPrev = 100; + psDec.channel_state[1].LastGainIndex = 10; + psDec.channel_state[1].prevSignalType = SilkConstants.TYPE_NO_VOICE_ACTIVITY; + psDec.channel_state[1].first_frame_after_reset = 1; + } + + /* Check if the temp buffer fits into the output PCM buffer. If it fits, + we can delay allocating the temp buffer until after the SILK peak stack + usage. We need to use a < and not a <= because of the two extra samples. */ + delay_stack_alloc = (decControl.internalSampleRate * decControl.nChannelsInternal + < decControl.API_sampleRate * decControl.nChannelsAPI) ? 1 : 0; + + if (delay_stack_alloc != 0) { + samplesOut_tmp = samplesOut; + samplesOut_tmp_ptrs[0] = samplesOut_ptr; + samplesOut_tmp_ptrs[1] = samplesOut_ptr + channel_state[0].frame_length + 2; + } else { + samplesOut1_tmp_storage1 = new short[decControl.nChannelsInternal * (channel_state[0].frame_length + 2)]; + samplesOut_tmp = samplesOut1_tmp_storage1; + samplesOut_tmp_ptrs[0] = 0; + samplesOut_tmp_ptrs[1] = channel_state[0].frame_length + 2; + } + + if (lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL) { + has_side = (decode_only_middle == 0) ? 1 : 0; + } else { + has_side = (psDec.prev_decode_only_middle == 0 + || (decControl.nChannelsInternal == 2 + && lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR + && channel_state[1].LBRR_flags[channel_state[1].nFramesDecoded] == 1)) ? 1 : 0; + } + /* Call decoder for one frame */ + for (n = 0; n < decControl.nChannelsInternal; n++) { + if (n == 0 || (has_side != 0)) { + int FrameIndex; + int condCoding; + + FrameIndex = channel_state[0].nFramesDecoded - n; + /* Use independent coding if no previous frame available */ + if (FrameIndex <= 0) { + condCoding = SilkConstants.CODE_INDEPENDENTLY; + } else if (lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR) { + condCoding = (channel_state[n].LBRR_flags[FrameIndex - 1] != 0) ? SilkConstants.CODE_CONDITIONALLY : SilkConstants.CODE_INDEPENDENTLY; + } else if (n > 0 && (psDec.prev_decode_only_middle != 0)) { + /* If we skipped a side frame in this packet, we don't + need LTP scaling; the LTP state is well-defined. */ + condCoding = SilkConstants.CODE_INDEPENDENTLY_NO_LTP_SCALING; + } else { + condCoding = SilkConstants.CODE_CONDITIONALLY; + } + ret += channel_state[n].silk_decode_frame(psRangeDec, samplesOut_tmp, samplesOut_tmp_ptrs[n] + 2, nSamplesOutDec, lostFlag, condCoding); + } else { + Arrays.MemSetWithOffset(samplesOut_tmp, (short) 0, samplesOut_tmp_ptrs[n] + 2, nSamplesOutDec.Val); + } + channel_state[n].nFramesDecoded++; + } + + if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 2) { + /* Convert Mid/Side to Left/Right */ + Stereo.silk_stereo_MS_to_LR(psDec.sStereo, samplesOut_tmp, samplesOut_tmp_ptrs[0], samplesOut_tmp, samplesOut_tmp_ptrs[1], MS_pred_Q13, channel_state[0].fs_kHz, nSamplesOutDec.Val); + } else { + /* Buffering */ + System.arraycopy(psDec.sStereo.sMid, 0, samplesOut_tmp, samplesOut_tmp_ptrs[0], 2); + System.arraycopy(samplesOut_tmp, samplesOut_tmp_ptrs[0] + nSamplesOutDec.Val, psDec.sStereo.sMid, 0, 2); + } + + /* Number of output samples */ + nSamplesOut.Val = Inlines.silk_DIV32(nSamplesOutDec.Val * decControl.API_sampleRate, Inlines.silk_SMULBB(channel_state[0].fs_kHz, 1000)); + + /* Set up pointers to temp buffers */ + if (decControl.nChannelsAPI == 2) { + samplesOut2_tmp = new short[nSamplesOut.Val]; + resample_out = samplesOut2_tmp; + resample_out_ptr = 0; + } else { + resample_out = samplesOut; + resample_out_ptr = samplesOut_ptr; + } + + if (delay_stack_alloc != 0) { + samplesOut1_tmp_storage2 = new short[decControl.nChannelsInternal * (channel_state[0].frame_length + 2)]; + System.arraycopy(samplesOut, samplesOut_ptr, samplesOut1_tmp_storage2, 0, decControl.nChannelsInternal * (channel_state[0].frame_length + 2)); + samplesOut_tmp = samplesOut1_tmp_storage2; + samplesOut_tmp_ptrs[0] = 0; + samplesOut_tmp_ptrs[1] = channel_state[0].frame_length + 2; + } + for (n = 0; n < Inlines.silk_min(decControl.nChannelsAPI, decControl.nChannelsInternal); n++) { + + /* Resample decoded signal to API_sampleRate */ + ret += Resampler.silk_resampler(channel_state[n].resampler_state, resample_out, resample_out_ptr, samplesOut_tmp, samplesOut_tmp_ptrs[n] + 1, nSamplesOutDec.Val); + + /* Interleave if stereo output and stereo stream */ + if (decControl.nChannelsAPI == 2) { + int nptr = samplesOut_ptr + n; + for (i = 0; i < nSamplesOut.Val; i++) { + samplesOut[nptr + 2 * i] = resample_out[resample_out_ptr + i]; + } + } + } + + /* Create two channel output from mono stream */ + if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 1) { + if (stereo_to_mono != 0) { + /* Resample right channel for newly collapsed stereo just in case + we weren't doing collapsing when switching to mono */ + ret += Resampler.silk_resampler(channel_state[1].resampler_state, resample_out, resample_out_ptr, samplesOut_tmp, samplesOut_tmp_ptrs[0] + 1, nSamplesOutDec.Val); + + for (i = 0; i < nSamplesOut.Val; i++) { + samplesOut[samplesOut_ptr + 1 + 2 * i] = resample_out[resample_out_ptr + i]; + } + } else { + for (i = 0; i < nSamplesOut.Val; i++) { + samplesOut[samplesOut_ptr + 1 + 2 * i] = samplesOut[samplesOut_ptr + 2 * i]; + } + } + } + + /* Export pitch lag, measured at 48 kHz sampling rate */ + if (channel_state[0].prevSignalType == SilkConstants.TYPE_VOICED) { + int[] mult_tab = {6, 4, 3}; + decControl.prevPitchLag = channel_state[0].lagPrev * mult_tab[(channel_state[0].fs_kHz - 8) >> 2]; + } else { + decControl.prevPitchLag = 0; + } + + if (lostFlag == DecoderAPIFlag.FLAG_PACKET_LOST) { + /* On packet loss, remove the gain clamping to prevent having the energy "bounce back" + if we lose packets when the energy is going down */ + for (i = 0; i < psDec.nChannelsInternal; i++) { + psDec.channel_state[i].LastGainIndex = 10; + } + } else { + psDec.prev_decode_only_middle = decode_only_middle; + } + + return ret; + } +} diff --git a/app/src/main/java/org/concentus/DecodeCore.java b/app/src/main/java/org/concentus/DecodeCore.java new file mode 100644 index 0000000000..0b26a41c9e --- /dev/null +++ b/app/src/main/java/org/concentus/DecodeCore.java @@ -0,0 +1,245 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class DecodeCore { + + /** + * ******************************************************* + */ + /* Core decoder. Performs inverse NSQ operation LTP + LPC */ + /** + * ******************************************************* + */ + static void silk_decode_core( + SilkChannelDecoder psDec, /* I/O Decoder state */ + SilkDecoderControl psDecCtrl, /* I Decoder control */ + short[] xq, /* O Decoded speech */ + int xq_ptr, + short[] pulses /* I Pulse signal [MAX_FRAME_LENGTH] */ + ) { + int i, k, lag = 0, start_idx, sLTP_buf_idx, NLSF_interpolation_flag, signalType; + short[] A_Q12; + short[] B_Q14 = psDecCtrl.LTPCoef_Q14; + int B_Q14_ptr; + int pxq; + short[] sLTP; + int[] sLTP_Q15; + int LTP_pred_Q13, LPC_pred_Q10, Gain_Q10, inv_gain_Q31, gain_adj_Q16, rand_seed, offset_Q10; + int pred_lag_ptr; + int pexc_Q14; + int[] pres_Q14; + int pres_Q14_ptr; + int[] res_Q14; + int[] sLPC_Q14; + + Inlines.OpusAssert(psDec.prev_gain_Q16 != 0); + + sLTP = new short[psDec.ltp_mem_length]; + sLTP_Q15 = new int[psDec.ltp_mem_length + psDec.frame_length]; + res_Q14 = new int[psDec.subfr_length]; + sLPC_Q14 = new int[psDec.subfr_length + SilkConstants.MAX_LPC_ORDER]; + + offset_Q10 = SilkTables.silk_Quantization_Offsets_Q10[psDec.indices.signalType >> 1][psDec.indices.quantOffsetType]; + + if (psDec.indices.NLSFInterpCoef_Q2 < 1 << 2) { + NLSF_interpolation_flag = 1; + } else { + NLSF_interpolation_flag = 0; + } + + /* Decode excitation */ + rand_seed = psDec.indices.Seed; + for (i = 0; i < psDec.frame_length; i++) { + rand_seed = Inlines.silk_RAND(rand_seed); + psDec.exc_Q14[i] = Inlines.silk_LSHIFT((int) pulses[i], 14); + if (psDec.exc_Q14[i] > 0) { + psDec.exc_Q14[i] -= SilkConstants.QUANT_LEVEL_ADJUST_Q10 << 4; + } else { + if (psDec.exc_Q14[i] < 0) { + psDec.exc_Q14[i] += SilkConstants.QUANT_LEVEL_ADJUST_Q10 << 4; + } + } + psDec.exc_Q14[i] += offset_Q10 << 4; + if (rand_seed < 0) { + psDec.exc_Q14[i] = -psDec.exc_Q14[i]; + } + + rand_seed = Inlines.silk_ADD32_ovflw(rand_seed, pulses[i]); + } + + /* Copy LPC state */ + System.arraycopy(psDec.sLPC_Q14_buf, 0, sLPC_Q14, 0, SilkConstants.MAX_LPC_ORDER); + + pexc_Q14 = 0; + pxq = xq_ptr; + sLTP_buf_idx = psDec.ltp_mem_length; + /* Loop over subframes */ + for (k = 0; k < psDec.nb_subfr; k++) { + pres_Q14 = res_Q14; + pres_Q14_ptr = 0; + A_Q12 = psDecCtrl.PredCoef_Q12[k >> 1]; + B_Q14_ptr = k * SilkConstants.LTP_ORDER; + signalType = psDec.indices.signalType; + + Gain_Q10 = Inlines.silk_RSHIFT(psDecCtrl.Gains_Q16[k], 6); + inv_gain_Q31 = Inlines.silk_INVERSE32_varQ(psDecCtrl.Gains_Q16[k], 47); + + /* Calculate gain adjustment factor */ + if (psDecCtrl.Gains_Q16[k] != psDec.prev_gain_Q16) { + gain_adj_Q16 = Inlines.silk_DIV32_varQ(psDec.prev_gain_Q16, psDecCtrl.Gains_Q16[k], 16); + + /* Scale short term state */ + for (i = 0; i < SilkConstants.MAX_LPC_ORDER; i++) { + sLPC_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, sLPC_Q14[i]); + } + } else { + gain_adj_Q16 = (int) 1 << 16; + } + + /* Save inv_gain */ + Inlines.OpusAssert(inv_gain_Q31 != 0); + psDec.prev_gain_Q16 = psDecCtrl.Gains_Q16[k]; + + /* Avoid abrupt transition from voiced PLC to unvoiced normal decoding */ + if (psDec.lossCnt != 0 && psDec.prevSignalType == SilkConstants.TYPE_VOICED + && psDec.indices.signalType != SilkConstants.TYPE_VOICED && k < SilkConstants.MAX_NB_SUBFR / 2) { + + Arrays.MemSetWithOffset(B_Q14, (short) 0, B_Q14_ptr, SilkConstants.LTP_ORDER); + B_Q14[B_Q14_ptr + (SilkConstants.LTP_ORDER / 2)] = (short) (((int) ((0.25f) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.25f, 14)*/); + + signalType = SilkConstants.TYPE_VOICED; + psDecCtrl.pitchL[k] = psDec.lagPrev; + } + + if (signalType == SilkConstants.TYPE_VOICED) { + /* Voiced */ + lag = psDecCtrl.pitchL[k]; + + /* Re-whitening */ + if (k == 0 || (k == 2 && (NLSF_interpolation_flag != 0))) { + /* Rewhiten with new A coefs */ + start_idx = psDec.ltp_mem_length - lag - psDec.LPC_order - SilkConstants.LTP_ORDER / 2; + Inlines.OpusAssert(start_idx > 0); + + if (k == 2) { + System.arraycopy(xq, xq_ptr, psDec.outBuf, psDec.ltp_mem_length, 2 * psDec.subfr_length); + } + + Filters.silk_LPC_analysis_filter(sLTP, start_idx, psDec.outBuf, (start_idx + k * psDec.subfr_length), + A_Q12, 0, psDec.ltp_mem_length - start_idx, psDec.LPC_order); + + /* After rewhitening the LTP state is unscaled */ + if (k == 0) { + /* Do LTP downscaling to reduce inter-packet dependency */ + inv_gain_Q31 = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(inv_gain_Q31, psDecCtrl.LTP_scale_Q14), 2); + } + for (i = 0; i < lag + SilkConstants.LTP_ORDER / 2; i++) { + sLTP_Q15[sLTP_buf_idx - i - 1] = Inlines.silk_SMULWB(inv_gain_Q31, sLTP[psDec.ltp_mem_length - i - 1]); + } + } else /* Update LTP state when Gain changes */ if (gain_adj_Q16 != (int) 1 << 16) { + for (i = 0; i < lag + SilkConstants.LTP_ORDER / 2; i++) { + sLTP_Q15[sLTP_buf_idx - i - 1] = Inlines.silk_SMULWW(gain_adj_Q16, sLTP_Q15[sLTP_buf_idx - i - 1]); + } + } + } + + /* Long-term prediction */ + if (signalType == SilkConstants.TYPE_VOICED) { + /* Set up pointer */ + pred_lag_ptr = sLTP_buf_idx - lag + SilkConstants.LTP_ORDER / 2; + for (i = 0; i < psDec.subfr_length; i++) { + /* Unrolled loop */ + /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ + LTP_pred_Q13 = 2; + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr], B_Q14[B_Q14_ptr]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 1], B_Q14[B_Q14_ptr + 1]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 2], B_Q14[B_Q14_ptr + 2]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 3], B_Q14[B_Q14_ptr + 3]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 4], B_Q14[B_Q14_ptr + 4]); + pred_lag_ptr += 1; + + /* Generate LPC excitation */ + pres_Q14[pres_Q14_ptr + i] = Inlines.silk_ADD_LSHIFT32(psDec.exc_Q14[pexc_Q14 + i], LTP_pred_Q13, 1); + + /* Update states */ + sLTP_Q15[sLTP_buf_idx] = Inlines.silk_LSHIFT(pres_Q14[pres_Q14_ptr + i], 1); + sLTP_buf_idx++; + } + } else { + pres_Q14 = psDec.exc_Q14; + pres_Q14_ptr = pexc_Q14; + } + + for (i = 0; i < psDec.subfr_length; i++) { + /* Short-term prediction */ + Inlines.OpusAssert(psDec.LPC_order == 10 || psDec.LPC_order == 16); + /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ + LPC_pred_Q10 = Inlines.silk_RSHIFT(psDec.LPC_order, 1); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 1], A_Q12[0]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 2], A_Q12[1]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 3], A_Q12[2]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 4], A_Q12[3]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 5], A_Q12[4]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 6], A_Q12[5]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 7], A_Q12[6]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 8], A_Q12[7]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 9], A_Q12[8]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 10], A_Q12[9]); + if (psDec.LPC_order == 16) { + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 11], A_Q12[10]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 12], A_Q12[11]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 13], A_Q12[12]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 14], A_Q12[13]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 15], A_Q12[14]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 16], A_Q12[15]); + } + + /* Add prediction to LPC excitation */ + sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i] = Inlines.silk_ADD_LSHIFT32(pres_Q14[pres_Q14_ptr + i], LPC_pred_Q10, 4); + + /* Scale with gain */ + xq[pxq + i] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i], Gain_Q10), 8)); + } + + /* DEBUG_STORE_DATA( dec.pcm, pxq, psDec.subfr_length * sizeof( short ) ) */ + + /* Update LPC filter state */ + System.arraycopy(sLPC_Q14, psDec.subfr_length, sLPC_Q14, 0, SilkConstants.MAX_LPC_ORDER); + pexc_Q14 += psDec.subfr_length; + pxq += psDec.subfr_length; + } + + /* Save LPC state */ + System.arraycopy(sLPC_Q14, 0, psDec.sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER); + } +} diff --git a/app/src/main/java/org/concentus/DecodeIndices.java b/app/src/main/java/org/concentus/DecodeIndices.java new file mode 100644 index 0000000000..e35ffa0cc0 --- /dev/null +++ b/app/src/main/java/org/concentus/DecodeIndices.java @@ -0,0 +1,180 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class DecodeIndices { + + /* Decode side-information parameters from payload */ + static void silk_decode_indices( + SilkChannelDecoder psDec, /* I/O State */ + EntropyCoder psRangeDec, /* I/O Compressor data structure */ + int FrameIndex, /* I Frame number */ + int decode_LBRR, /* I Flag indicating LBRR data is being decoded */ + int condCoding /* I The type of conditional coding to use */ + ) { + int i, k, Ix; + int decode_absolute_lagIndex, delta_lagIndex; + short[] ec_ix = new short[psDec.LPC_order]; + short[] pred_Q8 = new short[psDec.LPC_order]; + + /** + * **************************************** + */ + /* Decode signal type and quantizer offset */ + /** + * **************************************** + */ + if (decode_LBRR != 0 || psDec.VAD_flags[FrameIndex] != 0) { + Ix = psRangeDec.dec_icdf(SilkTables.silk_type_offset_VAD_iCDF, 8) + 2; + } else { + Ix = psRangeDec.dec_icdf(SilkTables.silk_type_offset_no_VAD_iCDF, 8); + } + psDec.indices.signalType = (byte) Inlines.silk_RSHIFT(Ix, 1); + psDec.indices.quantOffsetType = (byte) (Ix & 1); + + /** + * ************* + */ + /* Decode gains */ + /** + * ************* + */ + /* First subframe */ + if (condCoding == SilkConstants.CODE_CONDITIONALLY) { + /* Conditional coding */ + psDec.indices.GainsIndices[0] = (byte) psRangeDec.dec_icdf(SilkTables.silk_delta_gain_iCDF, 8); + } else { + /* Independent coding, in two stages: MSB bits followed by 3 LSBs */ + psDec.indices.GainsIndices[0] = (byte) Inlines.silk_LSHIFT(psRangeDec.dec_icdf(SilkTables.silk_gain_iCDF[psDec.indices.signalType], 8), 3); + psDec.indices.GainsIndices[0] += (byte) psRangeDec.dec_icdf(SilkTables.silk_uniform8_iCDF, 8); + } + + /* Remaining subframes */ + for (i = 1; i < psDec.nb_subfr; i++) { + psDec.indices.GainsIndices[i] = (byte) psRangeDec.dec_icdf(SilkTables.silk_delta_gain_iCDF, 8); + } + + /** + * ******************* + */ + /* Decode LSF Indices */ + /** + * ******************* + */ + psDec.indices.NLSFIndices[0] = (byte) psRangeDec.dec_icdf(psDec.psNLSF_CB.CB1_iCDF, (psDec.indices.signalType >> 1) * psDec.psNLSF_CB.nVectors, 8); + NLSF.silk_NLSF_unpack(ec_ix, pred_Q8, psDec.psNLSF_CB, psDec.indices.NLSFIndices[0]); + Inlines.OpusAssert(psDec.psNLSF_CB.order == psDec.LPC_order); + for (i = 0; i < psDec.psNLSF_CB.order; i++) { + Ix = psRangeDec.dec_icdf(psDec.psNLSF_CB.ec_iCDF, (ec_ix[i]), 8); + if (Ix == 0) { + Ix -= psRangeDec.dec_icdf(SilkTables.silk_NLSF_EXT_iCDF, 8); + } else if (Ix == 2 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE) { + Ix += psRangeDec.dec_icdf(SilkTables.silk_NLSF_EXT_iCDF, 8); + } + psDec.indices.NLSFIndices[i + 1] = (byte) (Ix - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE); + } + + /* Decode LSF interpolation factor */ + if (psDec.nb_subfr == SilkConstants.MAX_NB_SUBFR) { + psDec.indices.NLSFInterpCoef_Q2 = (byte) psRangeDec.dec_icdf(SilkTables.silk_NLSF_interpolation_factor_iCDF, 8); + } else { + psDec.indices.NLSFInterpCoef_Q2 = 4; + } + + if (psDec.indices.signalType == SilkConstants.TYPE_VOICED) { + /** + * ****************** + */ + /* Decode pitch lags */ + /** + * ****************** + */ + /* Get lag index */ + decode_absolute_lagIndex = 1; + if (condCoding == SilkConstants.CODE_CONDITIONALLY && psDec.ec_prevSignalType == SilkConstants.TYPE_VOICED) { + /* Decode Delta index */ + delta_lagIndex = (short) psRangeDec.dec_icdf(SilkTables.silk_pitch_delta_iCDF, 8); + if (delta_lagIndex > 0) { + delta_lagIndex = delta_lagIndex - 9; + psDec.indices.lagIndex = (short) (psDec.ec_prevLagIndex + delta_lagIndex); + decode_absolute_lagIndex = 0; + } + } + if (decode_absolute_lagIndex != 0) { + /* Absolute decoding */ + psDec.indices.lagIndex = (short) (psRangeDec.dec_icdf(SilkTables.silk_pitch_lag_iCDF, 8) * Inlines.silk_RSHIFT(psDec.fs_kHz, 1)); + psDec.indices.lagIndex += (short) psRangeDec.dec_icdf(psDec.pitch_lag_low_bits_iCDF, 8); + } + psDec.ec_prevLagIndex = psDec.indices.lagIndex; + + /* Get countour index */ + psDec.indices.contourIndex = (byte) psRangeDec.dec_icdf(psDec.pitch_contour_iCDF, 8); + + /** + * ***************** + */ + /* Decode LTP gains */ + /** + * ***************** + */ + /* Decode PERIndex value */ + psDec.indices.PERIndex = (byte) psRangeDec.dec_icdf(SilkTables.silk_LTP_per_index_iCDF, 8); + + for (k = 0; k < psDec.nb_subfr; k++) { + psDec.indices.LTPIndex[k] = (byte) psRangeDec.dec_icdf(SilkTables.silk_LTP_gain_iCDF_ptrs[psDec.indices.PERIndex], 8); + } + + /** + * ******************* + */ + /* Decode LTP scaling */ + /** + * ******************* + */ + if (condCoding == SilkConstants.CODE_INDEPENDENTLY) { + psDec.indices.LTP_scaleIndex = (byte) psRangeDec.dec_icdf(SilkTables.silk_LTPscale_iCDF, 8); + } else { + psDec.indices.LTP_scaleIndex = 0; + } + } + psDec.ec_prevSignalType = psDec.indices.signalType; + + /** + * ************ + */ + /* Decode seed */ + /** + * ************ + */ + psDec.indices.Seed = (byte) psRangeDec.dec_icdf(SilkTables.silk_uniform4_iCDF, 8); + } +} diff --git a/app/src/main/java/org/concentus/DecodeParameters.java b/app/src/main/java/org/concentus/DecodeParameters.java new file mode 100644 index 0000000000..cbc6c8a190 --- /dev/null +++ b/app/src/main/java/org/concentus/DecodeParameters.java @@ -0,0 +1,133 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class DecodeParameters { + + /* Decode parameters from payload */ + static void silk_decode_parameters( + SilkChannelDecoder psDec, /* I/O State */ + SilkDecoderControl psDecCtrl, /* I/O Decoder control */ + int condCoding /* I The type of conditional coding to use */ + ) { + int i, k, Ix; + short[] pNLSF_Q15 = new short[psDec.LPC_order]; + short[] pNLSF0_Q15 = new short[psDec.LPC_order]; + byte[][] cbk_ptr_Q7; + + /* Dequant Gains */ + BoxedValueByte boxedLastGainIndex = new BoxedValueByte(psDec.LastGainIndex); + GainQuantization.silk_gains_dequant(psDecCtrl.Gains_Q16, psDec.indices.GainsIndices, + boxedLastGainIndex, condCoding == SilkConstants.CODE_CONDITIONALLY ? 1 : 0, psDec.nb_subfr); + psDec.LastGainIndex = boxedLastGainIndex.Val; + + /** + * ************* + */ + /* Decode NLSFs */ + /** + * ************* + */ + NLSF.silk_NLSF_decode(pNLSF_Q15, psDec.indices.NLSFIndices, psDec.psNLSF_CB); + + /* Convert NLSF parameters to AR prediction filter coefficients */ + NLSF.silk_NLSF2A(psDecCtrl.PredCoef_Q12[1], pNLSF_Q15, psDec.LPC_order); + + /* If just reset, e.g., because internal Fs changed, do not allow interpolation */ + /* improves the case of packet loss in the first frame after a switch */ + if (psDec.first_frame_after_reset == 1) { + psDec.indices.NLSFInterpCoef_Q2 = 4; + } + + if (psDec.indices.NLSFInterpCoef_Q2 < 4) { + /* Calculation of the interpolated NLSF0 vector from the interpolation factor, */ + /* the previous NLSF1, and the current NLSF1 */ + for (i = 0; i < psDec.LPC_order; i++) { + pNLSF0_Q15[i] = (short) (psDec.prevNLSF_Q15[i] + Inlines.silk_RSHIFT(Inlines.silk_MUL(psDec.indices.NLSFInterpCoef_Q2, + pNLSF_Q15[i] - psDec.prevNLSF_Q15[i]), 2)); + } + + /* Convert NLSF parameters to AR prediction filter coefficients */ + NLSF.silk_NLSF2A(psDecCtrl.PredCoef_Q12[0], pNLSF0_Q15, psDec.LPC_order); + } else { + /* Copy LPC coefficients for first half from second half */ + System.arraycopy(psDecCtrl.PredCoef_Q12[1], 0, psDecCtrl.PredCoef_Q12[0], 0, psDec.LPC_order); + } + + System.arraycopy(pNLSF_Q15, 0, psDec.prevNLSF_Q15, 0, psDec.LPC_order); + + /* After a packet loss do BWE of LPC coefs */ + if (psDec.lossCnt != 0) { + BWExpander.silk_bwexpander(psDecCtrl.PredCoef_Q12[0], psDec.LPC_order, SilkConstants.BWE_AFTER_LOSS_Q16); + BWExpander.silk_bwexpander(psDecCtrl.PredCoef_Q12[1], psDec.LPC_order, SilkConstants.BWE_AFTER_LOSS_Q16); + } + + if (psDec.indices.signalType == SilkConstants.TYPE_VOICED) { + /** + * ****************** + */ + /* Decode pitch lags */ + /** + * ****************** + */ + + /* Decode pitch values */ + DecodePitch.silk_decode_pitch(psDec.indices.lagIndex, psDec.indices.contourIndex, psDecCtrl.pitchL, psDec.fs_kHz, psDec.nb_subfr); + + /* Decode Codebook Index */ + cbk_ptr_Q7 = SilkTables.silk_LTP_vq_ptrs_Q7[psDec.indices.PERIndex]; + /* set pointer to start of codebook */ + + for (k = 0; k < psDec.nb_subfr; k++) { + Ix = psDec.indices.LTPIndex[k]; + for (i = 0; i < SilkConstants.LTP_ORDER; i++) { + psDecCtrl.LTPCoef_Q14[k * SilkConstants.LTP_ORDER + i] = (short) (Inlines.silk_LSHIFT(cbk_ptr_Q7[Ix][i], 7)); + } + } + + /** + * ******************* + */ + /* Decode LTP scaling */ + /** + * ******************* + */ + Ix = psDec.indices.LTP_scaleIndex; + psDecCtrl.LTP_scale_Q14 = SilkTables.silk_LTPScales_table_Q14[Ix]; + } else { + Arrays.MemSet(psDecCtrl.pitchL, 0, psDec.nb_subfr); + Arrays.MemSet(psDecCtrl.LTPCoef_Q14, (short) 0, SilkConstants.LTP_ORDER * psDec.nb_subfr); + psDec.indices.PERIndex = 0; + psDecCtrl.LTP_scale_Q14 = 0; + } + } +} diff --git a/app/src/main/java/org/concentus/DecodePitch.java b/app/src/main/java/org/concentus/DecodePitch.java new file mode 100644 index 0000000000..c68ff7c1d5 --- /dev/null +++ b/app/src/main/java/org/concentus/DecodePitch.java @@ -0,0 +1,69 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class DecodePitch { + + static void silk_decode_pitch( + short lagIndex, /* I */ + byte contourIndex, /* O */ + int[] pitch_lags, /* O 4 pitch values */ + int Fs_kHz, /* I sampling frequency (kHz) */ + int nb_subfr /* I number of sub frames */ + ) { + int lag, k, min_lag, max_lag; + byte[][] Lag_CB_ptr; + + if (Fs_kHz == 8) { + if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR) { + Lag_CB_ptr = SilkTables.silk_CB_lags_stage2; + } else { + Inlines.OpusAssert(nb_subfr == SilkConstants.PE_MAX_NB_SUBFR >> 1); + Lag_CB_ptr = SilkTables.silk_CB_lags_stage2_10_ms; + } + } else if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR) { + Lag_CB_ptr = SilkTables.silk_CB_lags_stage3; + } else { + Inlines.OpusAssert(nb_subfr == SilkConstants.PE_MAX_NB_SUBFR >> 1); + Lag_CB_ptr = SilkTables.silk_CB_lags_stage3_10_ms; + } + + min_lag = Inlines.silk_SMULBB(SilkConstants.PE_MIN_LAG_MS, Fs_kHz); + max_lag = Inlines.silk_SMULBB(SilkConstants.PE_MAX_LAG_MS, Fs_kHz); + lag = min_lag + lagIndex; + + for (k = 0; k < nb_subfr; k++) { + pitch_lags[k] = lag + Lag_CB_ptr[k][contourIndex]; + pitch_lags[k] = Inlines.silk_LIMIT(pitch_lags[k], min_lag, max_lag); + } + } +} diff --git a/app/src/main/java/org/concentus/DecodePulses.java b/app/src/main/java/org/concentus/DecodePulses.java new file mode 100644 index 0000000000..9be3b0a8c4 --- /dev/null +++ b/app/src/main/java/org/concentus/DecodePulses.java @@ -0,0 +1,141 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class DecodePulses { + + /** + * ****************************************** + */ + /* Decode quantization indices of excitation */ + /** + * ****************************************** + */ + static void silk_decode_pulses( + EntropyCoder psRangeDec, /* I/O Compressor data structure */ + short[] pulses, /* O Excitation signal */ + int signalType, /* I Sigtype */ + int quantOffsetType, /* I quantOffsetType */ + int frame_length /* I Frame length */ + ) { + int i, j, k, iter, abs_q, nLS, RateLevelIndex; + int[] sum_pulses = new int[SilkConstants.MAX_NB_SHELL_BLOCKS]; + int[] nLshifts = new int[SilkConstants.MAX_NB_SHELL_BLOCKS]; + int pulses_ptr; + + /** + * ****************** + */ + /* Decode rate level */ + /** + * ****************** + */ + RateLevelIndex = psRangeDec.dec_icdf(SilkTables.silk_rate_levels_iCDF[signalType >> 1], 8); + + /* Calculate number of shell blocks */ + Inlines.OpusAssert(1 << SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH == SilkConstants.SHELL_CODEC_FRAME_LENGTH); + iter = Inlines.silk_RSHIFT(frame_length, SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH); + if (iter * SilkConstants.SHELL_CODEC_FRAME_LENGTH < frame_length) { + Inlines.OpusAssert(frame_length == 12 * 10); + /* Make sure only happens for 10 ms @ 12 kHz */ + iter++; + } + + /** + * ************************************************ + */ + /* Sum-Weighted-Pulses Decoding */ + /** + * ************************************************ + */ + for (i = 0; i < iter; i++) { + nLshifts[i] = 0; + sum_pulses[i] = psRangeDec.dec_icdf(SilkTables.silk_pulses_per_block_iCDF[RateLevelIndex], 8); + + /* LSB indication */ + while (sum_pulses[i] == SilkConstants.SILK_MAX_PULSES + 1) { + nLshifts[i]++; + /* When we've already got 10 LSBs, we shift the table to not allow (SILK_MAX_PULSES + 1) */ + sum_pulses[i] = psRangeDec.dec_icdf( + SilkTables.silk_pulses_per_block_iCDF[SilkConstants.N_RATE_LEVELS - 1], (nLshifts[i] == 10 ? 1 : 0), 8); + } + } + + /** + * ************************************************ + */ + /* Shell decoding */ + /** + * ************************************************ + */ + for (i = 0; i < iter; i++) { + if (sum_pulses[i] > 0) { + ShellCoder.silk_shell_decoder(pulses, Inlines.silk_SMULBB(i, SilkConstants.SHELL_CODEC_FRAME_LENGTH), psRangeDec, sum_pulses[i]); + } else { + Arrays.MemSetWithOffset(pulses, (byte) 0, Inlines.silk_SMULBB(i, SilkConstants.SHELL_CODEC_FRAME_LENGTH), SilkConstants.SHELL_CODEC_FRAME_LENGTH); + } + } + + /** + * ************************************************ + */ + /* LSB Decoding */ + /** + * ************************************************ + */ + for (i = 0; i < iter; i++) { + if (nLshifts[i] > 0) { + nLS = nLshifts[i]; + pulses_ptr = Inlines.silk_SMULBB(i, SilkConstants.SHELL_CODEC_FRAME_LENGTH); + for (k = 0; k < SilkConstants.SHELL_CODEC_FRAME_LENGTH; k++) { + abs_q = pulses[pulses_ptr + k]; + for (j = 0; j < nLS; j++) { + abs_q = Inlines.silk_LSHIFT(abs_q, 1); + abs_q += psRangeDec.dec_icdf(SilkTables.silk_lsb_iCDF, 8); + } + pulses[pulses_ptr + k] = (short) (abs_q); + } + /* Mark the number of pulses non-zero for sign decoding. */ + sum_pulses[i] |= nLS << 5; + } + } + + /** + * ************************************* + */ + /* Decode and add signs to pulse signal */ + /** + * ************************************* + */ + CodeSigns.silk_decode_signs(psRangeDec, pulses, frame_length, signalType, quantOffsetType, sum_pulses); + } +} diff --git a/app/src/main/java/org/concentus/DecoderAPIFlag.java b/app/src/main/java/org/concentus/DecoderAPIFlag.java new file mode 100644 index 0000000000..ad33a3d0a1 --- /dev/null +++ b/app/src/main/java/org/concentus/DecoderAPIFlag.java @@ -0,0 +1,39 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class DecoderAPIFlag { + + public static final int FLAG_DECODE_NORMAL = 0; + public static final int FLAG_PACKET_LOST = 1; + public static final int FLAG_DECODE_LBRR = 2; +} diff --git a/app/src/main/java/org/concentus/Downmix.java b/app/src/main/java/org/concentus/Downmix.java new file mode 100644 index 0000000000..c95bcc6a12 --- /dev/null +++ b/app/src/main/java/org/concentus/Downmix.java @@ -0,0 +1,78 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class Downmix { + + /// + /// + /// + /// The type of signal being handled (either short or float) + /// + /// + /// + /// + /// + /// + /// + static void downmix_int(short[] x, int x_ptr, int[] sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C) { + int scale; + int j; + for (j = 0; j < subframe; j++) { + sub[j + sub_ptr] = x[(j + offset) * C + c1]; + } + if (c2 > -1) { + for (j = 0; j < subframe; j++) { + sub[j + sub_ptr] += x[(j + offset) * C + c2]; + } + } else if (c2 == -2) { + int c; + for (c = 1; c < C; c++) { + for (j = 0; j < subframe; j++) { + sub[j + sub_ptr] += x[(j + offset) * C + c]; + } + } + } + scale = (1 << CeltConstants.SIG_SHIFT); + if (C == -2) { + scale /= C; + } else { + scale /= 2; + } + for (j = 0; j < subframe; j++) { + sub[j + sub_ptr] *= scale; + } + } +} diff --git a/app/src/main/java/org/concentus/EncControlState.java b/app/src/main/java/org/concentus/EncControlState.java new file mode 100644 index 0000000000..5da1308350 --- /dev/null +++ b/app/src/main/java/org/concentus/EncControlState.java @@ -0,0 +1,199 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Structure for controlling encoder operation +/// +class EncControlState { + + /* I: Number of channels; 1/2 */ + int nChannelsAPI = 0; + + /* I: Number of channels; 1/2 */ + int nChannelsInternal = 0; + + /* I: Input signal sampling rate in Hertz; 8000/12000/16000/24000/32000/44100/48000 */ + int API_sampleRate = 0; + + /* I: Maximum sampling rate in Hertz; 8000/12000/16000 */ + int maxInternalSampleRate = 0; + + /* I: Minimum sampling rate in Hertz; 8000/12000/16000 */ + int minInternalSampleRate = 0; + + /* I: Soft request for sampling rate in Hertz; 8000/12000/16000 */ + int desiredInternalSampleRate = 0; + + /* I: Number of samples per packet in milliseconds; 10/20/40/60 */ + int payloadSize_ms = 0; + + /* I: Bitrate during active speech in bits/second; internally limited */ + int bitRate = 0; + + /* I: Uplink packet loss in percent (0-100) */ + int packetLossPercentage = 0; + + /* I: Complexity mode; 0 is lowest, 10 is highest complexity */ + int complexity = 0; + + /* I: Flag to enable in-band Forward Error Correction (FEC); 0/1 */ + int useInBandFEC = 0; + + /* I: Flag to enable discontinuous transmission (DTX); 0/1 */ + int useDTX = 0; + + /* I: Flag to use constant bitrate */ + int useCBR = 0; + + /* I: Maximum number of bits allowed for the frame */ + int maxBits = 0; + + /* I: Causes a smooth downmix to mono */ + int toMono = 0; + + /* I: Opus encoder is allowing us to switch bandwidth */ + int opusCanSwitch = 0; + + /* I: Make frames as independent as possible (but still use LPC) */ + int reducedDependency = 0; + + /* O: Internal sampling rate used, in Hertz; 8000/12000/16000 */ + int internalSampleRate = 0; + + /* O: Flag that bandwidth switching is allowed (because low voice activity) */ + int allowBandwidthSwitch = 0; + + /* O: Flag that SILK runs in WB mode without variable LP filter (use for switching between WB/SWB/FB) */ + int inWBmodeWithoutVariableLP = 0; + + /* O: Stereo width */ + int stereoWidth_Q14 = 0; + + /* O: Tells the Opus encoder we're ready to switch */ + int switchReady = 0; + + void Reset() { + nChannelsAPI = 0; + nChannelsInternal = 0; + API_sampleRate = 0; + maxInternalSampleRate = 0; + minInternalSampleRate = 0; + desiredInternalSampleRate = 0; + payloadSize_ms = 0; + bitRate = 0; + packetLossPercentage = 0; + complexity = 0; + useInBandFEC = 0; + useDTX = 0; + useCBR = 0; + maxBits = 0; + toMono = 0; + opusCanSwitch = 0; + reducedDependency = 0; + internalSampleRate = 0; + allowBandwidthSwitch = 0; + inWBmodeWithoutVariableLP = 0; + stereoWidth_Q14 = 0; + switchReady = 0; + } + + /// + /// Checks this encoder control struct and returns error code, if any + /// + /// + int check_control_input() { + if (((API_sampleRate != 8000) + && (API_sampleRate != 12000) + && (API_sampleRate != 16000) + && (API_sampleRate != 24000) + && (API_sampleRate != 32000) + && (API_sampleRate != 44100) + && (API_sampleRate != 48000)) + || ((desiredInternalSampleRate != 8000) + && (desiredInternalSampleRate != 12000) + && (desiredInternalSampleRate != 16000)) + || ((maxInternalSampleRate != 8000) + && (maxInternalSampleRate != 12000) + && (maxInternalSampleRate != 16000)) + || ((minInternalSampleRate != 8000) + && (minInternalSampleRate != 12000) + && (minInternalSampleRate != 16000)) + || (minInternalSampleRate > desiredInternalSampleRate) + || (maxInternalSampleRate < desiredInternalSampleRate) + || (minInternalSampleRate > maxInternalSampleRate)) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_FS_NOT_SUPPORTED; + } + if (payloadSize_ms != 10 + && payloadSize_ms != 20 + && payloadSize_ms != 40 + && payloadSize_ms != 60) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_PACKET_SIZE_NOT_SUPPORTED; + } + if (packetLossPercentage < 0 || packetLossPercentage > 100) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_LOSS_RATE; + } + if (useDTX < 0 || useDTX > 1) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_DTX_SETTING; + } + if (useCBR < 0 || useCBR > 1) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_CBR_SETTING; + } + if (useInBandFEC < 0 || useInBandFEC > 1) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_INBAND_FEC_SETTING; + } + if (nChannelsAPI < 1 || nChannelsAPI > SilkConstants.ENCODER_NUM_CHANNELS) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR; + } + if (nChannelsInternal < 1 || nChannelsInternal > SilkConstants.ENCODER_NUM_CHANNELS) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR; + } + if (nChannelsInternal > nChannelsAPI) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR; + } + if (complexity < 0 || complexity > 10) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_COMPLEXITY_SETTING; + } + + return SilkError.SILK_NO_ERROR; + } +} diff --git a/app/src/main/java/org/concentus/EncodeAPI.java b/app/src/main/java/org/concentus/EncodeAPI.java new file mode 100644 index 0000000000..7d3ab4ef8e --- /dev/null +++ b/app/src/main/java/org/concentus/EncodeAPI.java @@ -0,0 +1,641 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class EncodeAPI { + + /// + /// Init or Reset encoder + /// + /// I/O State + /// I Run-time architecture + /// O Encoder Status + /// O Returns error code + static int silk_InitEncoder(SilkEncoder encState, EncControlState encStatus) { + int ret = SilkError.SILK_NO_ERROR; + + /* Reset encoder */ + encState.Reset(); + + for (int n = 0; n < SilkConstants.ENCODER_NUM_CHANNELS; n++) { + ret += SilkEncoder.silk_init_encoder(encState.state_Fxx[n]); + Inlines.OpusAssert(ret == SilkError.SILK_NO_ERROR); + } + + encState.nChannelsAPI = 1; + encState.nChannelsInternal = 1; + + /* Read control structure */ + ret += silk_QueryEncoder(encState, encStatus); + Inlines.OpusAssert(ret == SilkError.SILK_NO_ERROR); + + return ret; + } + + /// + /// Read control structure from encode + /// + /// I State + /// O Encoder Status + /// Returns error code + static int silk_QueryEncoder(SilkEncoder encState, EncControlState encStatus) { + int ret = SilkError.SILK_NO_ERROR; + SilkChannelEncoder state_Fxx = encState.state_Fxx[0]; + + encStatus.Reset(); + + encStatus.nChannelsAPI = encState.nChannelsAPI; + encStatus.nChannelsInternal = encState.nChannelsInternal; + encStatus.API_sampleRate = state_Fxx.API_fs_Hz; + encStatus.maxInternalSampleRate = state_Fxx.maxInternal_fs_Hz; + encStatus.minInternalSampleRate = state_Fxx.minInternal_fs_Hz; + encStatus.desiredInternalSampleRate = state_Fxx.desiredInternal_fs_Hz; + encStatus.payloadSize_ms = state_Fxx.PacketSize_ms; + encStatus.bitRate = state_Fxx.TargetRate_bps; + encStatus.packetLossPercentage = state_Fxx.PacketLoss_perc; + encStatus.complexity = state_Fxx.Complexity; + encStatus.useInBandFEC = state_Fxx.useInBandFEC; + encStatus.useDTX = state_Fxx.useDTX; + encStatus.useCBR = state_Fxx.useCBR; + encStatus.internalSampleRate = Inlines.silk_SMULBB(state_Fxx.fs_kHz, 1000); + encStatus.allowBandwidthSwitch = state_Fxx.allow_bandwidth_switch; + encStatus.inWBmodeWithoutVariableLP = (state_Fxx.fs_kHz == 16 && state_Fxx.sLP.mode == 0) ? 1 : 0; + + return ret; + } + + /// + /// Encode frame with Silk + /// Note: if prefillFlag is set, the input must contain 10 ms of audio, irrespective of what + /// encControl.payloadSize_ms is set to + /// + /// I/O State + /// I Control status + /// I Speech sample input vector + /// I Number of samples in input vector + /// I/O Compressor data structure + /// I/O Number of bytes in payload (input: Max bytes) + /// I Flag to indicate prefilling buffers no coding + /// error code + static int silk_Encode( + SilkEncoder psEnc, + EncControlState encControl, + short[] samplesIn, + int nSamplesIn, + EntropyCoder psRangeEnc, + BoxedValueInt nBytesOut, + int prefillFlag) { + int ret = SilkError.SILK_NO_ERROR; + int n, i, nBits, flags, tmp_payloadSize_ms = 0, tmp_complexity = 0; + int nSamplesToBuffer, nSamplesToBufferMax, nBlocksOf10ms; + int nSamplesFromInput = 0, nSamplesFromInputMax; + int speech_act_thr_for_switch_Q8; + int TargetRate_bps, channelRate_bps, LBRR_symbol, sum; + int[] MStargetRates_bps = new int[2]; + short[] buf; + int transition, curr_block, tot_blocks; + nBytesOut.Val = 0; + + if (encControl.reducedDependency != 0) { + psEnc.state_Fxx[0].first_frame_after_reset = 1; + psEnc.state_Fxx[1].first_frame_after_reset = 1; + } + psEnc.state_Fxx[0].nFramesEncoded = psEnc.state_Fxx[1].nFramesEncoded = 0; + + /* Check values in encoder control structure */ + ret += encControl.check_control_input(); + if (ret != SilkError.SILK_NO_ERROR) { + Inlines.OpusAssert(false); + return ret; + } + + encControl.switchReady = 0; + + if (encControl.nChannelsInternal > psEnc.nChannelsInternal) { + /* Mono . Stereo transition: init state of second channel and stereo state */ + ret += SilkEncoder.silk_init_encoder(psEnc.state_Fxx[1]); + + Arrays.MemSet(psEnc.sStereo.pred_prev_Q13, (short) 0, 2); + Arrays.MemSet(psEnc.sStereo.sSide, (short) 0, 2); + psEnc.sStereo.mid_side_amp_Q0[0] = 0; + psEnc.sStereo.mid_side_amp_Q0[1] = 1; + psEnc.sStereo.mid_side_amp_Q0[2] = 0; + psEnc.sStereo.mid_side_amp_Q0[3] = 1; + psEnc.sStereo.width_prev_Q14 = 0; + psEnc.sStereo.smth_width_Q14 = (short) (((int) ((1.0f) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/); + if (psEnc.nChannelsAPI == 2) { + psEnc.state_Fxx[1].resampler_state.Assign(psEnc.state_Fxx[0].resampler_state); + System.arraycopy(psEnc.state_Fxx[0].In_HP_State, 0, psEnc.state_Fxx[1].In_HP_State, 0, 2); + } + } + + transition = ((encControl.payloadSize_ms != psEnc.state_Fxx[0].PacketSize_ms) || (psEnc.nChannelsInternal != encControl.nChannelsInternal)) ? 1 : 0; + + psEnc.nChannelsAPI = encControl.nChannelsAPI; + psEnc.nChannelsInternal = encControl.nChannelsInternal; + + nBlocksOf10ms = Inlines.silk_DIV32(100 * nSamplesIn, encControl.API_sampleRate); + tot_blocks = (nBlocksOf10ms > 1) ? nBlocksOf10ms >> 1 : 1; + curr_block = 0; + if (prefillFlag != 0) { + /* Only accept input length of 10 ms */ + if (nBlocksOf10ms != 1) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES; + } + /* Reset Encoder */ + for (n = 0; n < encControl.nChannelsInternal; n++) { + ret += SilkEncoder.silk_init_encoder(psEnc.state_Fxx[n]); + Inlines.OpusAssert(ret == SilkError.SILK_NO_ERROR); + } + tmp_payloadSize_ms = encControl.payloadSize_ms; + encControl.payloadSize_ms = 10; + tmp_complexity = encControl.complexity; + encControl.complexity = 0; + for (n = 0; n < encControl.nChannelsInternal; n++) { + psEnc.state_Fxx[n].controlled_since_last_payload = 0; + psEnc.state_Fxx[n].prefillFlag = 1; + } + } else { + /* Only accept input lengths that are a multiple of 10 ms */ + if (nBlocksOf10ms * encControl.API_sampleRate != 100 * nSamplesIn || nSamplesIn < 0) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES; + } + /* Make sure no more than one packet can be produced */ + if (1000 * (int) nSamplesIn > encControl.payloadSize_ms * encControl.API_sampleRate) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES; + } + } + + TargetRate_bps = Inlines.silk_RSHIFT32(encControl.bitRate, encControl.nChannelsInternal - 1); + + for (n = 0; n < encControl.nChannelsInternal; n++) { + /* Force the side channel to the same rate as the mid */ + int force_fs_kHz = (n == 1) ? psEnc.state_Fxx[0].fs_kHz : 0; + ret += psEnc.state_Fxx[n].silk_control_encoder(encControl, TargetRate_bps, psEnc.allowBandwidthSwitch, n, force_fs_kHz); + + if (ret != SilkError.SILK_NO_ERROR) { + Inlines.OpusAssert(false); + return ret; + } + + if (psEnc.state_Fxx[n].first_frame_after_reset != 0 || transition != 0) { + for (i = 0; i < psEnc.state_Fxx[0].nFramesPerPacket; i++) { + psEnc.state_Fxx[n].LBRR_flags[i] = 0; + } + } + + psEnc.state_Fxx[n].inDTX = psEnc.state_Fxx[n].useDTX; + } + + Inlines.OpusAssert(encControl.nChannelsInternal == 1 || psEnc.state_Fxx[0].fs_kHz == psEnc.state_Fxx[1].fs_kHz); + + /* Input buffering/resampling and encoding */ + nSamplesToBufferMax = 10 * nBlocksOf10ms * psEnc.state_Fxx[0].fs_kHz; + nSamplesFromInputMax + = Inlines.silk_DIV32_16(nSamplesToBufferMax + * psEnc.state_Fxx[0].API_fs_Hz, + (short) (psEnc.state_Fxx[0].fs_kHz * 1000)); + + buf = new short[nSamplesFromInputMax]; + + int samplesIn_ptr = 0; + while (true) { + nSamplesToBuffer = psEnc.state_Fxx[0].frame_length - psEnc.state_Fxx[0].inputBufIx; + nSamplesToBuffer = Inlines.silk_min(nSamplesToBuffer, nSamplesToBufferMax); + nSamplesFromInput = Inlines.silk_DIV32_16(nSamplesToBuffer * psEnc.state_Fxx[0].API_fs_Hz, psEnc.state_Fxx[0].fs_kHz * 1000); + + /* Resample and write to buffer */ + if (encControl.nChannelsAPI == 2 && encControl.nChannelsInternal == 2) { + int id = psEnc.state_Fxx[0].nFramesEncoded; + for (n = 0; n < nSamplesFromInput; n++) { + buf[n] = samplesIn[samplesIn_ptr + (2 * n)]; + } + + /* Making sure to start both resamplers from the same state when switching from mono to stereo */ + if (psEnc.nPrevChannelsInternal == 1 && id == 0) { + //silk_memcpy(&psEnc.state_Fxx[1].resampler_state, &psEnc.state_Fxx[0].resampler_state, sizeof(psEnc.state_Fxx[1].resampler_state)); + psEnc.state_Fxx[1].resampler_state.Assign(psEnc.state_Fxx[0].resampler_state); + } + + ret += Resampler.silk_resampler( + psEnc.state_Fxx[0].resampler_state, + psEnc.state_Fxx[0].inputBuf, + psEnc.state_Fxx[0].inputBufIx + 2, + buf, + 0, + nSamplesFromInput); + + psEnc.state_Fxx[0].inputBufIx += nSamplesToBuffer; + + nSamplesToBuffer = psEnc.state_Fxx[1].frame_length - psEnc.state_Fxx[1].inputBufIx; + nSamplesToBuffer = Inlines.silk_min(nSamplesToBuffer, 10 * nBlocksOf10ms * psEnc.state_Fxx[1].fs_kHz); + for (n = 0; n < nSamplesFromInput; n++) { + buf[n] = samplesIn[samplesIn_ptr + (2 * n) + 1]; + } + ret += Resampler.silk_resampler( + psEnc.state_Fxx[1].resampler_state, + psEnc.state_Fxx[1].inputBuf, + psEnc.state_Fxx[1].inputBufIx + 2, + buf, + 0, + nSamplesFromInput); + + psEnc.state_Fxx[1].inputBufIx += nSamplesToBuffer; + } else if (encControl.nChannelsAPI == 2 && encControl.nChannelsInternal == 1) { + /* Combine left and right channels before resampling */ + for (n = 0; n < nSamplesFromInput; n++) { + sum = samplesIn[samplesIn_ptr + (2 * n)] + samplesIn[samplesIn_ptr + (2 * n) + 1]; + buf[n] = (short) Inlines.silk_RSHIFT_ROUND(sum, 1); + } + + ret += Resampler.silk_resampler( + psEnc.state_Fxx[0].resampler_state, + psEnc.state_Fxx[0].inputBuf, + psEnc.state_Fxx[0].inputBufIx + 2, + buf, + 0, + nSamplesFromInput); + + /* On the first mono frame, average the results for the two resampler states */ + if (psEnc.nPrevChannelsInternal == 2 && psEnc.state_Fxx[0].nFramesEncoded == 0) { + ret += Resampler.silk_resampler( + psEnc.state_Fxx[1].resampler_state, + psEnc.state_Fxx[1].inputBuf, + psEnc.state_Fxx[1].inputBufIx + 2, + buf, + 0, + nSamplesFromInput); + + for (n = 0; n < psEnc.state_Fxx[0].frame_length; n++) { + psEnc.state_Fxx[0].inputBuf[psEnc.state_Fxx[0].inputBufIx + n + 2] + = (short) (Inlines.silk_RSHIFT(psEnc.state_Fxx[0].inputBuf[psEnc.state_Fxx[0].inputBufIx + n + 2] + + psEnc.state_Fxx[1].inputBuf[psEnc.state_Fxx[1].inputBufIx + n + 2], 1)); + } + } + + psEnc.state_Fxx[0].inputBufIx += nSamplesToBuffer; + } else { + Inlines.OpusAssert(encControl.nChannelsAPI == 1 && encControl.nChannelsInternal == 1); + System.arraycopy(samplesIn, samplesIn_ptr, buf, 0, nSamplesFromInput); + ret += Resampler.silk_resampler( + psEnc.state_Fxx[0].resampler_state, + psEnc.state_Fxx[0].inputBuf, + psEnc.state_Fxx[0].inputBufIx + 2, + buf, + 0, + nSamplesFromInput); + + psEnc.state_Fxx[0].inputBufIx += nSamplesToBuffer; + } + + samplesIn_ptr += (nSamplesFromInput * encControl.nChannelsAPI); + nSamplesIn -= nSamplesFromInput; + + /* Default */ + psEnc.allowBandwidthSwitch = 0; + + /* Silk encoder */ + if (psEnc.state_Fxx[0].inputBufIx >= psEnc.state_Fxx[0].frame_length) { + /* Enough data in input buffer, so encode */ + Inlines.OpusAssert(psEnc.state_Fxx[0].inputBufIx == psEnc.state_Fxx[0].frame_length); + Inlines.OpusAssert(encControl.nChannelsInternal == 1 || psEnc.state_Fxx[1].inputBufIx == psEnc.state_Fxx[1].frame_length); + + /* Deal with LBRR data */ + if (psEnc.state_Fxx[0].nFramesEncoded == 0 && prefillFlag == 0) { + /* Create space at start of payload for VAD and FEC flags */ + short[] iCDF = {0, 0}; + iCDF[0] = (short) (256 - Inlines.silk_RSHIFT(256, (psEnc.state_Fxx[0].nFramesPerPacket + 1) * encControl.nChannelsInternal)); + psRangeEnc.enc_icdf(0, iCDF, 8); + + /* Encode any LBRR data from previous packet */ + /* Encode LBRR flags */ + for (n = 0; n < encControl.nChannelsInternal; n++) { + LBRR_symbol = 0; + for (i = 0; i < psEnc.state_Fxx[n].nFramesPerPacket; i++) { + LBRR_symbol |= Inlines.silk_LSHIFT(psEnc.state_Fxx[n].LBRR_flags[i], i); + } + + psEnc.state_Fxx[n].LBRR_flag = (byte) (LBRR_symbol > 0 ? 1 : 0); + if (LBRR_symbol != 0 && psEnc.state_Fxx[n].nFramesPerPacket > 1) { + psRangeEnc.enc_icdf(LBRR_symbol - 1, SilkTables.silk_LBRR_flags_iCDF_ptr[psEnc.state_Fxx[n].nFramesPerPacket - 2], 8); + } + } + + /* Code LBRR indices and excitation signals */ + for (i = 0; i < psEnc.state_Fxx[0].nFramesPerPacket; i++) { + for (n = 0; n < encControl.nChannelsInternal; n++) { + if (psEnc.state_Fxx[n].LBRR_flags[i] != 0) { + int condCoding; + + if (encControl.nChannelsInternal == 2 && n == 0) { + Stereo.silk_stereo_encode_pred(psRangeEnc, psEnc.sStereo.predIx[i]); + /* For LBRR data there's no need to code the mid-only flag if the side-channel LBRR flag is set */ + if (psEnc.state_Fxx[1].LBRR_flags[i] == 0) { + Stereo.silk_stereo_encode_mid_only(psRangeEnc, psEnc.sStereo.mid_only_flags[i]); + } + } + + /* Use conditional coding if previous frame available */ + if (i > 0 && psEnc.state_Fxx[n].LBRR_flags[i - 1] != 0) { + condCoding = SilkConstants.CODE_CONDITIONALLY; + } else { + condCoding = SilkConstants.CODE_INDEPENDENTLY; + } + + EncodeIndices.silk_encode_indices(psEnc.state_Fxx[n], psRangeEnc, i, 1, condCoding); + EncodePulses.silk_encode_pulses(psRangeEnc, psEnc.state_Fxx[n].indices_LBRR[i].signalType, psEnc.state_Fxx[n].indices_LBRR[i].quantOffsetType, + psEnc.state_Fxx[n].pulses_LBRR[i], psEnc.state_Fxx[n].frame_length); + } + } + } + + /* Reset LBRR flags */ + for (n = 0; n < encControl.nChannelsInternal; n++) { + Arrays.MemSet(psEnc.state_Fxx[n].LBRR_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET); + } + + psEnc.nBitsUsedLBRR = psRangeEnc.tell(); + } + + HPVariableCutoff.silk_HP_variable_cutoff(psEnc.state_Fxx); + + /* Total target bits for packet */ + nBits = Inlines.silk_DIV32_16(Inlines.silk_MUL(encControl.bitRate, encControl.payloadSize_ms), 1000); + + /* Subtract bits used for LBRR */ + if (prefillFlag == 0) { + nBits -= psEnc.nBitsUsedLBRR; + } + + /* Divide by number of uncoded frames left in packet */ + nBits = Inlines.silk_DIV32_16(nBits, psEnc.state_Fxx[0].nFramesPerPacket); + + /* Convert to bits/second */ + if (encControl.payloadSize_ms == 10) { + TargetRate_bps = Inlines.silk_SMULBB(nBits, 100); + } else { + TargetRate_bps = Inlines.silk_SMULBB(nBits, 50); + } + + /* Subtract fraction of bits in excess of target in previous frames and packets */ + TargetRate_bps -= Inlines.silk_DIV32_16(Inlines.silk_MUL(psEnc.nBitsExceeded, 1000), TuningParameters.BITRESERVOIR_DECAY_TIME_MS); + + if (prefillFlag == 0 && psEnc.state_Fxx[0].nFramesEncoded > 0) { + /* Compare actual vs target bits so far in this packet */ + int bitsBalance = psRangeEnc.tell() - psEnc.nBitsUsedLBRR - nBits * psEnc.state_Fxx[0].nFramesEncoded; + TargetRate_bps -= Inlines.silk_DIV32_16(Inlines.silk_MUL(bitsBalance, 1000), TuningParameters.BITRESERVOIR_DECAY_TIME_MS); + } + + /* Never exceed input bitrate */ + TargetRate_bps = Inlines.silk_LIMIT(TargetRate_bps, encControl.bitRate, 5000); + + /* Convert Left/Right to Mid/Side */ + if (encControl.nChannelsInternal == 2) { + BoxedValueByte midOnlyFlagBoxed = new BoxedValueByte(psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded]); + Stereo.silk_stereo_LR_to_MS(psEnc.sStereo, + psEnc.state_Fxx[0].inputBuf, + 2, + psEnc.state_Fxx[1].inputBuf, + 2, + psEnc.sStereo.predIx[psEnc.state_Fxx[0].nFramesEncoded], + midOnlyFlagBoxed, + MStargetRates_bps, + TargetRate_bps, + psEnc.state_Fxx[0].speech_activity_Q8, + encControl.toMono, + psEnc.state_Fxx[0].fs_kHz, + psEnc.state_Fxx[0].frame_length); + + psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded] = midOnlyFlagBoxed.Val; + + if (midOnlyFlagBoxed.Val == 0) { + /* Reset side channel encoder memory for first frame with side coding */ + if (psEnc.prev_decode_only_middle == 1) { + psEnc.state_Fxx[1].sShape.Reset(); + psEnc.state_Fxx[1].sPrefilt.Reset(); + psEnc.state_Fxx[1].sNSQ.Reset(); + Arrays.MemSet(psEnc.state_Fxx[1].prev_NLSFq_Q15, (short) 0, SilkConstants.MAX_LPC_ORDER); + Arrays.MemSet(psEnc.state_Fxx[1].sLP.In_LP_State, 0, 2); + + psEnc.state_Fxx[1].prevLag = 100; + psEnc.state_Fxx[1].sNSQ.lagPrev = 100; + psEnc.state_Fxx[1].sShape.LastGainIndex = 10; + psEnc.state_Fxx[1].prevSignalType = SilkConstants.TYPE_NO_VOICE_ACTIVITY; + psEnc.state_Fxx[1].sNSQ.prev_gain_Q16 = 65536; + psEnc.state_Fxx[1].first_frame_after_reset = 1; + } + + psEnc.state_Fxx[1].silk_encode_do_VAD(); + } else { + psEnc.state_Fxx[1].VAD_flags[psEnc.state_Fxx[0].nFramesEncoded] = 0; + } + + if (prefillFlag == 0) { + Stereo.silk_stereo_encode_pred(psRangeEnc, psEnc.sStereo.predIx[psEnc.state_Fxx[0].nFramesEncoded]); + if (psEnc.state_Fxx[1].VAD_flags[psEnc.state_Fxx[0].nFramesEncoded] == 0) { + Stereo.silk_stereo_encode_mid_only(psRangeEnc, psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded]); + } + } + } else { + /* Buffering */ + System.arraycopy(psEnc.sStereo.sMid, 0, psEnc.state_Fxx[0].inputBuf, 0, 2); + System.arraycopy(psEnc.state_Fxx[0].inputBuf, psEnc.state_Fxx[0].frame_length, psEnc.sStereo.sMid, 0, 2); + } + + psEnc.state_Fxx[0].silk_encode_do_VAD(); + + /* Encode */ + for (n = 0; n < encControl.nChannelsInternal; n++) { + int maxBits, useCBR; + + /* Handling rate constraints */ + maxBits = encControl.maxBits; + if (tot_blocks == 2 && curr_block == 0) { + maxBits = maxBits * 3 / 5; + } else if (tot_blocks == 3) { + if (curr_block == 0) { + maxBits = maxBits * 2 / 5; + } else if (curr_block == 1) { + maxBits = maxBits * 3 / 4; + } + } + + useCBR = (encControl.useCBR != 0 && curr_block == tot_blocks - 1) ? 1 : 0; + + if (encControl.nChannelsInternal == 1) { + channelRate_bps = TargetRate_bps; + } else { + channelRate_bps = MStargetRates_bps[n]; + if (n == 0 && MStargetRates_bps[1] > 0) { + useCBR = 0; + /* Give mid up to 1/2 of the max bits for that frame */ + maxBits -= encControl.maxBits / (tot_blocks * 2); + } + } + + if (channelRate_bps > 0) { + int condCoding; + + psEnc.state_Fxx[n].silk_control_SNR(channelRate_bps); + + /* Use independent coding if no previous frame available */ + if (psEnc.state_Fxx[0].nFramesEncoded - n <= 0) { + condCoding = SilkConstants.CODE_INDEPENDENTLY; + } else if (n > 0 && psEnc.prev_decode_only_middle != 0) { + /* If we skipped a side frame in this packet, we don't + need LTP scaling; the LTP state is well-defined. */ + condCoding = SilkConstants.CODE_INDEPENDENTLY_NO_LTP_SCALING; + } else { + condCoding = SilkConstants.CODE_CONDITIONALLY; + } + + ret += psEnc.state_Fxx[n].silk_encode_frame(nBytesOut, psRangeEnc, condCoding, maxBits, useCBR); + Inlines.OpusAssert(ret == SilkError.SILK_NO_ERROR); + } + + psEnc.state_Fxx[n].controlled_since_last_payload = 0; + psEnc.state_Fxx[n].inputBufIx = 0; + psEnc.state_Fxx[n].nFramesEncoded++; + } + + psEnc.prev_decode_only_middle = psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded - 1]; + + /* Insert VAD and FEC flags at beginning of bitstream */ + if (nBytesOut.Val > 0 && psEnc.state_Fxx[0].nFramesEncoded == psEnc.state_Fxx[0].nFramesPerPacket) { + flags = 0; + for (n = 0; n < encControl.nChannelsInternal; n++) { + for (i = 0; i < psEnc.state_Fxx[n].nFramesPerPacket; i++) { + flags = Inlines.silk_LSHIFT(flags, 1); + flags |= (int) psEnc.state_Fxx[n].VAD_flags[i]; + } + flags = Inlines.silk_LSHIFT(flags, 1); + flags |= (int) psEnc.state_Fxx[n].LBRR_flag; + } + + if (prefillFlag == 0) { + psRangeEnc.enc_patch_initial_bits(flags, ((psEnc.state_Fxx[0].nFramesPerPacket + 1) * encControl.nChannelsInternal)); + } + + /* Return zero bytes if all channels DTXed */ + if (psEnc.state_Fxx[0].inDTX != 0 && (encControl.nChannelsInternal == 1 || psEnc.state_Fxx[1].inDTX != 0)) { + nBytesOut.Val = 0; + } + + psEnc.nBitsExceeded += nBytesOut.Val * 8; + psEnc.nBitsExceeded -= Inlines.silk_DIV32_16(Inlines.silk_MUL(encControl.bitRate, encControl.payloadSize_ms), 1000); + psEnc.nBitsExceeded = Inlines.silk_LIMIT(psEnc.nBitsExceeded, 0, 10000); + + /* Update flag indicating if bandwidth switching is allowed */ + speech_act_thr_for_switch_Q8 = Inlines.silk_SMLAWB(((int) ((TuningParameters.SPEECH_ACTIVITY_DTX_THRES) * ((long) 1 << (8)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SPEECH_ACTIVITY_DTX_THRES, 8)*/, + ((int) (((1 - TuningParameters.SPEECH_ACTIVITY_DTX_THRES) / TuningParameters.MAX_BANDWIDTH_SWITCH_DELAY_MS) * ((long) 1 << (16 + 8)) + 0.5))/*Inlines.SILK_CONST((1 - TuningParameters.SPEECH_ACTIVITY_DTX_THRES) / TuningParameters.MAX_BANDWIDTH_SWITCH_DELAY_MS, 16 + 8)*/, + psEnc.timeSinceSwitchAllowed_ms); + if (psEnc.state_Fxx[0].speech_activity_Q8 < speech_act_thr_for_switch_Q8) { + psEnc.allowBandwidthSwitch = 1; + psEnc.timeSinceSwitchAllowed_ms = 0; + } else { + psEnc.allowBandwidthSwitch = 0; + psEnc.timeSinceSwitchAllowed_ms += encControl.payloadSize_ms; + } + } + + if (nSamplesIn == 0) { + break; + } + } else { + break; + } + + curr_block++; + } + + psEnc.nPrevChannelsInternal = encControl.nChannelsInternal; + + encControl.allowBandwidthSwitch = psEnc.allowBandwidthSwitch; + encControl.inWBmodeWithoutVariableLP = (psEnc.state_Fxx[0].fs_kHz == 16 && psEnc.state_Fxx[0].sLP.mode == 0) ? 1 : 0; + encControl.internalSampleRate = Inlines.silk_SMULBB(psEnc.state_Fxx[0].fs_kHz, 1000); + encControl.stereoWidth_Q14 = encControl.toMono != 0 ? 0 : psEnc.sStereo.smth_width_Q14; + + if (prefillFlag != 0) { + encControl.payloadSize_ms = tmp_payloadSize_ms; + encControl.complexity = tmp_complexity; + + for (n = 0; n < encControl.nChannelsInternal; n++) { + psEnc.state_Fxx[n].controlled_since_last_payload = 0; + psEnc.state_Fxx[n].prefillFlag = 0; + } + } + + return ret; + } +} + +//────────────────────▄▄▄▄▄▄▄▄▄▄──────────────────── +//─────────────▄▄▄▄████▓▓▓▓▓▓▓▓▓██▄▄──────────────── +//─────────▄▄██▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒░▀▀▄───────────── +//────────▀▀▀▀▀█████▓▓▓▓▒▒▒▒▒▒▒▒▒▒░░░░░▀▄─────────── +//────────────▄▄█▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░▀▄───────── +//─────▄▀▀▄─▄█▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░█▄▀▀▄───── +//────█░░░▄█▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░█░░░░░█──── +//───█░░░█▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░▄▀░░░▄░░█─── +//──█░░░█▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░█░░░░░█░░█── +//──█░░█▓▒▒▄▄▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░██░░░░░░█░█── +//─█░░█▓▄▄▀█▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░▄▄▀█░░░░▄▀█░░░░░░█░░█─ +//─█░██▀░▄▀▒▒▒▒▒▒▒▒▒▒▒▒▄▄▄▄▀▀▀░▄▀░░░▄▀░░░░░░░░░█░░█─ +//─██▀░░█▒▒▒▒▒▒▒▒▒▄▄▀▀▀░░░░░░▄▀░░▄▄▀░░░░░░░░░░░░░░█─ +//──█░░█▒▒▒▒▒▒▒▄▀▀░░░░░░░░░░▀▀▀▀▀░░░░░░░░░░▄▄░░░░░█─ +//───██▒▒▒▒▒▒▄▀██▄▄░░░░░█░░░░░░░░░░░░░▄▄▄█▀░░░░░░█── +//────█▒▒▒▒▒█─▓██████▄▄█░░░█░░░░▄▄▄████▓─█▀▀░░░░█─── +//───█▒▒▒▒▄█─▓█──████▓─█░░░░████████──█▓─█▄░░░▄▀──── +//───█▒▒▄▀█──▓█▒▓████▓─█░░░░█─▓█████▓▒█▓─█░░█▀────── +//──█▒▄▀█░█──▓███─███▓─█░░░░█─▓████─██▓──█░█──────── +//──█▀──█░█──▓▓██████▓─█░░░░█─▓██████▓▓──█░█──────── +//──────█░░█──▓▓████▓─█░░░░░░█─▓████▓▓──█░░█──────── +//──────█░░▀▄───▓▓▓▓──█░░░░░░█──▓▓▓▓───▄▀░░█──────── +//───────█░░▀▄───────█░░░░░░░░█───────▄▀░░█───────── +//───────█░░░░▀▄▄▄▄▄▀░░▄▀▀▀▀▄░░▀▄▄▄▄▄▀░░░░█───────── +//────────█░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█────────── +//─────────█░░░░░░░░░░▀▄░░░░▄▀░░░░░░░░░░█─────────── +//──────────▀▄░░░░░░░░░░░░░░░░░░░░░░░░▄▀──────────── +//────────────▀▄░░░░░░▀▄░░░░▄▀░░░░░░▄▀────────────── +//──────────────▀▄▄░░░░░▀▀▀▀░░░░░▄▄▀──────────────── +//─────────────────▀▀▄▄▄▄▄▄▄▄▄▄▀▀─────────────────── +//────────────────────────────────────────────────── +//───────█───█───█─████──███──███──█───█─████─█───── +//──────█─█──█───█─█────█────█───█─██─██─█────█───── +//─────█▄▄▄█─█─█─█─██────██──█───█─█─█─█─██───█───── +//─────█───█──█─█──█───────█─█───█─█───█─█────────── +//─────█───█──█─█──████─███───███──█───█─████─█───── +//────────────────────────────────────────────────── +//────────────────────────────────────────────────── diff --git a/app/src/main/java/org/concentus/EncodeIndices.java b/app/src/main/java/org/concentus/EncodeIndices.java new file mode 100644 index 0000000000..719ca835ba --- /dev/null +++ b/app/src/main/java/org/concentus/EncodeIndices.java @@ -0,0 +1,219 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class EncodeIndices { + + /// + /// Encode side-information parameters to payload + /// + /// I/O Encoder state + /// I/O Compressor data structure + /// I Frame number + /// I Flag indicating LBRR data is being encoded + /// I The type of conditional coding to use + static void silk_encode_indices( + SilkChannelEncoder psEncC, + EntropyCoder psRangeEnc, + int FrameIndex, + int encode_LBRR, + int condCoding) { + int i, k, typeOffset; + int encode_absolute_lagIndex, delta_lagIndex; + short[] ec_ix = new short[SilkConstants.MAX_LPC_ORDER]; + short[] pred_Q8 = new short[SilkConstants.MAX_LPC_ORDER]; + SideInfoIndices psIndices; + + if (encode_LBRR != 0) { + psIndices = psEncC.indices_LBRR[FrameIndex]; + } else { + psIndices = psEncC.indices; + } + + /** + * **************************************** + */ + /* Encode signal type and quantizer offset */ + /** + * **************************************** + */ + typeOffset = 2 * psIndices.signalType + psIndices.quantOffsetType; + Inlines.OpusAssert(typeOffset >= 0 && typeOffset < 6); + Inlines.OpusAssert(encode_LBRR == 0 || typeOffset >= 2); + if (encode_LBRR != 0 || typeOffset >= 2) { + psRangeEnc.enc_icdf(typeOffset - 2, SilkTables.silk_type_offset_VAD_iCDF, 8); + } else { + psRangeEnc.enc_icdf(typeOffset, SilkTables.silk_type_offset_no_VAD_iCDF, 8); + } + + /** + * ************* + */ + /* Encode gains */ + /** + * ************* + */ + /* first subframe */ + if (condCoding == SilkConstants.CODE_CONDITIONALLY) { + /* conditional coding */ + Inlines.OpusAssert(psIndices.GainsIndices[0] >= 0 && psIndices.GainsIndices[0] < SilkConstants.MAX_DELTA_GAIN_QUANT - SilkConstants.MIN_DELTA_GAIN_QUANT + 1); + psRangeEnc.enc_icdf(psIndices.GainsIndices[0], SilkTables.silk_delta_gain_iCDF, 8); + } else { + /* independent coding, in two stages: MSB bits followed by 3 LSBs */ + Inlines.OpusAssert(psIndices.GainsIndices[0] >= 0 && psIndices.GainsIndices[0] < SilkConstants.N_LEVELS_QGAIN); + psRangeEnc.enc_icdf(Inlines.silk_RSHIFT(psIndices.GainsIndices[0], 3), SilkTables.silk_gain_iCDF[psIndices.signalType], 8); + psRangeEnc.enc_icdf(psIndices.GainsIndices[0] & 7, SilkTables.silk_uniform8_iCDF, 8); + } + + /* remaining subframes */ + for (i = 1; i < psEncC.nb_subfr; i++) { + Inlines.OpusAssert(psIndices.GainsIndices[i] >= 0 && psIndices.GainsIndices[i] < SilkConstants.MAX_DELTA_GAIN_QUANT - SilkConstants.MIN_DELTA_GAIN_QUANT + 1); + psRangeEnc.enc_icdf(psIndices.GainsIndices[i], SilkTables.silk_delta_gain_iCDF, 8); + } + + /** + * ************* + */ + /* Encode NLSFs */ + /** + * ************* + */ + psRangeEnc.enc_icdf(psIndices.NLSFIndices[0], psEncC.psNLSF_CB.CB1_iCDF, ((psIndices.signalType >> 1) * psEncC.psNLSF_CB.nVectors), 8); + NLSF.silk_NLSF_unpack(ec_ix, pred_Q8, psEncC.psNLSF_CB, psIndices.NLSFIndices[0]); + Inlines.OpusAssert(psEncC.psNLSF_CB.order == psEncC.predictLPCOrder); + + for (i = 0; i < psEncC.psNLSF_CB.order; i++) { + if (psIndices.NLSFIndices[i + 1] >= SilkConstants.NLSF_QUANT_MAX_AMPLITUDE) { + psRangeEnc.enc_icdf(2 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE, psEncC.psNLSF_CB.ec_iCDF, (ec_ix[i]), 8); + psRangeEnc.enc_icdf(psIndices.NLSFIndices[i + 1] - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE, SilkTables.silk_NLSF_EXT_iCDF, 8); + } else if (psIndices.NLSFIndices[i + 1] <= 0 - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE) { + psRangeEnc.enc_icdf(0, psEncC.psNLSF_CB.ec_iCDF, ec_ix[i], 8); + psRangeEnc.enc_icdf(-psIndices.NLSFIndices[i + 1] - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE, SilkTables.silk_NLSF_EXT_iCDF, 8); + } else { + psRangeEnc.enc_icdf(psIndices.NLSFIndices[i + 1] + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE, psEncC.psNLSF_CB.ec_iCDF, ec_ix[i], 8); + } + } + + /* Encode NLSF interpolation factor */ + if (psEncC.nb_subfr == SilkConstants.MAX_NB_SUBFR) { + Inlines.OpusAssert(psIndices.NLSFInterpCoef_Q2 >= 0 && psIndices.NLSFInterpCoef_Q2 < 5); + psRangeEnc.enc_icdf(psIndices.NLSFInterpCoef_Q2, SilkTables.silk_NLSF_interpolation_factor_iCDF, 8); + } + + if (psIndices.signalType == SilkConstants.TYPE_VOICED) { + /** + * ****************** + */ + /* Encode pitch lags */ + /** + * ****************** + */ + /* lag index */ + encode_absolute_lagIndex = 1; + if (condCoding == SilkConstants.CODE_CONDITIONALLY && psEncC.ec_prevSignalType == SilkConstants.TYPE_VOICED) { + /* Delta Encoding */ + delta_lagIndex = psIndices.lagIndex - psEncC.ec_prevLagIndex; + + if (delta_lagIndex < -8 || delta_lagIndex > 11) { + delta_lagIndex = 0; + } else { + delta_lagIndex = delta_lagIndex + 9; + encode_absolute_lagIndex = 0; + /* Only use delta */ + } + + Inlines.OpusAssert(delta_lagIndex >= 0 && delta_lagIndex < 21); + psRangeEnc.enc_icdf(delta_lagIndex, SilkTables.silk_pitch_delta_iCDF, 8); + } + + if (encode_absolute_lagIndex != 0) { + /* Absolute encoding */ + int pitch_high_bits, pitch_low_bits; + pitch_high_bits = Inlines.silk_DIV32_16(psIndices.lagIndex, Inlines.silk_RSHIFT(psEncC.fs_kHz, 1)); + pitch_low_bits = psIndices.lagIndex - Inlines.silk_SMULBB(pitch_high_bits, Inlines.silk_RSHIFT(psEncC.fs_kHz, 1)); + Inlines.OpusAssert(pitch_low_bits < psEncC.fs_kHz / 2); + Inlines.OpusAssert(pitch_high_bits < 32); + psRangeEnc.enc_icdf(pitch_high_bits, SilkTables.silk_pitch_lag_iCDF, 8); + psRangeEnc.enc_icdf(pitch_low_bits, psEncC.pitch_lag_low_bits_iCDF, 8); + } + psEncC.ec_prevLagIndex = psIndices.lagIndex; + + /* Countour index */ + Inlines.OpusAssert(psIndices.contourIndex >= 0); + Inlines.OpusAssert((psIndices.contourIndex < 34 && psEncC.fs_kHz > 8 && psEncC.nb_subfr == 4) || (psIndices.contourIndex < 11 && psEncC.fs_kHz == 8 && psEncC.nb_subfr == 4) || (psIndices.contourIndex < 12 && psEncC.fs_kHz > 8 && psEncC.nb_subfr == 2) || (psIndices.contourIndex < 3 && psEncC.fs_kHz == 8 && psEncC.nb_subfr == 2)); + psRangeEnc.enc_icdf(psIndices.contourIndex, psEncC.pitch_contour_iCDF, 8); + + /** + * ***************** + */ + /* Encode LTP gains */ + /** + * ***************** + */ + /* PERIndex value */ + Inlines.OpusAssert(psIndices.PERIndex >= 0 && psIndices.PERIndex < 3); + psRangeEnc.enc_icdf(psIndices.PERIndex, SilkTables.silk_LTP_per_index_iCDF, 8); + + /* Codebook Indices */ + for (k = 0; k < psEncC.nb_subfr; k++) { + Inlines.OpusAssert(psIndices.LTPIndex[k] >= 0 && psIndices.LTPIndex[k] < (8 << psIndices.PERIndex)); + psRangeEnc.enc_icdf(psIndices.LTPIndex[k], SilkTables.silk_LTP_gain_iCDF_ptrs[psIndices.PERIndex], 8); + } + + /** + * ******************* + */ + /* Encode LTP scaling */ + /** + * ******************* + */ + if (condCoding == SilkConstants.CODE_INDEPENDENTLY) { + Inlines.OpusAssert(psIndices.LTP_scaleIndex >= 0 && psIndices.LTP_scaleIndex < 3); + psRangeEnc.enc_icdf(psIndices.LTP_scaleIndex, SilkTables.silk_LTPscale_iCDF, 8); + } + + Inlines.OpusAssert(condCoding == 0 || psIndices.LTP_scaleIndex == 0); + } + + psEncC.ec_prevSignalType = psIndices.signalType; + + /** + * ************ + */ + /* Encode seed */ + /** + * ************ + */ + Inlines.OpusAssert(psIndices.Seed >= 0 && psIndices.Seed < 4); + psRangeEnc.enc_icdf(psIndices.Seed, SilkTables.silk_uniform4_iCDF, 8); + } +} diff --git a/app/src/main/java/org/concentus/EncodePulses.java b/app/src/main/java/org/concentus/EncodePulses.java new file mode 100644 index 0000000000..b4e77f2360 --- /dev/null +++ b/app/src/main/java/org/concentus/EncodePulses.java @@ -0,0 +1,264 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class EncodePulses { + + /// + /// + /// + /// (O) + /// (I) + /// I max value for sum of pulses + /// I number of output values + /// return ok + static int combine_and_check( + int[] pulses_comb, + int pulses_comb_ptr, + int[] pulses_in, + int pulses_in_ptr, + int max_pulses, + int len) { + for (int k = 0; k < len; k++) { + int k2p = 2 * k + pulses_in_ptr; + int sum = pulses_in[k2p] + pulses_in[k2p + 1]; + if (sum > max_pulses) { + return 1; + } + pulses_comb[pulses_comb_ptr + k] = sum; + } + return 0; + } + + /// + /// + /// + /// (O) + /// (I) + /// I max value for sum of pulses + /// I number of output values + /// return ok + static int combine_and_check( + int[] pulses_comb, + int[] pulses_in, + int max_pulses, + int len) { + for (int k = 0; k < len; k++) { + int sum = pulses_in[2 * k] + pulses_in[2 * k + 1]; + if (sum > max_pulses) { + return 1; + } + pulses_comb[k] = sum; + } + return 0; + } + + /// + /// Encode quantization indices of excitation + /// + /// I/O compressor data structure + /// I Signal type + /// I quantOffsetType + /// I quantization indices + /// I Frame length + static void silk_encode_pulses( + EntropyCoder psRangeEnc, + int signalType, + int quantOffsetType, + byte[] pulses, + int frame_length) { + int i, k, j, iter, bit, nLS, scale_down, RateLevelIndex = 0; + int abs_q, minSumBits_Q5, sumBits_Q5; + int[] abs_pulses; + int[] sum_pulses; + int[] nRshifts; + int[] pulses_comb = new int[8]; + int abs_pulses_ptr; + int pulses_ptr; + short[] nBits_ptr; + + Arrays.MemSet(pulses_comb, 0, 8); + + /** + * ************************* + */ + /* Prepare for shell coding */ + /** + * ************************* + */ + /* Calculate number of shell blocks */ + Inlines.OpusAssert(1 << SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH == SilkConstants.SHELL_CODEC_FRAME_LENGTH); + iter = Inlines.silk_RSHIFT(frame_length, SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH); + if (iter * SilkConstants.SHELL_CODEC_FRAME_LENGTH < frame_length) { + Inlines.OpusAssert(frame_length == 12 * 10); + /* Make sure only happens for 10 ms @ 12 kHz */ + iter++; + Arrays.MemSetWithOffset(pulses, (byte) 0, frame_length, SilkConstants.SHELL_CODEC_FRAME_LENGTH); + } + + /* Take the absolute value of the pulses */ + abs_pulses = new int[iter * SilkConstants.SHELL_CODEC_FRAME_LENGTH]; + Inlines.OpusAssert((SilkConstants.SHELL_CODEC_FRAME_LENGTH & 3) == 0); + + // unrolled loop + for (i = 0; i < iter * SilkConstants.SHELL_CODEC_FRAME_LENGTH; i += 4) { + abs_pulses[i + 0] = (int) Inlines.silk_abs(pulses[i + 0]); + abs_pulses[i + 1] = (int) Inlines.silk_abs(pulses[i + 1]); + abs_pulses[i + 2] = (int) Inlines.silk_abs(pulses[i + 2]); + abs_pulses[i + 3] = (int) Inlines.silk_abs(pulses[i + 3]); + } + + /* Calc sum pulses per shell code frame */ + sum_pulses = new int[iter]; + nRshifts = new int[iter]; + abs_pulses_ptr = 0; + for (i = 0; i < iter; i++) { + nRshifts[i] = 0; + + while (true) { + /* 1+1 . 2 */ + scale_down = combine_and_check(pulses_comb, 0, abs_pulses, abs_pulses_ptr, SilkTables.silk_max_pulses_table[0], 8); + /* 2+2 . 4 */ + scale_down += combine_and_check(pulses_comb, pulses_comb, SilkTables.silk_max_pulses_table[1], 4); + /* 4+4 . 8 */ + scale_down += combine_and_check(pulses_comb, pulses_comb, SilkTables.silk_max_pulses_table[2], 2); + /* 8+8 . 16 */ + scale_down += combine_and_check(sum_pulses, i, pulses_comb, 0, SilkTables.silk_max_pulses_table[3], 1); + + if (scale_down != 0) { + /* We need to downscale the quantization signal */ + nRshifts[i]++; + for (k = abs_pulses_ptr; k < abs_pulses_ptr + SilkConstants.SHELL_CODEC_FRAME_LENGTH; k++) { + abs_pulses[k] = Inlines.silk_RSHIFT(abs_pulses[k], 1); + } + } else { + /* Jump out of while(1) loop and go to next shell coding frame */ + break; + } + } + + abs_pulses_ptr += SilkConstants.SHELL_CODEC_FRAME_LENGTH; + } + + /** + * *********** + */ + /* Rate level */ + /** + * *********** + */ + /* find rate level that leads to fewest bits for coding of pulses per block info */ + minSumBits_Q5 = Integer.MAX_VALUE; + for (k = 0; k < SilkConstants.N_RATE_LEVELS - 1; k++) { + nBits_ptr = SilkTables.silk_pulses_per_block_BITS_Q5[k]; + sumBits_Q5 = SilkTables.silk_rate_levels_BITS_Q5[signalType >> 1][k]; + for (i = 0; i < iter; i++) { + if (nRshifts[i] > 0) { + sumBits_Q5 += nBits_ptr[SilkConstants.SILK_MAX_PULSES + 1]; + } else { + sumBits_Q5 += nBits_ptr[sum_pulses[i]]; + } + } + if (sumBits_Q5 < minSumBits_Q5) { + minSumBits_Q5 = sumBits_Q5; + RateLevelIndex = k; + } + } + + psRangeEnc.enc_icdf(RateLevelIndex, SilkTables.silk_rate_levels_iCDF[signalType >> 1], 8); + + /** + * ************************************************ + */ + /* Sum-Weighted-Pulses Encoding */ + /** + * ************************************************ + */ + for (i = 0; i < iter; i++) { + if (nRshifts[i] == 0) { + psRangeEnc.enc_icdf(sum_pulses[i], SilkTables.silk_pulses_per_block_iCDF[RateLevelIndex], 8); + } else { + psRangeEnc.enc_icdf(SilkConstants.SILK_MAX_PULSES + 1, SilkTables.silk_pulses_per_block_iCDF[RateLevelIndex], 8); + for (k = 0; k < nRshifts[i] - 1; k++) { + psRangeEnc.enc_icdf(SilkConstants.SILK_MAX_PULSES + 1, SilkTables.silk_pulses_per_block_iCDF[SilkConstants.N_RATE_LEVELS - 1], 8); + } + + psRangeEnc.enc_icdf(sum_pulses[i], SilkTables.silk_pulses_per_block_iCDF[SilkConstants.N_RATE_LEVELS - 1], 8); + } + } + + /** + * *************** + */ + /* Shell Encoding */ + /** + * *************** + */ + for (i = 0; i < iter; i++) { + if (sum_pulses[i] > 0) { + ShellCoder.silk_shell_encoder(psRangeEnc, abs_pulses, i * SilkConstants.SHELL_CODEC_FRAME_LENGTH); + } + } + + /** + * ************* + */ + /* LSB Encoding */ + /** + * ************* + */ + for (i = 0; i < iter; i++) { + if (nRshifts[i] > 0) { + pulses_ptr = i * SilkConstants.SHELL_CODEC_FRAME_LENGTH; + nLS = nRshifts[i] - 1; + for (k = 0; k < SilkConstants.SHELL_CODEC_FRAME_LENGTH; k++) { + abs_q = (byte) Inlines.silk_abs(pulses[pulses_ptr + k]); + for (j = nLS; j > 0; j--) { + bit = Inlines.silk_RSHIFT(abs_q, j) & 1; + psRangeEnc.enc_icdf(bit, SilkTables.silk_lsb_iCDF, 8); + } + bit = abs_q & 1; + psRangeEnc.enc_icdf(bit, SilkTables.silk_lsb_iCDF, 8); + } + } + } + + /** + * ************* + */ + /* Encode signs */ + /** + * ************* + */ + CodeSigns.silk_encode_signs(psRangeEnc, pulses, frame_length, signalType, quantOffsetType, sum_pulses); + } +} diff --git a/app/src/main/java/org/concentus/EntropyCoder.java b/app/src/main/java/org/concentus/EntropyCoder.java new file mode 100644 index 0000000000..49fa3ac747 --- /dev/null +++ b/app/src/main/java/org/concentus/EntropyCoder.java @@ -0,0 +1,720 @@ +/* Copyright (c) 2001-2011 Timothy B. Terriberry + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/*A range decoder. +This is an entropy decoder based upon \cite{Mar79}, which is itself a +rediscovery of the FIFO arithmetic code introduced by \cite{Pas76}. +It is very similar to arithmetic encoding, except that encoding is done with +digits in any base, instead of with bits, and so it is faster when using +larger bases (i.e.: a byte). +The author claims an average waste of $\frac{1}{2}\log_b(2b)$ bits, where $b$ +is the base, longer than the theoretical optimum, but to my knowledge there +is no published justification for this claim. +This only seems true when using near-infinite precision arithmetic so that +the process is carried out with no rounding errors. + +An excellent description of implementation details is available at +http://www.arturocampos.com/ac_range.html +A recent work \cite{MNW98} which proposes several changes to arithmetic +encoding for efficiency actually re-discovers many of the principles +behind range encoding, and presents a good theoretical analysis of them. + +End of stream is handled by writing out the smallest number of bits that +ensures that the stream will be correctly decoded regardless of the value of +any subsequent bits. +ec_tell() can be used to determine how many bits were needed to decode +all the symbols thus far; other data can be packed in the remaining bits of +the input buffer. +@PHDTHESIS{Pas76, +author="Richard Clark Pasco", +title="Source coding algorithms for fast data compression", +school="Dept. of Electrical Engineering, Stanford University", +address="Stanford, CA", +month=May, +year=1976 +} +@INPROCEEDINGS{Mar79, +author="Martin, G.N.N.", +title="Range encoding: an algorithm for removing redundancy from a digitised +message", +booktitle="Video & Data Recording Conference", +year=1979, +address="Southampton", +month=Jul +} +@ARTICLE{MNW98, +author="Alistair Moffat and Radford Neal and Ian H. Witten", +title="Arithmetic Coding Revisited", +journal="{ACM} Transactions on Information Systems", +year=1998, +volume=16, +number=3, +pages="256--294", +month=Jul, +URL="http://www.stanford.edu/class/ee398a/handouts/papers/Moffat98ArithmCoding.pdf" +}*/ +class EntropyCoder { + + private final int EC_WINDOW_SIZE = 32; + + ///*The number of bits to use for the range-coded part of uint integers.*/ + private final int EC_UINT_BITS = 8; + + ///*The resolution of fractional-precision bit usage measurements, i.e., + // 3 => 1/8th bits.*/ + static final int BITRES = 3; + + /*The number of bits to output at a time.*/ + private final int EC_SYM_BITS = 8; + + /*The total number of bits in each of the state registers.*/ + private final int EC_CODE_BITS = 32; + + /*The maximum symbol value.*/ + private final long EC_SYM_MAX = 0x000000FF; + + /*Bits to shift by to move a symbol into the high-order position.*/ + private final int EC_CODE_SHIFT = 0x00000017; + + /*Carry bit of the high-order range symbol.*/ + private final long EC_CODE_TOP = 0x80000000L; + + /*Low-order bit of the high-order range symbol.*/ + private final long EC_CODE_BOT = 0x00800000; + + /*The number of bits available for the last, partial symbol in the code field.*/ + private final int EC_CODE_EXTRA = 0x00000007; + + //////////////// Coder State //////////////////// + + /*POINTER to Buffered input/output.*/ + private byte[] buf; + private int buf_ptr; + + /*The size of the buffer.*/ + int storage; + + /*The offset at which the last byte containing raw bits was read/written.*/ + int end_offs; + + /*Bits that will be read from/written at the end.*/ + long end_window; + + /*Number of valid bits in end_window.*/ + int nend_bits; + + /*The total number of whole bits read/written. + This does not include partial bits currently in the range coder.*/ + int nbits_total; + + /*The offset at which the next range coder byte will be read/written.*/ + int offs; + + /*The number of values in the current range.*/ + long rng; + + /*In the decoder: the difference between the top of the current range and + the input value, minus one. + In the encoder: the low end of the current range.*/ + long val; + + /*In the decoder: the saved normalization factor from ec_decode(). + In the encoder: the number of oustanding carry propagating symbols.*/ + long ext; + + /*A buffered input/output symbol, awaiting carry propagation.*/ + int rem; + + /*Nonzero if an error occurred.*/ + int error; + + EntropyCoder() { + Reset(); + } + + void Reset() { + buf = null; + buf_ptr = 0; + storage = 0; + end_offs = 0; + end_window = 0; + nend_bits = 0; + offs = 0; + rng = 0; + val = 0; + ext = 0; + rem = 0; + error = 0; + } + + void Assign(EntropyCoder other) { + this.buf = other.buf; + this.buf_ptr = other.buf_ptr; + this.storage = other.storage; + this.end_offs = other.end_offs; + this.end_window = other.end_window; + this.nend_bits = other.nend_bits; + this.nbits_total = other.nbits_total; + this.offs = other.offs; + this.rng = other.rng; + this.val = other.val; + this.ext = other.ext; + this.rem = other.rem; + this.error = other.error; + } + + byte[] get_buffer() { + byte[] convertedBuf = new byte[this.storage]; + System.arraycopy(this.buf, this.buf_ptr, convertedBuf, 0, this.storage); + return convertedBuf; + } + + void write_buffer(byte[] data, int data_ptr, int target_offset, int size) { + System.arraycopy(data, data_ptr, this.buf, this.buf_ptr + target_offset, size); + } + + int read_byte() { + return this.offs < this.storage ? Inlines.SignedByteToUnsignedInt(this.buf[buf_ptr + this.offs++]) : 0; + } + + int read_byte_from_end() { + return this.end_offs < this.storage + ? Inlines.SignedByteToUnsignedInt(this.buf[buf_ptr + (this.storage - ++(this.end_offs))]) : 0; + } + + int write_byte(long _value) { + if (this.offs + this.end_offs >= this.storage) { + return -1; + } + this.buf[buf_ptr + this.offs++] = (byte) (_value & 0xFF); + return 0; + } + + int write_byte_at_end(long _value) { + if (this.offs + this.end_offs >= this.storage) { + return -1; + } + + this.buf[buf_ptr + (this.storage - ++(this.end_offs))] = (byte) (_value & 0xFF); + return 0; + } + + /// + /// Normalizes the contents of val and rng so that rng lies entirely in the high-order symbol. + /// + /// + void dec_normalize() { + /*If the range is too small, rescale it and input some bits.*/ + while (this.rng <= EC_CODE_BOT) { + int sym; + this.nbits_total += EC_SYM_BITS; + this.rng = Inlines.CapToUInt32(this.rng << EC_SYM_BITS); + + /*Use up the remaining bits from our last symbol.*/ + sym = this.rem; + + /*Read the next value from the input.*/ + this.rem = read_byte(); + + /*Take the rest of the bits we need from this new symbol.*/ + sym = (sym << EC_SYM_BITS | this.rem) >> (EC_SYM_BITS - EC_CODE_EXTRA); + + /*And subtract them from val, capped to be less than EC_CODE_TOP.*/ + this.val = Inlines.CapToUInt32((((long) this.val << EC_SYM_BITS) + (EC_SYM_MAX & ~sym)) + & (EC_CODE_TOP - 1)); + } + } + + void dec_init(byte[] _buf, int _buf_ptr, int _storage) { + this.buf = _buf; + this.buf_ptr = _buf_ptr; + this.storage = _storage; + this.end_offs = 0; + this.end_window = 0; + this.nend_bits = 0; + /*This is the offset from which ec_tell() will subtract partial bits. + The final value after the ec_dec_normalize() call will be the same as in + the encoder, but we have to compensate for the bits that are added there.*/ + this.nbits_total = EC_CODE_BITS + 1 + - ((EC_CODE_BITS - EC_CODE_EXTRA) / EC_SYM_BITS) * EC_SYM_BITS; + this.offs = 0; + this.rng = 1 << EC_CODE_EXTRA; + this.rem = read_byte(); + this.val = Inlines.CapToUInt32(this.rng - 1 - (this.rem >> (EC_SYM_BITS - EC_CODE_EXTRA))); + this.error = 0; + /*Normalize the interval.*/ + dec_normalize(); + } + + long decode(long _ft) { + _ft = Inlines.CapToUInt32(_ft); + this.ext = Inlines.CapToUInt32(this.rng / _ft); + long s = Inlines.CapToUInt32(this.val / this.ext); + return Inlines.CapToUInt32(_ft - Inlines.EC_MINI(Inlines.CapToUInt32(s + 1), _ft)); + } + + long decode_bin(int _bits) { + this.ext = this.rng >> _bits; + long s = Inlines.CapToUInt32(this.val / this.ext); + return Inlines.CapToUInt32(Inlines.CapToUInt32(1L << _bits) - Inlines.EC_MINI(Inlines.CapToUInt32(s + 1), 1L << _bits)); + } + + void dec_update(long _fl, long _fh, long _ft) { + _fl = Inlines.CapToUInt32(_fl); + _fh = Inlines.CapToUInt32(_fh); + _ft = Inlines.CapToUInt32(_ft); + long s = Inlines.CapToUInt32(this.ext * (_ft - _fh)); + this.val = this.val - s; + this.rng = _fl > 0 ? Inlines.CapToUInt32(this.ext * (_fh - _fl)) : this.rng - s; + dec_normalize(); + } + + /// + /// The probability of having a "one" is 1/(1<<_logp). + /// + /// + /// + /// + int dec_bit_logp(long _logp) { + long r; + long d; + long s; + int ret; + r = this.rng; + d = this.val; + s = r >> (int) _logp; + ret = d < s ? 1 : 0; + if (ret == 0) { + this.val = Inlines.CapToUInt32(d - s); + } + this.rng = ret != 0 ? s : r - s; + dec_normalize(); + return ret; + } + + int dec_icdf(short[] _icdf, int _ftb) { + long t; + long s = this.rng; + long d = this.val; + long r = s >> _ftb; + int ret = -1; + do { + t = s; + s = Inlines.CapToUInt32(r * _icdf[++ret]); + } while (d < s); + this.val = Inlines.CapToUInt32(d - s); + this.rng = Inlines.CapToUInt32(t - s); + dec_normalize(); + return ret; + } + + int dec_icdf(short[] _icdf, int _icdf_offset, int _ftb) { + long t; + long s = this.rng; + long d = this.val; + long r = s >> _ftb; + int ret = _icdf_offset - 1; + do { + t = s; + s = Inlines.CapToUInt32(r * _icdf[++ret]); + } while (d < s); + this.val = Inlines.CapToUInt32(d - s); + this.rng = Inlines.CapToUInt32(t - s); + dec_normalize(); + return ret - _icdf_offset; + } + + long dec_uint(long _ft) { + _ft = Inlines.CapToUInt32(_ft); + long ft; + long s; + int ftb; + /*In order to optimize EC_ILOG(), it is undefined for the value 0.*/ + Inlines.OpusAssert(_ft > 1); + _ft--; + ftb = Inlines.EC_ILOG(_ft); + if (ftb > EC_UINT_BITS) { + long t; + ftb -= EC_UINT_BITS; + ft = Inlines.CapToUInt32((_ft >> ftb) + 1); + s = Inlines.CapToUInt32(decode(ft)); + dec_update(s, (s + 1), ft); + t = Inlines.CapToUInt32((s << ftb | dec_bits(ftb))); + if (t <= _ft) { + return t; + } + this.error = 1; + return _ft; + } else { + _ft++; + s = Inlines.CapToUInt32(decode(_ft)); + dec_update(s, s + 1, _ft); + return s; + } + } + + int dec_bits(int _bits) { + long window; + int available; + int ret; + window = this.end_window; + available = this.nend_bits; + if (available < _bits) { + do { + window = Inlines.CapToUInt32(window | (read_byte_from_end() << available)); + available += EC_SYM_BITS; + } while (available <= EC_WINDOW_SIZE - EC_SYM_BITS); + } + ret = (int) (0xFFFFFFFF & (window & ((1 << _bits) - 1))); + window = window >> _bits; + available = available - _bits; + this.end_window = Inlines.CapToUInt32(window); + this.nend_bits = available; + this.nbits_total = this.nbits_total + _bits; + return ret; + } + + /// + /// Outputs a symbol, with a carry bit. + /// If there is a potential to propagate a carry over several symbols, they are + /// buffered until it can be determined whether or not an actual carry will + /// occur. + /// If the counter for the buffered symbols overflows, then the stream becomes + /// undecodable. + /// This gives a theoretical limit of a few billion symbols in a single packet on + /// 32-bit systems. + /// The alternative is to truncate the range in order to force a carry, but + /// requires similar carry tracking in the decoder, needlessly slowing it down. + /// + /// + /// + void enc_carry_out(int _c) { + if (_c != EC_SYM_MAX) { + /*No further carry propagation possible, flush buffer.*/ + int carry; + carry = _c >> EC_SYM_BITS; + + /*Don't output a byte on the first write. + This compare should be taken care of by branch-prediction thereafter.*/ + if (this.rem >= 0) { + this.error |= write_byte(Inlines.CapToUInt32(this.rem + carry)); + } + + if (this.ext > 0) { + long sym; + sym = (EC_SYM_MAX + carry) & EC_SYM_MAX; + do { + this.error |= write_byte(sym); + } while (--(this.ext) > 0); + } + + this.rem = (int) (_c & EC_SYM_MAX); + } else { + this.ext++; + } + } + + void enc_normalize() { + /*If the range is too small, output some bits and rescale it.*/ + while (this.rng <= EC_CODE_BOT) { + enc_carry_out((int) (this.val >> EC_CODE_SHIFT)); + /*Move the next-to-high-order symbol into the high-order position.*/ + this.val = Inlines.CapToUInt32((this.val << EC_SYM_BITS) & (EC_CODE_TOP - 1)); + this.rng = Inlines.CapToUInt32(this.rng << EC_SYM_BITS); + this.nbits_total += EC_SYM_BITS; + } + } + + void enc_init(byte[] _buf, int buf_ptr, int _size) { + this.buf = _buf; + this.buf_ptr = buf_ptr; + this.end_offs = 0; + this.end_window = 0; + this.nend_bits = 0; + /*This is the offset from which ec_tell() will subtract partial bits.*/ + this.nbits_total = EC_CODE_BITS + 1; + this.offs = 0; + this.rng = Inlines.CapToUInt32(EC_CODE_TOP); + this.rem = -1; + this.val = 0; + this.ext = 0; + this.storage = _size; + this.error = 0; + } + + void encode(long _fl, long _fh, long _ft) { + _fl = Inlines.CapToUInt32(_fl); + _fh = Inlines.CapToUInt32(_fh); + _ft = Inlines.CapToUInt32(_ft); + long r = Inlines.CapToUInt32(this.rng / _ft); + if (_fl > 0) { + this.val += Inlines.CapToUInt32(this.rng - (r * (_ft - _fl))); + this.rng = Inlines.CapToUInt32(r * (_fh - _fl)); + } else { + this.rng = Inlines.CapToUInt32(this.rng - (r * (_ft - _fh))); + } + + enc_normalize(); + } + + void encode_bin(long _fl, long _fh, int _bits) { + _fl = Inlines.CapToUInt32(_fl); + _fh = Inlines.CapToUInt32(_fh); + long r = Inlines.CapToUInt32(this.rng >> (int) _bits); + if (_fl > 0) { + this.val = Inlines.CapToUInt32(this.val + Inlines.CapToUInt32(this.rng - (r * ((1 << (int) _bits) - _fl)))); + this.rng = Inlines.CapToUInt32(r * (_fh - _fl)); + } else { + this.rng = Inlines.CapToUInt32(this.rng - (r * ((1 << (int) _bits) - _fh))); + } + + enc_normalize(); + } + + /*The probability of having a "one" is 1/(1<<_logp).*/ + void enc_bit_logp(int _val, int _logp) { + long r = this.rng; + long l = this.val; + long s = r >> _logp; + r -= s; + if (_val != 0) { + this.val = Inlines.CapToUInt32(l + r); + } + + this.rng = _val != 0 ? s : r; + enc_normalize(); + } + + void enc_icdf(int _s, short[] _icdf, int _ftb) { + long r = Inlines.CapToUInt32(this.rng >> _ftb); + if (_s > 0) { + this.val = this.val + Inlines.CapToUInt32(this.rng - Inlines.CapToUInt32(r * _icdf[_s - 1])); + this.rng = (r * Inlines.CapToUInt32(_icdf[_s - 1] - _icdf[_s])); + } else { + this.rng = Inlines.CapToUInt32(this.rng - (r * _icdf[_s])); + } + enc_normalize(); + } + + void enc_icdf(int _s, short[] _icdf, int icdf_ptr, int _ftb) { + long r = Inlines.CapToUInt32(this.rng >> _ftb); + if (_s > 0) { + this.val = this.val + Inlines.CapToUInt32(this.rng - Inlines.CapToUInt32(r * _icdf[icdf_ptr + _s - 1])); + this.rng = Inlines.CapToUInt32(r * Inlines.CapToUInt32(_icdf[icdf_ptr + _s - 1] - _icdf[icdf_ptr + _s])); + } else { + this.rng = Inlines.CapToUInt32(this.rng - (r * _icdf[icdf_ptr + _s])); + } + enc_normalize(); + } + + void enc_uint(long _fl, long _ft) { + _fl = Inlines.CapToUInt32(_fl); + _ft = Inlines.CapToUInt32(_ft); + + long ft; + long fl; + int ftb; + /*In order to optimize EC_ILOG(), it is undefined for the value 0.*/ + Inlines.OpusAssert(_ft > 1); + _ft--; + ftb = Inlines.EC_ILOG(_ft); + if (ftb > EC_UINT_BITS) { + ftb -= EC_UINT_BITS; + ft = Inlines.CapToUInt32((_ft >> ftb) + 1); + fl = Inlines.CapToUInt32(_fl >> ftb); + encode(fl, fl + 1, ft); + enc_bits(_fl & Inlines.CapToUInt32((1 << ftb) - 1), ftb); + } else { + encode(_fl, _fl + 1, _ft + 1); + } + } + + void enc_bits(long _fl, int _bits) { + _fl = Inlines.CapToUInt32(_fl); + long window; + int used; + window = this.end_window; + used = this.nend_bits; + Inlines.OpusAssert(_bits > 0); + + if (used + _bits > EC_WINDOW_SIZE) { + do { + this.error = this.error | write_byte_at_end(window & EC_SYM_MAX); + window >>= EC_SYM_BITS; + used -= EC_SYM_BITS; + } while (used >= EC_SYM_BITS); + } + + window = window | Inlines.CapToUInt32(_fl << used); + used += _bits; + this.end_window = window; + this.nend_bits = used; + this.nbits_total += _bits; + } + + void enc_patch_initial_bits(long _val, int _nbits) { + int shift; + long mask; + Inlines.OpusAssert(_nbits <= EC_SYM_BITS); + shift = EC_SYM_BITS - _nbits; + mask = ((1 << _nbits) - 1) << shift; + + if (this.offs > 0) { + /*The first byte has been finalized.*/ + this.buf[buf_ptr] = (byte) ((this.buf[buf_ptr] & ~mask) | Inlines.CapToUInt32(_val << shift)); + } else if (this.rem >= 0) { + /*The first byte is still awaiting carry propagation.*/ + this.rem = (int) Inlines.CapToUInt32((Inlines.CapToUInt32((this.rem & ~mask) | _val)) << shift); + } else if (this.rng <= (EC_CODE_TOP >> (int) _nbits)) { + /*The renormalization loop has never been run.*/ + this.val = Inlines.CapToUInt32((this.val & ~(mask << EC_CODE_SHIFT)) + | Inlines.CapToUInt32(Inlines.CapToUInt32(_val) << (EC_CODE_SHIFT + shift))); + } else { + /*The encoder hasn't even encoded _nbits of data yet.*/ + this.error = -1; + } + } + + void enc_shrink(int _size) { + Inlines.OpusAssert(this.offs + this.end_offs <= _size); + Arrays.MemMove(this.buf, buf_ptr + (int) _size - (int) this.end_offs, buf_ptr + (int) this.storage - (int) this.end_offs, (int) this.end_offs); + this.storage = _size; + } + + int range_bytes() { + return this.offs; + } + + int get_error() { + return this.error; + } + + /// + /// Returns the number of bits "used" by the encoded or decoded symbols so far. + /// This same number can be computed in either the encoder or the decoder, and is + /// suitable for making coding decisions. + /// This will always be slightly larger than the exact value (e.g., all + /// rounding error is in the positive direction). + /// + /// + /// The number of bits. + int tell() { + int returnVal = this.nbits_total - Inlines.EC_ILOG(this.rng); + return returnVal; + } + + private static final int[] correction = {35733, 38967, 42495, 46340, 50535, 55109, 60097, 65535}; + + /// + /// This is a faster version of ec_tell_frac() that takes advantage + /// of the low(1/8 bit) resolution to use just a linear function + /// followed by a lookup to determine the exact transition thresholds. + /// + /// + /// + int tell_frac() { + int nbits; + int r; + int l; + long b; + nbits = this.nbits_total << BITRES; + l = Inlines.EC_ILOG(this.rng); + r = (int) (this.rng >> (l - 16)); + b = Inlines.CapToUInt32((r >> 12) - 8); + b = Inlines.CapToUInt32(b + (r > correction[(int) b] ? 1 : 0)); + l = (int) ((l << 3) + b); + return nbits - l; + } + + void enc_done() { + long window; + int used; + long msk; + long end; + int l; + /*We output the minimum number of bits that ensures that the symbols encoded + thus far will be decoded correctly regardless of the bits that follow.*/ + l = EC_CODE_BITS - Inlines.EC_ILOG(this.rng); + msk = Inlines.CapToUInt32((EC_CODE_TOP - 1) >>> l); + end = Inlines.CapToUInt32((Inlines.CapToUInt32(this.val + msk)) & ~msk); + + if ((end | msk) >= this.val + this.rng) { + l++; + msk >>= 1; + end = Inlines.CapToUInt32((Inlines.CapToUInt32(this.val + msk)) & ~msk); + } + + while (l > 0) { + enc_carry_out((int) (end >> EC_CODE_SHIFT)); + end = Inlines.CapToUInt32((end << EC_SYM_BITS) & (EC_CODE_TOP - 1)); + l -= EC_SYM_BITS; + } + + /*If we have a buffered byte flush it into the output buffer.*/ + if (this.rem >= 0 || this.ext > 0) { + enc_carry_out(0); + } + + /*If we have buffered extra bits, flush them as well.*/ + window = this.end_window; + used = this.nend_bits; + + while (used >= EC_SYM_BITS) { + this.error |= write_byte_at_end(window & EC_SYM_MAX); + window = window >> EC_SYM_BITS; + used -= EC_SYM_BITS; + } + + /*Clear any excess space and add any remaining extra bits to the last byte.*/ + if (this.error == 0) { + Arrays.MemSetWithOffset(this.buf, (byte) 0, buf_ptr + (int) this.offs, (int) this.storage - (int) this.offs - (int) this.end_offs); + if (used > 0) { + /*If there's no range coder data at all, give up.*/ + if (this.end_offs >= this.storage) { + this.error = -1; + } else { + l = -l; + /*If we've busted, don't add too many extra bits to the last byte; it + would corrupt the range coder data, and that's more important.*/ + if (this.offs + this.end_offs >= this.storage && l < used) { + window = Inlines.CapToUInt32(window & ((1 << l) - 1)); + this.error = -1; + } + + int z = buf_ptr + this.storage - this.end_offs - 1; + this.buf[z] = (byte) (this.buf[z] | (byte) (window & 0xFF)); + } + } + } + } +} diff --git a/app/src/main/java/org/concentus/FFTState.java b/app/src/main/java/org/concentus/FFTState.java new file mode 100644 index 0000000000..2b9a18b825 --- /dev/null +++ b/app/src/main/java/org/concentus/FFTState.java @@ -0,0 +1,46 @@ +/* Copyright (c) 2003-2004, Mark Borgerding + Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Modified from KISS-FFT by Jean-Marc Valin + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class FFTState { + + int nfft = 0; + short scale = 0; + int scale_shift = 0; + int shift = 0; + short[] factors = new short[2 * KissFFT.MAXFACTORS]; + short[] bitrev = null; + short[] twiddles = null; +} diff --git a/app/src/main/java/org/concentus/Filters.java b/app/src/main/java/org/concentus/Filters.java new file mode 100644 index 0000000000..1f0dad42bb --- /dev/null +++ b/app/src/main/java/org/concentus/Filters.java @@ -0,0 +1,584 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class Filters { + + static void silk_warped_LPC_analysis_filter( + int[] state, /* I/O State [order + 1] */ + int[] res_Q2, /* O Residual signal [length] */ + short[] coef_Q13, /* I Coefficients [order] */ + int coef_Q13_ptr, + short[] input, /* I Input signal [length] */ + int input_ptr, + short lambda_Q16, /* I Warping factor */ + int length, /* I Length of input signal */ + int order /* I Filter order (even) */ + ) { + int n, i; + int acc_Q11, tmp1, tmp2; + + /* Order must be even */ + Inlines.OpusAssert((order & 1) == 0); + + for (n = 0; n < length; n++) { + /* Output of lowpass section */ + tmp2 = Inlines.silk_SMLAWB(state[0], state[1], lambda_Q16); + state[0] = Inlines.silk_LSHIFT(input[input_ptr + n], 14); + /* Output of allpass section */ + tmp1 = Inlines.silk_SMLAWB(state[1], state[2] - tmp2, lambda_Q16); + state[1] = tmp2; + acc_Q11 = Inlines.silk_RSHIFT(order, 1); + acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp2, coef_Q13[coef_Q13_ptr]); + /* Loop over allpass sections */ + for (i = 2; i < order; i += 2) { + /* Output of allpass section */ + tmp2 = Inlines.silk_SMLAWB(state[i], state[i + 1] - tmp1, lambda_Q16); + state[i] = tmp1; + acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp1, coef_Q13[coef_Q13_ptr + i - 1]); + /* Output of allpass section */ + tmp1 = Inlines.silk_SMLAWB(state[i + 1], state[i + 2] - tmp2, lambda_Q16); + state[i + 1] = tmp2; + acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp2, coef_Q13[coef_Q13_ptr + i]); + } + state[order] = tmp1; + acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp1, coef_Q13[coef_Q13_ptr + order - 1]); + res_Q2[n] = Inlines.silk_LSHIFT((int) input[input_ptr + n], 2) - Inlines.silk_RSHIFT_ROUND(acc_Q11, 9); + } + } + + static void silk_prefilter( + SilkChannelEncoder psEnc, /* I/O Encoder state */ + SilkEncoderControl psEncCtrl, /* I Encoder control */ + int[] xw_Q3, /* O Weighted signal */ + short[] x, /* I Speech signal */ + int x_ptr + ) { + SilkPrefilterState P = psEnc.sPrefilt; + int j, k, lag; + int tmp_32; + int AR1_shp_Q13; + int px; + int pxw_Q3; + int HarmShapeGain_Q12, Tilt_Q14; + int HarmShapeFIRPacked_Q12, LF_shp_Q14; + int[] x_filt_Q12; + int[] st_res_Q2; + short[] B_Q10 = new short[2]; + + /* Set up pointers */ + px = x_ptr; + pxw_Q3 = 0; + lag = P.lagPrev; + x_filt_Q12 = new int[psEnc.subfr_length]; + st_res_Q2 = new int[psEnc.subfr_length]; + for (k = 0; k < psEnc.nb_subfr; k++) { + /* Update Variables that change per sub frame */ + if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED) { + lag = psEncCtrl.pitchL[k]; + } + + /* Noise shape parameters */ + HarmShapeGain_Q12 = Inlines.silk_SMULWB((int) psEncCtrl.HarmShapeGain_Q14[k], 16384 - psEncCtrl.HarmBoost_Q14[k]); + Inlines.OpusAssert(HarmShapeGain_Q12 >= 0); + HarmShapeFIRPacked_Q12 = Inlines.silk_RSHIFT(HarmShapeGain_Q12, 2); + HarmShapeFIRPacked_Q12 |= Inlines.silk_LSHIFT((int) Inlines.silk_RSHIFT(HarmShapeGain_Q12, 1), 16); + Tilt_Q14 = psEncCtrl.Tilt_Q14[k]; + LF_shp_Q14 = psEncCtrl.LF_shp_Q14[k]; + AR1_shp_Q13 = k * SilkConstants.MAX_SHAPE_LPC_ORDER; + + /* Short term FIR filtering*/ + silk_warped_LPC_analysis_filter(P.sAR_shp, st_res_Q2, psEncCtrl.AR1_Q13, AR1_shp_Q13, x, px, + (short) (psEnc.warping_Q16), psEnc.subfr_length, psEnc.shapingLPCOrder); + + /* Reduce (mainly) low frequencies during harmonic emphasis */ + B_Q10[0] = (short) (Inlines.silk_RSHIFT_ROUND(psEncCtrl.GainsPre_Q14[k], 4)); + tmp_32 = Inlines.silk_SMLABB(((int) ((TuningParameters.INPUT_TILT) * ((long) 1 << (26)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.INPUT_TILT, 26)*/, psEncCtrl.HarmBoost_Q14[k], HarmShapeGain_Q12); + /* Q26 */ + tmp_32 = Inlines.silk_SMLABB(tmp_32, psEncCtrl.coding_quality_Q14, ((int) ((TuningParameters.HIGH_RATE_INPUT_TILT) * ((long) 1 << (12)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HIGH_RATE_INPUT_TILT, 12)*/); + /* Q26 */ + tmp_32 = Inlines.silk_SMULWB(tmp_32, -psEncCtrl.GainsPre_Q14[k]); + /* Q24 */ + tmp_32 = Inlines.silk_RSHIFT_ROUND(tmp_32, 14); + /* Q10 */ + B_Q10[1] = (short) (Inlines.silk_SAT16(tmp_32)); + x_filt_Q12[0] = Inlines.silk_MLA(Inlines.silk_MUL(st_res_Q2[0], B_Q10[0]), P.sHarmHP_Q2, B_Q10[1]); + for (j = 1; j < psEnc.subfr_length; j++) { + x_filt_Q12[j] = Inlines.silk_MLA(Inlines.silk_MUL(st_res_Q2[j], B_Q10[0]), st_res_Q2[j - 1], B_Q10[1]); + } + P.sHarmHP_Q2 = st_res_Q2[psEnc.subfr_length - 1]; + + silk_prefilt(P, x_filt_Q12, xw_Q3, pxw_Q3, HarmShapeFIRPacked_Q12, Tilt_Q14, LF_shp_Q14, lag, psEnc.subfr_length); + + px += psEnc.subfr_length; + pxw_Q3 += psEnc.subfr_length; + } + + P.lagPrev = psEncCtrl.pitchL[psEnc.nb_subfr - 1]; + + } + + /* Prefilter for finding Quantizer input signal */ + static void silk_prefilt( + SilkPrefilterState P, /* I/O state */ + int[] st_res_Q12, /* I short term residual signal */ + int[] xw_Q3, /* O prefiltered signal */ + int xw_Q3_ptr, + int HarmShapeFIRPacked_Q12, /* I Harmonic shaping coeficients */ + int Tilt_Q14, /* I Tilt shaping coeficient */ + int LF_shp_Q14, /* I Low-frequancy shaping coeficients */ + int lag, /* I Lag for harmonic shaping */ + int length /* I Length of signals */ + ) { + int i, idx, LTP_shp_buf_idx; + int n_LTP_Q12, n_Tilt_Q10, n_LF_Q10; + int sLF_MA_shp_Q12, sLF_AR_shp_Q12; + short[] LTP_shp_buf; + + /* To speed up use temp variables instead of using the struct */ + LTP_shp_buf = P.sLTP_shp; + LTP_shp_buf_idx = P.sLTP_shp_buf_idx; + sLF_AR_shp_Q12 = P.sLF_AR_shp_Q12; + sLF_MA_shp_Q12 = P.sLF_MA_shp_Q12; + + for (i = 0; i < length; i++) { + if (lag > 0) { + /* unrolled loop */ + Inlines.OpusAssert(SilkConstants.HARM_SHAPE_FIR_TAPS == 3); + idx = lag + LTP_shp_buf_idx; + n_LTP_Q12 = Inlines.silk_SMULBB(LTP_shp_buf[(idx - SilkConstants.HARM_SHAPE_FIR_TAPS / 2 - 1) & SilkConstants.LTP_MASK], HarmShapeFIRPacked_Q12); + n_LTP_Q12 = Inlines.silk_SMLABT(n_LTP_Q12, LTP_shp_buf[(idx - SilkConstants.HARM_SHAPE_FIR_TAPS / 2) & SilkConstants.LTP_MASK], HarmShapeFIRPacked_Q12); + n_LTP_Q12 = Inlines.silk_SMLABB(n_LTP_Q12, LTP_shp_buf[(idx - SilkConstants.HARM_SHAPE_FIR_TAPS / 2 + 1) & SilkConstants.LTP_MASK], HarmShapeFIRPacked_Q12); + } else { + n_LTP_Q12 = 0; + } + + n_Tilt_Q10 = Inlines.silk_SMULWB(sLF_AR_shp_Q12, Tilt_Q14); + n_LF_Q10 = Inlines.silk_SMLAWB(Inlines.silk_SMULWT(sLF_AR_shp_Q12, LF_shp_Q14), sLF_MA_shp_Q12, LF_shp_Q14); + + sLF_AR_shp_Q12 = Inlines.silk_SUB32(st_res_Q12[i], Inlines.silk_LSHIFT(n_Tilt_Q10, 2)); + sLF_MA_shp_Q12 = Inlines.silk_SUB32(sLF_AR_shp_Q12, Inlines.silk_LSHIFT(n_LF_Q10, 2)); + + LTP_shp_buf_idx = (LTP_shp_buf_idx - 1) & SilkConstants.LTP_MASK; + LTP_shp_buf[LTP_shp_buf_idx] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sLF_MA_shp_Q12, 12)); + + xw_Q3[xw_Q3_ptr + i] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SUB32(sLF_MA_shp_Q12, n_LTP_Q12), 9); + } + + /* Copy temp variable back to state */ + P.sLF_AR_shp_Q12 = sLF_AR_shp_Q12; + P.sLF_MA_shp_Q12 = sLF_MA_shp_Q12; + P.sLTP_shp_buf_idx = LTP_shp_buf_idx; + } + + /// + /// Second order ARMA filter, alternative implementation + /// + /// I input signal + /// I MA coefficients [3] + /// I AR coefficients [2] + /// I/O State vector [2] + /// O output signal + /// I signal length (must be even) + /// I Operate on interleaved signal if > 1 + static void silk_biquad_alt( + short[] input, + int input_ptr, + int[] B_Q28, + int[] A_Q28, + int[] S, + short[] output, + int output_ptr, + int len, + int stride) { + /* DIRECT FORM II TRANSPOSED (uses 2 element state vector) */ + int k; + int inval, A0_U_Q28, A0_L_Q28, A1_U_Q28, A1_L_Q28, out32_Q14; + + /* Negate A_Q28 values and split in two parts */ + A0_L_Q28 = (-A_Q28[0]) & 0x00003FFF; + /* lower part */ + A0_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[0], 14); + /* upper part */ + A1_L_Q28 = (-A_Q28[1]) & 0x00003FFF; + /* lower part */ + A1_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[1], 14); + /* upper part */ + + for (k = 0; k < len; k++) { + /* S[ 0 ], S[ 1 ]: Q12 */ + inval = input[input_ptr + k * stride]; + out32_Q14 = Inlines.silk_LSHIFT(Inlines.silk_SMLAWB(S[0], B_Q28[0], inval), 2); + + S[0] = S[1] + Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A0_L_Q28), 14); + S[0] = Inlines.silk_SMLAWB(S[0], out32_Q14, A0_U_Q28); + S[0] = Inlines.silk_SMLAWB(S[0], B_Q28[1], inval); + + S[1] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A1_L_Q28), 14); + S[1] = Inlines.silk_SMLAWB(S[1], out32_Q14, A1_U_Q28); + S[1] = Inlines.silk_SMLAWB(S[1], B_Q28[2], inval); + + /* Scale back to Q0 and saturate */ + output[output_ptr + k * stride] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT(out32_Q14 + (1 << 14) - 1, 14)); + } + } + + static void silk_biquad_alt( + short[] input, + int input_ptr, + int[] B_Q28, + int[] A_Q28, + int[] S, + int S_ptr, + short[] output, + int output_ptr, + int len, + int stride) { + /* DIRECT FORM II TRANSPOSED (uses 2 element state vector) */ + int k; + int inval, A0_U_Q28, A0_L_Q28, A1_U_Q28, A1_L_Q28, out32_Q14; + + /* Negate A_Q28 values and split in two parts */ + A0_L_Q28 = (-A_Q28[0]) & 0x00003FFF; + /* lower part */ + A0_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[0], 14); + /* upper part */ + A1_L_Q28 = (-A_Q28[1]) & 0x00003FFF; + /* lower part */ + A1_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[1], 14); + /* upper part */ + + for (k = 0; k < len; k++) { + int s1 = S_ptr + 1; + /* S[ 0 ], S[ 1 ]: Q12 */ + inval = input[input_ptr + k * stride]; + out32_Q14 = Inlines.silk_LSHIFT(Inlines.silk_SMLAWB(S[S_ptr], B_Q28[0], inval), 2); + + S[S_ptr] = S[s1] + Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A0_L_Q28), 14); + S[S_ptr] = Inlines.silk_SMLAWB(S[S_ptr], out32_Q14, A0_U_Q28); + S[S_ptr] = Inlines.silk_SMLAWB(S[S_ptr], B_Q28[1], inval); + + S[s1] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A1_L_Q28), 14); + S[s1] = Inlines.silk_SMLAWB(S[s1], out32_Q14, A1_U_Q28); + S[s1] = Inlines.silk_SMLAWB(S[s1], B_Q28[2], inval); + + /* Scale back to Q0 and saturate */ + output[output_ptr + k * stride] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT(out32_Q14 + (1 << 14) - 1, 14)); + } + } + + /* Coefficients for 2-band filter bank based on first-order allpass filters */ + private final static short A_fb1_20 = 5394 << 1; + private final static short A_fb1_21 = -24290; + + /* (opus_int16)(20623 << 1) */ + + /// + /// Split signal into two decimated bands using first-order allpass filters + /// + /// I Input signal [N] + /// I/O State vector [2] + /// O Low band [N/2] + /// O High band [N/2] + /// I Number of input samples + static void silk_ana_filt_bank_1( + short[] input, + int input_ptr, + int[] S, + short[] outL, + short[] outH, + int outH_ptr, + int N) { + int k, N2 = Inlines.silk_RSHIFT(N, 1); + int in32, X, Y, out_1, out_2; + + /* Internal variables and state are in Q10 format */ + for (k = 0; k < N2; k++) { + /* Convert to Q10 */ + in32 = Inlines.silk_LSHIFT((int) input[input_ptr + 2 * k], 10); + + /* All-pass section for even input sample */ + Y = Inlines.silk_SUB32(in32, S[0]); + X = Inlines.silk_SMLAWB(Y, Y, A_fb1_21); + out_1 = Inlines.silk_ADD32(S[0], X); + S[0] = Inlines.silk_ADD32(in32, X); + + /* Convert to Q10 */ + in32 = Inlines.silk_LSHIFT((int) input[input_ptr + 2 * k + 1], 10); + + /* All-pass section for odd input sample, and add to output of previous section */ + Y = Inlines.silk_SUB32(in32, S[1]); + X = Inlines.silk_SMULWB(Y, A_fb1_20); + out_2 = Inlines.silk_ADD32(S[1], X); + S[1] = Inlines.silk_ADD32(in32, X); + + /* Add/subtract, convert back to int16 and store to output */ + outL[k] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_ADD32(out_2, out_1), 11)); + outH[outH_ptr + k] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SUB32(out_2, out_1), 11)); + } + } + + /// + /// Chirp (bandwidth expand) LP AR filter + /// + /// I/O AR filter to be expanded (without leading 1) + /// I Length of ar + /// I Chirp factor in Q16 + static void silk_bwexpander_32(int[] ar, int d, int chirp_Q16) { + int i; + int chirp_minus_one_Q16 = chirp_Q16 - 65536; + + for (i = 0; i < d - 1; i++) { + ar[i] = Inlines.silk_SMULWW(chirp_Q16, ar[i]); + chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, chirp_minus_one_Q16), 16); + } + + ar[d - 1] = Inlines.silk_SMULWW(chirp_Q16, ar[d - 1]); + } + + /// + /// Elliptic/Cauer filters designed with 0.1 dB passband ripple, + /// 80 dB minimum stopband attenuation, and + /// [0.95 : 0.15 : 0.35] normalized cut off frequencies. + /// Helper function, interpolates the filter taps + /// + /// order [TRANSITION_NB] + /// order [TRANSITION_NA] + /// + /// + static void silk_LP_interpolate_filter_taps( + int[] B_Q28, + int[] A_Q28, + int ind, + int fac_Q16) { + int nb, na; + + if (ind < SilkConstants.TRANSITION_INT_NUM - 1) { + if (fac_Q16 > 0) { + if (fac_Q16 < 32768) { + /* fac_Q16 is in range of a 16-bit int */ + /* Piece-wise linear interpolation of B and A */ + for (nb = 0; nb < SilkConstants.TRANSITION_NB; nb++) { + B_Q28[nb] = Inlines.silk_SMLAWB( + SilkTables.silk_Transition_LP_B_Q28[ind][nb], + SilkTables.silk_Transition_LP_B_Q28[ind + 1][nb] + - SilkTables.silk_Transition_LP_B_Q28[ind][nb], + fac_Q16); + } + + for (na = 0; na < SilkConstants.TRANSITION_NA; na++) { + A_Q28[na] = Inlines.silk_SMLAWB( + SilkTables.silk_Transition_LP_A_Q28[ind][na], + SilkTables.silk_Transition_LP_A_Q28[ind + 1][na] + - SilkTables.silk_Transition_LP_A_Q28[ind][na], + fac_Q16); + } + } else { + /* ( fac_Q16 - ( 1 << 16 ) ) is in range of a 16-bit int */ + Inlines.OpusAssert(fac_Q16 - (1 << 16) == Inlines.silk_SAT16(fac_Q16 - (1 << 16))); + + /* Piece-wise linear interpolation of B and A */ + for (nb = 0; nb < SilkConstants.TRANSITION_NB; nb++) { + B_Q28[nb] = Inlines.silk_SMLAWB( + SilkTables.silk_Transition_LP_B_Q28[ind + 1][nb], + SilkTables.silk_Transition_LP_B_Q28[ind + 1][nb] + - SilkTables.silk_Transition_LP_B_Q28[ind][nb], + fac_Q16 - ((int) 1 << 16)); + } + + for (na = 0; na < SilkConstants.TRANSITION_NA; na++) { + A_Q28[na] = Inlines.silk_SMLAWB( + SilkTables.silk_Transition_LP_A_Q28[ind + 1][na], + SilkTables.silk_Transition_LP_A_Q28[ind + 1][na] + - SilkTables.silk_Transition_LP_A_Q28[ind][na], + fac_Q16 - ((int) 1 << 16)); + } + } + } else { + System.arraycopy(SilkTables.silk_Transition_LP_B_Q28[ind], 0, B_Q28, 0, SilkConstants.TRANSITION_NB); + System.arraycopy(SilkTables.silk_Transition_LP_A_Q28[ind], 0, A_Q28, 0, SilkConstants.TRANSITION_NA); + } + } else { + System.arraycopy(SilkTables.silk_Transition_LP_B_Q28[SilkConstants.TRANSITION_INT_NUM - 1], 0, B_Q28, 0, SilkConstants.TRANSITION_NB); + System.arraycopy(SilkTables.silk_Transition_LP_A_Q28[SilkConstants.TRANSITION_INT_NUM - 1], 0, A_Q28, 0, SilkConstants.TRANSITION_NA); + } + } + + /// + /// LPC analysis filter + /// NB! State is kept internally and the + /// filter always starts with zero state + /// first d output samples are set to zero + /// + /// O Output signal + /// I Input signal + /// I MA prediction coefficients, Q12 [order] + /// I Signal length + /// I Filter order + /// I Run-time architecture + static void silk_LPC_analysis_filter( + short[] output, + int output_ptr, + short[] input, + int input_ptr, + short[] B, + int B_ptr, + int len, + int d) { + int j; + + short[] mem = new short[SilkConstants.SILK_MAX_ORDER_LPC]; + short[] num = new short[SilkConstants.SILK_MAX_ORDER_LPC]; + + Inlines.OpusAssert(d >= 6); + Inlines.OpusAssert((d & 1) == 0); + Inlines.OpusAssert(d <= len); + + Inlines.OpusAssert(d <= SilkConstants.SILK_MAX_ORDER_LPC); + for (j = 0; j < d; j++) { + num[j] = (short) (0 - B[B_ptr + j]); + } + for (j = 0; j < d; j++) { + mem[j] = input[input_ptr + d - j - 1]; + } + Kernels.celt_fir(input, input_ptr + d, num, output, output_ptr + d, len - d, d, mem); + for (j = output_ptr; j < output_ptr + d; j++) { + output[j] = 0; + } + } + + private static final int QA = 24; + private static final int A_LIMIT = ((int) ((0.99975f) * ((long) 1 << (QA)) + 0.5))/*Inlines.SILK_CONST(0.99975f, QA)*/; + + /// + /// Compute inverse of LPC prediction gain, and + /// test if LPC coefficients are stable (all poles within unit circle) + /// + /// Prediction coefficients, order [2][SILK_MAX_ORDER_LPC] + /// Prediction order + /// inverse prediction gain in energy domain, Q30 + static int LPC_inverse_pred_gain_QA( + int[][] A_QA, + int order) { + int k, n, mult2Q; + int invGain_Q30, rc_Q31, rc_mult1_Q30, rc_mult2, tmp_QA; + int[] Aold_QA, Anew_QA; + + Anew_QA = A_QA[order & 1]; + + invGain_Q30 = (int) 1 << 30; + for (k = order - 1; k > 0; k--) { + /* Check for stability */ + if ((Anew_QA[k] > A_LIMIT) || (Anew_QA[k] < -A_LIMIT)) { + return 0; + } + + /* Set RC equal to negated AR coef */ + rc_Q31 = 0 - Inlines.silk_LSHIFT(Anew_QA[k], 31 - QA); + + /* rc_mult1_Q30 range: [ 1 : 2^30 ] */ + rc_mult1_Q30 = ((int) 1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31); + Inlines.OpusAssert(rc_mult1_Q30 > (1 << 15)); + /* reduce A_LIMIT if fails */ + Inlines.OpusAssert(rc_mult1_Q30 <= (1 << 30)); + + /* rc_mult2 range: [ 2^30 : silk_int32_MAX ] */ + mult2Q = 32 - Inlines.silk_CLZ32(Inlines.silk_abs(rc_mult1_Q30)); + rc_mult2 = Inlines.silk_INVERSE32_varQ(rc_mult1_Q30, mult2Q + 30); + + /* Update inverse gain */ + /* invGain_Q30 range: [ 0 : 2^30 ] */ + invGain_Q30 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, rc_mult1_Q30), 2); + Inlines.OpusAssert(invGain_Q30 >= 0); + Inlines.OpusAssert(invGain_Q30 <= (1 << 30)); + + /* Swap pointers */ + Aold_QA = Anew_QA; + Anew_QA = A_QA[k & 1]; + + /* Update AR coefficient */ + for (n = 0; n < k; n++) { + tmp_QA = Aold_QA[n] - Inlines.MUL32_FRAC_Q(Aold_QA[k - n - 1], rc_Q31, 31); + Anew_QA[n] = Inlines.MUL32_FRAC_Q(tmp_QA, rc_mult2, mult2Q); + } + } + + /* Check for stability */ + if ((Anew_QA[0] > A_LIMIT) || (Anew_QA[0] < -A_LIMIT)) { + return 0; + } + + /* Set RC equal to negated AR coef */ + rc_Q31 = 0 - Inlines.silk_LSHIFT(Anew_QA[0], 31 - QA); + + /* Range: [ 1 : 2^30 ] */ + rc_mult1_Q30 = ((int) 1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31); + + /* Update inverse gain */ + /* Range: [ 0 : 2^30 ] */ + invGain_Q30 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, rc_mult1_Q30), 2); + Inlines.OpusAssert(invGain_Q30 >= 0); + Inlines.OpusAssert(invGain_Q30 <= 1 << 30); + + return invGain_Q30; + } + + /// + /// For input in Q12 domain + /// + /// Prediction coefficients, Q12 [order] + /// I Prediction order + /// inverse prediction gain in energy domain, Q30 + static int silk_LPC_inverse_pred_gain(short[] A_Q12, int order) { + int k; + int[][] Atmp_QA = new int[2][]; + Atmp_QA[0] = new int[order]; + Atmp_QA[1] = new int[order]; + int[] Anew_QA; + int DC_resp = 0; + + Anew_QA = Atmp_QA[order & 1]; + + /* Increase Q domain of the AR coefficients */ + for (k = 0; k < order; k++) { + DC_resp += (int) A_Q12[k]; + Anew_QA[k] = Inlines.silk_LSHIFT32((int) A_Q12[k], QA - 12); + } + + /* If the DC is unstable, we don't even need to do the full calculations */ + if (DC_resp >= 4096) { + return 0; + } + + return LPC_inverse_pred_gain_QA(Atmp_QA, order); + } +} diff --git a/app/src/main/java/org/concentus/FindLPC.java b/app/src/main/java/org/concentus/FindLPC.java new file mode 100644 index 0000000000..5f8c17f6d8 --- /dev/null +++ b/app/src/main/java/org/concentus/FindLPC.java @@ -0,0 +1,158 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class FindLPC { + + /* Finds LPC vector from correlations, and converts to NLSF */ + static void silk_find_LPC( + SilkChannelEncoder psEncC, /* I/O Encoder state */ + short[] NLSF_Q15, /* O NLSFs */ + short[] x, /* I Input signal */ + int minInvGain_Q30 /* I Inverse of max prediction gain */ + ) { + int k, subfr_length; + int[] a_Q16 = new int[SilkConstants.MAX_LPC_ORDER]; + int isInterpLower, shift; + BoxedValueInt res_nrg0 = new BoxedValueInt(0); + BoxedValueInt res_nrg1 = new BoxedValueInt(0); + BoxedValueInt rshift0 = new BoxedValueInt(0); + BoxedValueInt rshift1 = new BoxedValueInt(0); + BoxedValueInt scratch_box1 = new BoxedValueInt(0); + BoxedValueInt scratch_box2 = new BoxedValueInt(0); + + /* Used only for LSF interpolation */ + int[] a_tmp_Q16 = new int[SilkConstants.MAX_LPC_ORDER]; + int res_nrg_interp, res_nrg, res_tmp_nrg; + int res_nrg_interp_Q, res_nrg_Q, res_tmp_nrg_Q; + short[] a_tmp_Q12 = new short[SilkConstants.MAX_LPC_ORDER]; + short[] NLSF0_Q15 = new short[SilkConstants.MAX_LPC_ORDER]; + + subfr_length = psEncC.subfr_length + psEncC.predictLPCOrder; + + /* Default: no interpolation */ + psEncC.indices.NLSFInterpCoef_Q2 = 4; + + /* Burg AR analysis for the full frame */ + BurgModified.silk_burg_modified(scratch_box1, scratch_box2, a_Q16, x, 0, minInvGain_Q30, subfr_length, psEncC.nb_subfr, psEncC.predictLPCOrder); + res_nrg = scratch_box1.Val; + res_nrg_Q = scratch_box2.Val; + + if (psEncC.useInterpolatedNLSFs != 0 && psEncC.first_frame_after_reset == 0 && psEncC.nb_subfr == SilkConstants.MAX_NB_SUBFR) { + short[] LPC_res; + + /* Optimal solution for last 10 ms */ + BurgModified.silk_burg_modified(scratch_box1, scratch_box2, a_tmp_Q16, x, (2 * subfr_length), minInvGain_Q30, subfr_length, 2, psEncC.predictLPCOrder); + res_tmp_nrg = scratch_box1.Val; + res_tmp_nrg_Q = scratch_box2.Val; + + /* subtract residual energy here, as that's easier than adding it to the */ + /* residual energy of the first 10 ms in each iteration of the search below */ + shift = res_tmp_nrg_Q - res_nrg_Q; + if (shift >= 0) { + if (shift < 32) { + res_nrg = res_nrg - Inlines.silk_RSHIFT(res_tmp_nrg, shift); + } + } else { + Inlines.OpusAssert(shift > -32); + res_nrg = Inlines.silk_RSHIFT(res_nrg, -shift) - res_tmp_nrg; + res_nrg_Q = res_tmp_nrg_Q; + } + + /* Convert to NLSFs */ + NLSF.silk_A2NLSF(NLSF_Q15, a_tmp_Q16, psEncC.predictLPCOrder); + + LPC_res = new short[2 * subfr_length]; + + /* Search over interpolation indices to find the one with lowest residual energy */ + for (k = 3; k >= 0; k--) { + /* Interpolate NLSFs for first half */ + Inlines.silk_interpolate(NLSF0_Q15, psEncC.prev_NLSFq_Q15, NLSF_Q15, k, psEncC.predictLPCOrder); + + /* Convert to LPC for residual energy evaluation */ + NLSF.silk_NLSF2A(a_tmp_Q12, NLSF0_Q15, psEncC.predictLPCOrder); + + /* Calculate residual energy with NLSF interpolation */ + Filters.silk_LPC_analysis_filter(LPC_res, 0, x, 0, a_tmp_Q12, 0, 2 * subfr_length, psEncC.predictLPCOrder); + + SumSqrShift.silk_sum_sqr_shift(res_nrg0, rshift0, LPC_res, psEncC.predictLPCOrder, subfr_length - psEncC.predictLPCOrder); + + SumSqrShift.silk_sum_sqr_shift(res_nrg1, rshift1, LPC_res, psEncC.predictLPCOrder + subfr_length, subfr_length - psEncC.predictLPCOrder); + + /* Add subframe energies from first half frame */ + shift = rshift0.Val - rshift1.Val; + if (shift >= 0) { + res_nrg1.Val = Inlines.silk_RSHIFT(res_nrg1.Val, shift); + res_nrg_interp_Q = 0 - rshift0.Val; + } else { + res_nrg0.Val = Inlines.silk_RSHIFT(res_nrg0.Val, 0 - shift); + res_nrg_interp_Q = 0 - rshift1.Val; + } + res_nrg_interp = Inlines.silk_ADD32(res_nrg0.Val, res_nrg1.Val); + + /* Compare with first half energy without NLSF interpolation, or best interpolated value so far */ + shift = res_nrg_interp_Q - res_nrg_Q; + if (shift >= 0) { + if (Inlines.silk_RSHIFT(res_nrg_interp, shift) < res_nrg) { + isInterpLower = (true ? 1 : 0); + } else { + isInterpLower = (false ? 1 : 0); + } + } else if (-shift < 32) { + if (res_nrg_interp < Inlines.silk_RSHIFT(res_nrg, -shift)) { + isInterpLower = (true ? 1 : 0); + } else { + isInterpLower = (false ? 1 : 0); + } + } else { + isInterpLower = (false ? 1 : 0); + } + + /* Determine whether current interpolated NLSFs are best so far */ + if (isInterpLower == (true ? 1 : 0)) { + /* Interpolation has lower residual energy */ + res_nrg = res_nrg_interp; + res_nrg_Q = res_nrg_interp_Q; + psEncC.indices.NLSFInterpCoef_Q2 = (byte) k; + } + } + } + + if (psEncC.indices.NLSFInterpCoef_Q2 == 4) { + /* NLSF interpolation is currently inactive, calculate NLSFs from full frame AR coefficients */ + NLSF.silk_A2NLSF(NLSF_Q15, a_Q16, psEncC.predictLPCOrder); + } + + Inlines.OpusAssert(psEncC.indices.NLSFInterpCoef_Q2 == 4 || (psEncC.useInterpolatedNLSFs != 0 && psEncC.first_frame_after_reset == 0 && psEncC.nb_subfr == SilkConstants.MAX_NB_SUBFR)); + + } +} diff --git a/app/src/main/java/org/concentus/FindLTP.java b/app/src/main/java/org/concentus/FindLTP.java new file mode 100644 index 0000000000..9ee550d508 --- /dev/null +++ b/app/src/main/java/org/concentus/FindLTP.java @@ -0,0 +1,296 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class FindLTP { + + /* Head room for correlations */ + private static final int LTP_CORRS_HEAD_ROOM = 2; + + /// + /// Finds linear prediction coeffecients and weights + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + static void silk_find_LTP( + short[] b_Q14, /* O LTP coefs [SilkConstants.MAX_NB_SUBFR * SilkConstants.LTP_ORDER] */ + int[] WLTP, /* O Weight for LTP quantization [SilkConstants.MAX_NB_SUBFR * SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER] */ + BoxedValueInt LTPredCodGain_Q7, /* O LTP coding gain */ + short[] r_lpc, /* I residual signal after LPC signal + state for first 10 ms */ + int[] lag, /* I LTP lags [SilkConstants.MAX_NB_SUBFR] */ + int[] Wght_Q15, /* I weights [SilkConstants.MAX_NB_SUBFR] */ + int subfr_length, /* I subframe length */ + int nb_subfr, /* I number of subframes */ + int mem_offset, /* I number of samples in LTP memory */ + int[] corr_rshifts /* O right shifts applied to correlations [SilkConstants.MAX_NB_SUBFR] */ + ) { + int i, k, lshift; + int r_ptr; + int lag_ptr; + int b_Q14_ptr; + + int regu; + int WLTP_ptr; + int[] b_Q16 = new int[SilkConstants.LTP_ORDER]; + int[] delta_b_Q14 = new int[SilkConstants.LTP_ORDER]; + int[] d_Q14 = new int[SilkConstants.MAX_NB_SUBFR]; + int[] nrg = new int[SilkConstants.MAX_NB_SUBFR]; + int g_Q26; + int[] w = new int[SilkConstants.MAX_NB_SUBFR]; + int WLTP_max, max_abs_d_Q14, max_w_bits; + + int temp32, denom32; + int extra_shifts; + int rr_shifts, maxRshifts, maxRshifts_wxtra, LZs; + int LPC_res_nrg, LPC_LTP_res_nrg, div_Q16; + int[] Rr = new int[SilkConstants.LTP_ORDER]; + int[] rr = new int[SilkConstants.MAX_NB_SUBFR]; + int wd, m_Q12; + + b_Q14_ptr = 0; + WLTP_ptr = 0; + r_ptr = mem_offset; + for (k = 0; k < nb_subfr; k++) { + lag_ptr = r_ptr - (lag[k] + SilkConstants.LTP_ORDER / 2); + BoxedValueInt boxed_rr = new BoxedValueInt(0); + BoxedValueInt boxed_rr_shift = new BoxedValueInt(0); + SumSqrShift.silk_sum_sqr_shift(boxed_rr, boxed_rr_shift, r_lpc, r_ptr, subfr_length); + /* rr[ k ] in Q( -rr_shifts ) */ + rr[k] = boxed_rr.Val; + rr_shifts = boxed_rr_shift.Val; + + /* Assure headroom */ + LZs = Inlines.silk_CLZ32(rr[k]); + if (LZs < LTP_CORRS_HEAD_ROOM) { + rr[k] = Inlines.silk_RSHIFT_ROUND(rr[k], LTP_CORRS_HEAD_ROOM - LZs); + rr_shifts += (LTP_CORRS_HEAD_ROOM - LZs); + } + corr_rshifts[k] = rr_shifts; + BoxedValueInt boxed_shifts = new BoxedValueInt(corr_rshifts[k]); + CorrelateMatrix.silk_corrMatrix(r_lpc, lag_ptr, subfr_length, SilkConstants.LTP_ORDER, LTP_CORRS_HEAD_ROOM, WLTP, WLTP_ptr, boxed_shifts); + /* WLTP_ptr in Q( -corr_rshifts[ k ] ) */ + corr_rshifts[k] = boxed_shifts.Val; + + /* The correlation vector always has lower max abs value than rr and/or RR so head room is assured */ + CorrelateMatrix.silk_corrVector(r_lpc, lag_ptr, r_lpc, r_ptr, subfr_length, SilkConstants.LTP_ORDER, Rr, corr_rshifts[k]); + /* Rr_ptr in Q( -corr_rshifts[ k ] ) */ + if (corr_rshifts[k] > rr_shifts) { + rr[k] = Inlines.silk_RSHIFT(rr[k], corr_rshifts[k] - rr_shifts); + /* rr[ k ] in Q( -corr_rshifts[ k ] ) */ + } + Inlines.OpusAssert(rr[k] >= 0); + + regu = 1; + regu = Inlines.silk_SMLAWB(regu, rr[k], ((int) ((TuningParameters.LTP_DAMPING / 3) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_DAMPING / 3, 16)*/); + regu = Inlines.silk_SMLAWB(regu, Inlines.MatrixGet(WLTP, WLTP_ptr, 0, 0, SilkConstants.LTP_ORDER), ((int) ((TuningParameters.LTP_DAMPING / 3) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_DAMPING / 3, 16)*/); + regu = Inlines.silk_SMLAWB(regu, Inlines.MatrixGet(WLTP, WLTP_ptr, SilkConstants.LTP_ORDER - 1, SilkConstants.LTP_ORDER - 1, SilkConstants.LTP_ORDER), ((int) ((TuningParameters.LTP_DAMPING / 3) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_DAMPING / 3, 16)*/); + RegularizeCorrelations.silk_regularize_correlations(WLTP, WLTP_ptr, rr, k, regu, SilkConstants.LTP_ORDER); + + LinearAlgebra.silk_solve_LDL(WLTP, WLTP_ptr, SilkConstants.LTP_ORDER, Rr, b_Q16); + /* WLTP_ptr and Rr_ptr both in Q(-corr_rshifts[k]) */ + + /* Limit and store in Q14 */ + silk_fit_LTP(b_Q16, b_Q14, b_Q14_ptr); + + /* Calculate residual energy */ + nrg[k] = ResidualEnergy.silk_residual_energy16_covar(b_Q14, b_Q14_ptr, WLTP, WLTP_ptr, Rr, rr[k], SilkConstants.LTP_ORDER, 14); + /* nrg in Q( -corr_rshifts[ k ] ) */ + + /* temp = Wght[ k ] / ( nrg[ k ] * Wght[ k ] + 0.01f * subfr_length ); */ + extra_shifts = Inlines.silk_min_int(corr_rshifts[k], LTP_CORRS_HEAD_ROOM); + denom32 = Inlines.silk_LSHIFT_SAT32(Inlines.silk_SMULWB(nrg[k], Wght_Q15[k]), 1 + extra_shifts) + + /* Q( -corr_rshifts[ k ] + extra_shifts ) */ Inlines.silk_RSHIFT(Inlines.silk_SMULWB((int) subfr_length, 655), corr_rshifts[k] - extra_shifts); + /* Q( -corr_rshifts[ k ] + extra_shifts ) */ + denom32 = Inlines.silk_max(denom32, 1); + Inlines.OpusAssert(((long) Wght_Q15[k] << 16) < Integer.MAX_VALUE); + /* Wght always < 0.5 in Q0 */ + temp32 = Inlines.silk_DIV32(Inlines.silk_LSHIFT((int) Wght_Q15[k], 16), denom32); + /* Q( 15 + 16 + corr_rshifts[k] - extra_shifts ) */ + temp32 = Inlines.silk_RSHIFT(temp32, 31 + corr_rshifts[k] - extra_shifts - 26); + /* Q26 */ + + /* Limit temp such that the below scaling never wraps around */ + WLTP_max = 0; + for (i = WLTP_ptr; i < WLTP_ptr + (SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER); i++) { + WLTP_max = Inlines.silk_max(WLTP[i], WLTP_max); + } + lshift = Inlines.silk_CLZ32(WLTP_max) - 1 - 3; + /* keep 3 bits free for vq_nearest_neighbor */ + Inlines.OpusAssert(26 - 18 + lshift >= 0); + if (26 - 18 + lshift < 31) { + temp32 = Inlines.silk_min_32(temp32, Inlines.silk_LSHIFT((int) 1, 26 - 18 + lshift)); + } + + Inlines.silk_scale_vector32_Q26_lshift_18(WLTP, WLTP_ptr, temp32, SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER); + /* WLTP_ptr in Q( 18 - corr_rshifts[ k ] ) */ + + w[k] = Inlines.MatrixGet(WLTP, WLTP_ptr, SilkConstants.LTP_ORDER / 2, SilkConstants.LTP_ORDER / 2, SilkConstants.LTP_ORDER); + /* w in Q( 18 - corr_rshifts[ k ] ) */ + Inlines.OpusAssert(w[k] >= 0); + + r_ptr += subfr_length; + b_Q14_ptr += SilkConstants.LTP_ORDER; + WLTP_ptr += (SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER); + } + + maxRshifts = 0; + for (k = 0; k < nb_subfr; k++) { + maxRshifts = Inlines.silk_max_int(corr_rshifts[k], maxRshifts); + } + + /* Compute LTP coding gain */ + if (LTPredCodGain_Q7 != null) { + LPC_LTP_res_nrg = 0; + LPC_res_nrg = 0; + Inlines.OpusAssert(LTP_CORRS_HEAD_ROOM >= 2); + /* Check that no overflow will happen when adding */ + for (k = 0; k < nb_subfr; k++) { + LPC_res_nrg = Inlines.silk_ADD32(LPC_res_nrg, Inlines.silk_RSHIFT(Inlines.silk_ADD32(Inlines.silk_SMULWB(rr[k], Wght_Q15[k]), 1), 1 + (maxRshifts - corr_rshifts[k]))); + /* Q( -maxRshifts ) */ + LPC_LTP_res_nrg = Inlines.silk_ADD32(LPC_LTP_res_nrg, Inlines.silk_RSHIFT(Inlines.silk_ADD32(Inlines.silk_SMULWB(nrg[k], Wght_Q15[k]), 1), 1 + (maxRshifts - corr_rshifts[k]))); + /* Q( -maxRshifts ) */ + } + LPC_LTP_res_nrg = Inlines.silk_max(LPC_LTP_res_nrg, 1); + /* avoid division by zero */ + + div_Q16 = Inlines.silk_DIV32_varQ(LPC_res_nrg, LPC_LTP_res_nrg, 16); + LTPredCodGain_Q7.Val = (int) Inlines.silk_SMULBB(3, Inlines.silk_lin2log(div_Q16) - (16 << 7)); + + Inlines.OpusAssert(LTPredCodGain_Q7.Val == (int) Inlines.silk_SAT16(Inlines.silk_MUL(3, Inlines.silk_lin2log(div_Q16) - (16 << 7)))); + } + + /* smoothing */ + /* d = sum( B, 1 ); */ + b_Q14_ptr = 0; + for (k = 0; k < nb_subfr; k++) { + d_Q14[k] = 0; + for (i = b_Q14_ptr; i < b_Q14_ptr + SilkConstants.LTP_ORDER; i++) { + d_Q14[k] += b_Q14[i]; + } + b_Q14_ptr += SilkConstants.LTP_ORDER; + } + + /* m = ( w * d' ) / ( sum( w ) + 1e-3 ); */ + + /* Find maximum absolute value of d_Q14 and the bits used by w in Q0 */ + max_abs_d_Q14 = 0; + max_w_bits = 0; + for (k = 0; k < nb_subfr; k++) { + max_abs_d_Q14 = Inlines.silk_max_32(max_abs_d_Q14, Inlines.silk_abs(d_Q14[k])); + /* w[ k ] is in Q( 18 - corr_rshifts[ k ] ) */ + /* Find bits needed in Q( 18 - maxRshifts ) */ + max_w_bits = Inlines.silk_max_32(max_w_bits, 32 - Inlines.silk_CLZ32(w[k]) + corr_rshifts[k] - maxRshifts); + } + + /* max_abs_d_Q14 = (5 << 15); worst case, i.e. SilkConstants.LTP_ORDER * -silk_int16_MIN */ + Inlines.OpusAssert(max_abs_d_Q14 <= (5 << 15)); + + /* How many bits is needed for w*d' in Q( 18 - maxRshifts ) in the worst case, of all d_Q14's being equal to max_abs_d_Q14 */ + extra_shifts = max_w_bits + 32 - Inlines.silk_CLZ32(max_abs_d_Q14) - 14; + + /* Subtract what we got available; bits in output var plus maxRshifts */ + extra_shifts -= (32 - 1 - 2 + maxRshifts); + /* Keep sign bit free as well as 2 bits for accumulation */ + extra_shifts = Inlines.silk_max_int(extra_shifts, 0); + + maxRshifts_wxtra = maxRshifts + extra_shifts; + + temp32 = Inlines.silk_RSHIFT(262, maxRshifts + extra_shifts) + 1; + /* 1e-3f in Q( 18 - (maxRshifts + extra_shifts) ) */ + wd = 0; + for (k = 0; k < nb_subfr; k++) { + /* w has at least 2 bits of headroom so no overflow should happen */ + temp32 = Inlines.silk_ADD32(temp32, Inlines.silk_RSHIFT(w[k], maxRshifts_wxtra - corr_rshifts[k])); + /* Q( 18 - maxRshifts_wxtra ) */ + wd = Inlines.silk_ADD32(wd, Inlines.silk_LSHIFT(Inlines.silk_SMULWW(Inlines.silk_RSHIFT(w[k], maxRshifts_wxtra - corr_rshifts[k]), d_Q14[k]), 2)); + /* Q( 18 - maxRshifts_wxtra ) */ + } + m_Q12 = Inlines.silk_DIV32_varQ(wd, temp32, 12); + + b_Q14_ptr = 0; + for (k = 0; k < nb_subfr; k++) { + /* w[ k ] from Q( 18 - corr_rshifts[ k ] ) to Q( 16 ) */ + if (2 - corr_rshifts[k] > 0) { + temp32 = Inlines.silk_RSHIFT(w[k], 2 - corr_rshifts[k]); + } else { + temp32 = Inlines.silk_LSHIFT_SAT32(w[k], corr_rshifts[k] - 2); + } + + g_Q26 = Inlines.silk_MUL( + Inlines.silk_DIV32( + ((int) ((TuningParameters.LTP_SMOOTHING) * ((long) 1 << (26)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_SMOOTHING, 26)*/, + Inlines.silk_RSHIFT(((int) ((TuningParameters.LTP_SMOOTHING) * ((long) 1 << (26)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_SMOOTHING, 26)*/, 10) + temp32), /* Q10 */ + Inlines.silk_LSHIFT_SAT32(Inlines.silk_SUB_SAT32((int) m_Q12, Inlines.silk_RSHIFT(d_Q14[k], 2)), 4)); + /* Q16 */ + + temp32 = 0; + for (i = 0; i < SilkConstants.LTP_ORDER; i++) { + delta_b_Q14[i] = Inlines.silk_max_16(b_Q14[b_Q14_ptr + i], (short) 1638); + /* 1638_Q14 = 0.1_Q0 */ + temp32 += delta_b_Q14[i]; + /* Q14 */ + } + temp32 = Inlines.silk_DIV32(g_Q26, temp32); + /* Q14 . Q12 */ + for (i = 0; i < SilkConstants.LTP_ORDER; i++) { + b_Q14[b_Q14_ptr + i] = (short) (Inlines.silk_LIMIT_32((int) b_Q14[b_Q14_ptr + i] + Inlines.silk_SMULWB(Inlines.silk_LSHIFT_SAT32(temp32, 4), delta_b_Q14[i]), -16000, 28000)); + } + b_Q14_ptr += SilkConstants.LTP_ORDER; + } + } + + /// + /// + /// + /// [SilkConstants.LTP_ORDER] + /// [SilkConstants.LTP_ORDER] + /// + static void silk_fit_LTP( + int[] LTP_coefs_Q16, + short[] LTP_coefs_Q14, + int LTP_coefs_Q14_ptr) { + int i; + + for (i = 0; i < SilkConstants.LTP_ORDER; i++) { + LTP_coefs_Q14[LTP_coefs_Q14_ptr + i] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(LTP_coefs_Q16[i], 2)); + } + } +} diff --git a/app/src/main/java/org/concentus/FindPitchLags.java b/app/src/main/java/org/concentus/FindPitchLags.java new file mode 100644 index 0000000000..02dcb3bf4b --- /dev/null +++ b/app/src/main/java/org/concentus/FindPitchLags.java @@ -0,0 +1,165 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class FindPitchLags { + + /* Find pitch lags */ + static void silk_find_pitch_lags( + SilkChannelEncoder psEnc, /* I/O encoder state */ + SilkEncoderControl psEncCtrl, /* I/O encoder control */ + short[] res, /* O residual */ + short[] x, /* I Speech signal */ + int x_ptr + ) { + int buf_len, i, scale; + int thrhld_Q13, res_nrg; + int x_buf, x_buf_ptr; + short[] Wsig; + int Wsig_ptr; + int[] auto_corr = new int[SilkConstants.MAX_FIND_PITCH_LPC_ORDER + 1]; + short[] rc_Q15 = new short[SilkConstants.MAX_FIND_PITCH_LPC_ORDER]; + int[] A_Q24 = new int[SilkConstants.MAX_FIND_PITCH_LPC_ORDER]; + short[] A_Q12 = new short[SilkConstants.MAX_FIND_PITCH_LPC_ORDER]; + + /** + * *************************************** + */ + /* Set up buffer lengths etc based on Fs */ + /** + * *************************************** + */ + buf_len = psEnc.la_pitch + psEnc.frame_length + psEnc.ltp_mem_length; + + /* Safety check */ + Inlines.OpusAssert(buf_len >= psEnc.pitch_LPC_win_length); + + x_buf = x_ptr - psEnc.ltp_mem_length; + + /** + * ********************************** + */ + /* Estimate LPC AR coefficients */ + /** + * ********************************** + */ + + /* Calculate windowed signal */ + Wsig = new short[psEnc.pitch_LPC_win_length]; + + /* First LA_LTP samples */ + x_buf_ptr = x_buf + buf_len - psEnc.pitch_LPC_win_length; + Wsig_ptr = 0; + ApplySineWindow.silk_apply_sine_window(Wsig, Wsig_ptr, x, x_buf_ptr, 1, psEnc.la_pitch); + + /* Middle un - windowed samples */ + Wsig_ptr += psEnc.la_pitch; + x_buf_ptr += psEnc.la_pitch; + System.arraycopy(x, x_buf_ptr, Wsig, Wsig_ptr, (psEnc.pitch_LPC_win_length - Inlines.silk_LSHIFT(psEnc.la_pitch, 1))); + + /* Last LA_LTP samples */ + Wsig_ptr += psEnc.pitch_LPC_win_length - Inlines.silk_LSHIFT(psEnc.la_pitch, 1); + x_buf_ptr += psEnc.pitch_LPC_win_length - Inlines.silk_LSHIFT(psEnc.la_pitch, 1); + ApplySineWindow.silk_apply_sine_window(Wsig, Wsig_ptr, x, x_buf_ptr, 2, psEnc.la_pitch); + + /* Calculate autocorrelation sequence */ + BoxedValueInt boxed_scale = new BoxedValueInt(0); + Autocorrelation.silk_autocorr(auto_corr, boxed_scale, Wsig, psEnc.pitch_LPC_win_length, psEnc.pitchEstimationLPCOrder + 1); + scale = boxed_scale.Val; + + /* Add white noise, as fraction of energy */ + auto_corr[0] = Inlines.silk_SMLAWB(auto_corr[0], auto_corr[0], ((int) ((TuningParameters.FIND_PITCH_WHITE_NOISE_FRACTION) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_PITCH_WHITE_NOISE_FRACTION, 16)*/) + 1; + + /* Calculate the reflection coefficients using schur */ + res_nrg = Schur.silk_schur(rc_Q15, auto_corr, psEnc.pitchEstimationLPCOrder); + + /* Prediction gain */ + psEncCtrl.predGain_Q16 = Inlines.silk_DIV32_varQ(auto_corr[0], Inlines.silk_max_int(res_nrg, 1), 16); + + /* Convert reflection coefficients to prediction coefficients */ + K2A.silk_k2a(A_Q24, rc_Q15, psEnc.pitchEstimationLPCOrder); + + /* Convert From 32 bit Q24 to 16 bit Q12 coefs */ + for (i = 0; i < psEnc.pitchEstimationLPCOrder; i++) { + A_Q12[i] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT(A_Q24[i], 12)); + } + + /* Do BWE */ + BWExpander.silk_bwexpander(A_Q12, psEnc.pitchEstimationLPCOrder, ((int) ((TuningParameters.FIND_PITCH_BANDWIDTH_EXPANSION) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_PITCH_BANDWIDTH_EXPANSION, 16)*/); + + /** + * ************************************** + */ + /* LPC analysis filtering */ + /** + * ************************************** + */ + Filters.silk_LPC_analysis_filter(res, 0, x, x_buf, A_Q12, 0, buf_len, psEnc.pitchEstimationLPCOrder); + + if (psEnc.indices.signalType != SilkConstants.TYPE_NO_VOICE_ACTIVITY && psEnc.first_frame_after_reset == 0) { + /* Threshold for pitch estimator */ + thrhld_Q13 = ((int) ((0.6f) * ((long) 1 << (13)) + 0.5))/*Inlines.SILK_CONST(0.6f, 13)*/; + thrhld_Q13 = Inlines.silk_SMLABB(thrhld_Q13, ((int) ((-0.004f) * ((long) 1 << (13)) + 0.5))/*Inlines.SILK_CONST(-0.004f, 13)*/, psEnc.pitchEstimationLPCOrder); + thrhld_Q13 = Inlines.silk_SMLAWB(thrhld_Q13, ((int) ((-0.1f) * ((long) 1 << (21)) + 0.5))/*Inlines.SILK_CONST(-0.1f, 21)*/, psEnc.speech_activity_Q8); + thrhld_Q13 = Inlines.silk_SMLABB(thrhld_Q13, ((int) ((-0.15f) * ((long) 1 << (13)) + 0.5))/*Inlines.SILK_CONST(-0.15f, 13)*/, Inlines.silk_RSHIFT(psEnc.prevSignalType, 1)); + thrhld_Q13 = Inlines.silk_SMLAWB(thrhld_Q13, ((int) ((-0.1f) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(-0.1f, 14)*/, psEnc.input_tilt_Q15); + thrhld_Q13 = Inlines.silk_SAT16(thrhld_Q13); + + /** + * ************************************** + */ + /* Call pitch estimator */ + /** + * ************************************** + */ + BoxedValueShort boxed_lagIndex = new BoxedValueShort(psEnc.indices.lagIndex); + BoxedValueByte boxed_contourIndex = new BoxedValueByte(psEnc.indices.contourIndex); + BoxedValueInt boxed_LTPcorr = new BoxedValueInt(psEnc.LTPCorr_Q15); + if (PitchAnalysisCore.silk_pitch_analysis_core(res, psEncCtrl.pitchL, boxed_lagIndex, boxed_contourIndex, + boxed_LTPcorr, psEnc.prevLag, psEnc.pitchEstimationThreshold_Q16, + (int) thrhld_Q13, psEnc.fs_kHz, psEnc.pitchEstimationComplexity, psEnc.nb_subfr) == 0) { + psEnc.indices.signalType = SilkConstants.TYPE_VOICED; + } else { + psEnc.indices.signalType = SilkConstants.TYPE_UNVOICED; + } + + psEnc.indices.lagIndex = boxed_lagIndex.Val; + psEnc.indices.contourIndex = boxed_contourIndex.Val; + psEnc.LTPCorr_Q15 = boxed_LTPcorr.Val; + } else { + Arrays.MemSet(psEncCtrl.pitchL, 0, SilkConstants.MAX_NB_SUBFR); + psEnc.indices.lagIndex = 0; + psEnc.indices.contourIndex = 0; + psEnc.LTPCorr_Q15 = 0; + } + } +} diff --git a/app/src/main/java/org/concentus/FindPredCoefs.java b/app/src/main/java/org/concentus/FindPredCoefs.java new file mode 100644 index 0000000000..053bac986a --- /dev/null +++ b/app/src/main/java/org/concentus/FindPredCoefs.java @@ -0,0 +1,163 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class FindPredCoefs { + + static void silk_find_pred_coefs( + SilkChannelEncoder psEnc, /* I/O encoder state */ + SilkEncoderControl psEncCtrl, /* I/O encoder control */ + short[] res_pitch, /* I Residual from pitch analysis */ + short[] x, /* I Speech signal */ + int x_ptr, + int condCoding /* I The type of conditional coding to use */ + ) { + int i; + int[] invGains_Q16 = new int[SilkConstants.MAX_NB_SUBFR]; + int[] local_gains = new int[SilkConstants.MAX_NB_SUBFR]; + int[] Wght_Q15 = new int[SilkConstants.MAX_NB_SUBFR]; + short[] NLSF_Q15 = new short[SilkConstants.MAX_LPC_ORDER]; + int x_ptr2; + int x_pre_ptr; + short[] LPC_in_pre; + int tmp, min_gain_Q16, minInvGain_Q30; + int[] LTP_corrs_rshift = new int[SilkConstants.MAX_NB_SUBFR]; + + /* weighting for weighted least squares */ + min_gain_Q16 = Integer.MAX_VALUE >> 6; + for (i = 0; i < psEnc.nb_subfr; i++) { + min_gain_Q16 = Inlines.silk_min(min_gain_Q16, psEncCtrl.Gains_Q16[i]); + } + for (i = 0; i < psEnc.nb_subfr; i++) { + /* Divide to Q16 */ + Inlines.OpusAssert(psEncCtrl.Gains_Q16[i] > 0); + /* Invert and normalize gains, and ensure that maximum invGains_Q16 is within range of a 16 bit int */ + invGains_Q16[i] = Inlines.silk_DIV32_varQ(min_gain_Q16, psEncCtrl.Gains_Q16[i], 16 - 2); + + /* Ensure Wght_Q15 a minimum value 1 */ + invGains_Q16[i] = Inlines.silk_max(invGains_Q16[i], 363); + + /* Square the inverted gains */ + Inlines.OpusAssert(invGains_Q16[i] == Inlines.silk_SAT16(invGains_Q16[i])); + tmp = Inlines.silk_SMULWB(invGains_Q16[i], invGains_Q16[i]); + Wght_Q15[i] = Inlines.silk_RSHIFT(tmp, 1); + + /* Invert the inverted and normalized gains */ + local_gains[i] = Inlines.silk_DIV32(((int) 1 << 16), invGains_Q16[i]); + } + + LPC_in_pre = new short[psEnc.nb_subfr * psEnc.predictLPCOrder + psEnc.frame_length]; + if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED) { + int[] WLTP; + + /** + * ******* + */ + /* VOICED */ + /** + * ******* + */ + Inlines.OpusAssert(psEnc.ltp_mem_length - psEnc.predictLPCOrder >= psEncCtrl.pitchL[0] + SilkConstants.LTP_ORDER / 2); + + WLTP = new int[psEnc.nb_subfr * SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER]; + + /* LTP analysis */ + BoxedValueInt boxed_codgain = new BoxedValueInt(psEncCtrl.LTPredCodGain_Q7); + FindLTP.silk_find_LTP(psEncCtrl.LTPCoef_Q14, WLTP, boxed_codgain, + res_pitch, psEncCtrl.pitchL, Wght_Q15, psEnc.subfr_length, + psEnc.nb_subfr, psEnc.ltp_mem_length, LTP_corrs_rshift); + psEncCtrl.LTPredCodGain_Q7 = boxed_codgain.Val; + + /* Quantize LTP gain parameters */ + BoxedValueByte boxed_periodicity = new BoxedValueByte(psEnc.indices.PERIndex); + BoxedValueInt boxed_gain = new BoxedValueInt(psEnc.sum_log_gain_Q7); + QuantizeLTPGains.silk_quant_LTP_gains(psEncCtrl.LTPCoef_Q14, psEnc.indices.LTPIndex, boxed_periodicity, + boxed_gain, WLTP, psEnc.mu_LTP_Q9, psEnc.LTPQuantLowComplexity, psEnc.nb_subfr + ); + psEnc.indices.PERIndex = boxed_periodicity.Val; + psEnc.sum_log_gain_Q7 = boxed_gain.Val; + + /* Control LTP scaling */ + LTPScaleControl.silk_LTP_scale_ctrl(psEnc, psEncCtrl, condCoding); + + /* Create LTP residual */ + LTPAnalysisFilter.silk_LTP_analysis_filter(LPC_in_pre, x, x_ptr - psEnc.predictLPCOrder, psEncCtrl.LTPCoef_Q14, + psEncCtrl.pitchL, invGains_Q16, psEnc.subfr_length, psEnc.nb_subfr, psEnc.predictLPCOrder); + + } else { + /** + * ********* + */ + /* UNVOICED */ + /** + * ********* + */ + /* Create signal with prepended subframes, scaled by inverse gains */ + x_ptr2 = x_ptr - psEnc.predictLPCOrder; + x_pre_ptr = 0; + for (i = 0; i < psEnc.nb_subfr; i++) { + Inlines.silk_scale_copy_vector16(LPC_in_pre, x_pre_ptr, x, x_ptr2, invGains_Q16[i], + psEnc.subfr_length + psEnc.predictLPCOrder); + x_pre_ptr += psEnc.subfr_length + psEnc.predictLPCOrder; + x_ptr2 += psEnc.subfr_length; + } + + Arrays.MemSet(psEncCtrl.LTPCoef_Q14, (short) 0, psEnc.nb_subfr * SilkConstants.LTP_ORDER); + psEncCtrl.LTPredCodGain_Q7 = 0; + psEnc.sum_log_gain_Q7 = 0; + } + + /* Limit on total predictive coding gain */ + if (psEnc.first_frame_after_reset != 0) { + minInvGain_Q30 = ((int) ((1.0f / SilkConstants.MAX_PREDICTION_POWER_GAIN_AFTER_RESET) * ((long) 1 << (30)) + 0.5))/*Inlines.SILK_CONST(1.0f / SilkConstants.MAX_PREDICTION_POWER_GAIN_AFTER_RESET, 30)*/; + } else { + minInvGain_Q30 = Inlines.silk_log2lin(Inlines.silk_SMLAWB(16 << 7, (int) psEncCtrl.LTPredCodGain_Q7, ((int) ((1.0f / 3f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f / 3f, 16)*/)); + /* Q16 */ + minInvGain_Q30 = Inlines.silk_DIV32_varQ(minInvGain_Q30, + Inlines.silk_SMULWW(((int) ((SilkConstants.MAX_PREDICTION_POWER_GAIN) * ((long) 1 << (0)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.MAX_PREDICTION_POWER_GAIN, 0)*/, + Inlines.silk_SMLAWB(((int) ((0.25f) * ((long) 1 << (18)) + 0.5))/*Inlines.SILK_CONST(0.25f, 18)*/, ((int) ((0.75f) * ((long) 1 << (18)) + 0.5))/*Inlines.SILK_CONST(0.75f, 18)*/, psEncCtrl.coding_quality_Q14)), 14); + } + + /* LPC_in_pre contains the LTP-filtered input for voiced, and the unfiltered input for unvoiced */ + FindLPC.silk_find_LPC(psEnc, NLSF_Q15, LPC_in_pre, minInvGain_Q30); + + /* Quantize LSFs */ + NLSF.silk_process_NLSFs(psEnc, psEncCtrl.PredCoef_Q12, NLSF_Q15, psEnc.prev_NLSFq_Q15); + + /* Calculate residual energy using quantized LPC coefficients */ + ResidualEnergy.silk_residual_energy(psEncCtrl.ResNrg, psEncCtrl.ResNrgQ, LPC_in_pre, psEncCtrl.PredCoef_Q12, local_gains, + psEnc.subfr_length, psEnc.nb_subfr, psEnc.predictLPCOrder); + + /* Copy to prediction struct for use in next frame for interpolation */ + System.arraycopy(NLSF_Q15, 0, psEnc.prev_NLSFq_Q15, 0, SilkConstants.MAX_LPC_ORDER); + } +} diff --git a/app/src/main/java/org/concentus/GainQuantization.java b/app/src/main/java/org/concentus/GainQuantization.java new file mode 100644 index 0000000000..c81f5e0c84 --- /dev/null +++ b/app/src/main/java/org/concentus/GainQuantization.java @@ -0,0 +1,161 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class GainQuantization { + + private static final int OFFSET = ((SilkConstants.MIN_QGAIN_DB * 128) / 6 + 16 * 128); + private static final int SCALE_Q16 = ((65536 * (SilkConstants.N_LEVELS_QGAIN - 1)) / (((SilkConstants.MAX_QGAIN_DB - SilkConstants.MIN_QGAIN_DB) * 128) / 6)); + private static final int INV_SCALE_Q16 = ((65536 * (((SilkConstants.MAX_QGAIN_DB - SilkConstants.MIN_QGAIN_DB) * 128) / 6)) / (SilkConstants.N_LEVELS_QGAIN - 1)); + + /// + /// Gain scalar quantization with hysteresis, uniform on log scale + /// + /// O gain indices [MAX_NB_SUBFR] + /// I/O gains (quantized out) [MAX_NB_SUBFR] + /// I/O last index in previous frame. [Porting note] original implementation passed this as an int8* + /// I first gain is delta coded if 1 + /// I number of subframes + static void silk_gains_quant( + byte[] ind, + int[] gain_Q16, + BoxedValueByte prev_ind, + int conditional, + int nb_subfr) { + int k, double_step_size_threshold; + + for (k = 0; k < nb_subfr; k++) { + // Debug.WriteLine("2a 0x{0:x}", (uint)gain_Q16[k]); + /* Convert to log scale, scale, floor() */ + ind[k] = (byte) (Inlines.silk_SMULWB(SCALE_Q16, Inlines.silk_lin2log(gain_Q16[k]) - OFFSET)); + + /* Round towards previous quantized gain (hysteresis) */ + if (ind[k] < prev_ind.Val) { + ind[k]++; + } + + ind[k] = (byte) (Inlines.silk_LIMIT_int(ind[k], 0, SilkConstants.N_LEVELS_QGAIN - 1)); + + /* Compute delta indices and limit */ + if (k == 0 && conditional == 0) { + /* Full index */ + ind[k] = (byte) (Inlines.silk_LIMIT_int(ind[k], prev_ind.Val + SilkConstants.MIN_DELTA_GAIN_QUANT, SilkConstants.N_LEVELS_QGAIN - 1)); + prev_ind.Val = ind[k]; + } else { + /* Delta index */ + ind[k] = (byte) (ind[k] - prev_ind.Val); + + /* Double the quantization step size for large gain increases, so that the max gain level can be reached */ + double_step_size_threshold = 2 * SilkConstants.MAX_DELTA_GAIN_QUANT - SilkConstants.N_LEVELS_QGAIN + prev_ind.Val; + if (ind[k] > double_step_size_threshold) { + ind[k] = (byte) (double_step_size_threshold + Inlines.silk_RSHIFT(ind[k] - double_step_size_threshold + 1, 1)); + } + + ind[k] = (byte) (Inlines.silk_LIMIT_int(ind[k], SilkConstants.MIN_DELTA_GAIN_QUANT, SilkConstants.MAX_DELTA_GAIN_QUANT)); + + /* Accumulate deltas */ + if (ind[k] > double_step_size_threshold) { + prev_ind.Val = (byte) (prev_ind.Val + (byte) (Inlines.silk_LSHIFT(ind[k], 1) - double_step_size_threshold)); + } else { + prev_ind.Val = (byte) (prev_ind.Val + ind[k]); + } + + /* Shift to make non-negative */ + ind[k] -= SilkConstants.MIN_DELTA_GAIN_QUANT; + // Debug.WriteLine("2b 0x{0:x}", (uint)ind[k]); + } + + /* Scale and convert to linear scale */ + gain_Q16[k] = Inlines.silk_log2lin(Inlines.silk_min_32(Inlines.silk_SMULWB(INV_SCALE_Q16, prev_ind.Val) + OFFSET, 3967)); + /* 3967 = 31 in Q7 */ + } + } + + /// + /// Gains scalar dequantization, uniform on log scale + /// + /// O quantized gains [MAX_NB_SUBFR] + /// I gain indices [MAX_NB_SUBFR] + /// I/O last index in previous frame [Porting note] original implementation passed this as an int8* + /// I first gain is delta coded if 1 + /// I number of subframes + static void silk_gains_dequant( + int[] gain_Q16, + byte[] ind, + BoxedValueByte prev_ind, + int conditional, + int nb_subfr) { + int k, ind_tmp, double_step_size_threshold; + + for (k = 0; k < nb_subfr; k++) { + if (k == 0 && conditional == 0) { + /* Gain index is not allowed to go down more than 16 steps (~21.8 dB) */ + prev_ind.Val = (byte) (Inlines.silk_max_int(ind[k], prev_ind.Val - 16)); + } else { + /* Delta index */ + ind_tmp = ind[k] + SilkConstants.MIN_DELTA_GAIN_QUANT; + + /* Accumulate deltas */ + double_step_size_threshold = 2 * SilkConstants.MAX_DELTA_GAIN_QUANT - SilkConstants.N_LEVELS_QGAIN + prev_ind.Val; + if (ind_tmp > double_step_size_threshold) { + prev_ind.Val = (byte) (prev_ind.Val + (byte) (Inlines.silk_LSHIFT(ind_tmp, 1) - double_step_size_threshold)); + } else { + prev_ind.Val = (byte) (prev_ind.Val + (byte) (ind_tmp)); + } + } + + prev_ind.Val = (byte) (Inlines.silk_LIMIT_int(prev_ind.Val, 0, SilkConstants.N_LEVELS_QGAIN - 1)); + + /* Scale and convert to linear scale */ + gain_Q16[k] = Inlines.silk_log2lin(Inlines.silk_min_32(Inlines.silk_SMULWB(INV_SCALE_Q16, prev_ind.Val) + OFFSET, 3967)); + /* 3967 = 31 in Q7 */ + } + } + + /// + /// Compute unique identifier of gain indices vector + /// + /// I gain indices [MAX_NB_SUBFR] + /// I number of subframes + /// unique identifier of gains + static int silk_gains_ID(byte[] ind, int nb_subfr) { + int k; + int gainsID; + + gainsID = 0; + for (k = 0; k < nb_subfr; k++) { + gainsID = Inlines.silk_ADD_LSHIFT32(ind[k], gainsID, 8); + } + + return gainsID; + } +} diff --git a/app/src/main/java/org/concentus/HPVariableCutoff.java b/app/src/main/java/org/concentus/HPVariableCutoff.java new file mode 100644 index 0000000000..240a49f114 --- /dev/null +++ b/app/src/main/java/org/concentus/HPVariableCutoff.java @@ -0,0 +1,79 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class HPVariableCutoff { + + /// + /// High-pass filter with cutoff frequency adaptation based on pitch lag statistics + /// + /// I/O Encoder states + static void silk_HP_variable_cutoff(SilkChannelEncoder[] state_Fxx) { + int quality_Q15; + int pitch_freq_Hz_Q16, pitch_freq_log_Q7, delta_freq_Q7; + SilkChannelEncoder psEncC1 = state_Fxx[0]; + + /* Adaptive cutoff frequency: estimate low end of pitch frequency range */ + if (psEncC1.prevSignalType == SilkConstants.TYPE_VOICED) { + /* difference, in log domain */ + pitch_freq_Hz_Q16 = Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(Inlines.silk_MUL(psEncC1.fs_kHz, 1000), 16), psEncC1.prevLag); + pitch_freq_log_Q7 = Inlines.silk_lin2log(pitch_freq_Hz_Q16) - (16 << 7); + + /* adjustment based on quality */ + quality_Q15 = psEncC1.input_quality_bands_Q15[0]; + pitch_freq_log_Q7 = Inlines.silk_SMLAWB(pitch_freq_log_Q7, Inlines.silk_SMULWB(Inlines.silk_LSHIFT(-quality_Q15, 2), quality_Q15), + pitch_freq_log_Q7 - (Inlines.silk_lin2log(((int) ((TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ, 16)*/) - (16 << 7))); + + /* delta_freq = pitch_freq_log - psEnc.variable_HP_smth1; */ + delta_freq_Q7 = pitch_freq_log_Q7 - Inlines.silk_RSHIFT(psEncC1.variable_HP_smth1_Q15, 8); + if (delta_freq_Q7 < 0) { + /* less smoothing for decreasing pitch frequency, to track something close to the minimum */ + delta_freq_Q7 = Inlines.silk_MUL(delta_freq_Q7, 3); + } + + /* limit delta, to reduce impact of outliers in pitch estimation */ + delta_freq_Q7 = Inlines.silk_LIMIT_32( + delta_freq_Q7, + 0 - ((int) ((TuningParameters.VARIABLE_HP_MAX_DELTA_FREQ) * ((long) 1 << (7)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_MAX_DELTA_FREQ, 7)*/, + ((int) ((TuningParameters.VARIABLE_HP_MAX_DELTA_FREQ) * ((long) 1 << (7)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_MAX_DELTA_FREQ, 7)*/); + + /* update smoother */ + psEncC1.variable_HP_smth1_Q15 = Inlines.silk_SMLAWB(psEncC1.variable_HP_smth1_Q15, + Inlines.silk_SMULBB(psEncC1.speech_activity_Q8, delta_freq_Q7), ((int) ((TuningParameters.VARIABLE_HP_SMTH_COEF1) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_SMTH_COEF1, 16)*/); + + /* limit frequency range */ + psEncC1.variable_HP_smth1_Q15 = Inlines.silk_LIMIT_32(psEncC1.variable_HP_smth1_Q15, + Inlines.silk_LSHIFT(Inlines.silk_lin2log(TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ), 8), + Inlines.silk_LSHIFT(Inlines.silk_lin2log(TuningParameters.VARIABLE_HP_MAX_CUTOFF_HZ), 8)); + } + } +} diff --git a/app/src/main/java/org/concentus/Inlines.java b/app/src/main/java/org/concentus/Inlines.java new file mode 100644 index 0000000000..559dd818a9 --- /dev/null +++ b/app/src/main/java/org/concentus/Inlines.java @@ -0,0 +1,1781 @@ +/* Copyright (c) 2006-2011 Skype Limited. + Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class Inlines { + + static void OpusAssert(boolean condition) { + if (!condition) { + throw new AssertionError(); + } + //Debug.Assert(condition); + } + + static void OpusAssert(boolean condition, String message) { + if (!condition) { + throw new AssertionError(message); + } + //Debug.Assert(condition, message); + } + + static long CapToUInt32(long val) { + return (0xFFFFFFFFL & (int) val); + } + + static long CapToUInt32(int val) { + return val; + } + + // CELT-SPECIFIC INLINES + // /** Multiply a 16-bit signed value by a 16-bit unsigned value. The result is a 32-bit signed value */ + //#define MULT16_16SU(a,b) ((opus_val32)(opus_val16)(a)*(opus_val32)(opus_uint16)(b)) + static int MULT16_16SU(int a, int b) { + return ((int) (short) (a) * (int) (b & 0xFFFF)); + } + + // /** 16x32 multiplication, followed by a 16-bit shift right. Results fits in 32 bits */ + //#define MULT16_32_Q16(a,b) ADD32(MULT16_16((a),SHR((b),16)), SHR(MULT16_16SU((a),((b)&0x0000ffff)),16)) + static int MULT16_32_Q16(short a, int b) { + return ADD32(MULT16_16((a), SHR((b), 16)), SHR(MULT16_16SU((a), ((b) & 0x0000ffff)), 16)); + } + + static int MULT16_32_Q16(int a, int b) { + return ADD32(MULT16_16((a), SHR((b), 16)), SHR(MULT16_16SU((a), ((b) & 0x0000ffff)), 16)); + } + + // /** 16x32 multiplication, followed by a 16-bit shift right (round-to-nearest). Results fits in 32 bits */ + //#define MULT16_32_P16(a,b) ADD32(MULT16_16((a),SHR((b),16)), PSHR(MULT16_16SU((a),((b)&0x0000ffff)),16)) + static int MULT16_32_P16(short a, int b) { + return ADD32(MULT16_16((a), SHR((b), 16)), PSHR(MULT16_16SU((a), ((b) & 0x0000ffff)), 16)); + } + + static int MULT16_32_P16(int a, int b) { + return ADD32(MULT16_16((a), SHR((b), 16)), PSHR(MULT16_16SU((a), ((b) & 0x0000ffff)), 16)); + } + + // /** 16x32 multiplication, followed by a 15-bit shift right. Results fits in 32 bits */ + static int MULT16_32_Q15(short a, int b) { + return ((a * (b >> 16)) << 1) + ((a * (b & 0xFFFF)) >> 15); + //return ADD32(SHL(MULT16_16((a), SHR((b), 16)), 1), SHR(MULT16_16SU((a), (ushort)((b) & 0x0000ffff)), 15)); + } + + static int MULT16_32_Q15(int a, int b) { + return ((a * (b >> 16)) << 1) + ((a * (b & 0xFFFF)) >> 15); + //return ADD32(SHL(MULT16_16((a), SHR((b), 16)), 1), SHR(MULT16_16SU((a), (uint)((b) & 0x0000ffff)), 15)); + } + + // /** 32x32 multiplication, followed by a 31-bit shift right. Results fits in 32 bits */ + //#define MULT32_32_Q31(a,b) ADD32(ADD32(SHL(MULT16_16(SHR((a),16),SHR((b),16)),1), SHR(MULT16_16SU(SHR((a),16),((b)&0x0000ffff)),15)), SHR(MULT16_16SU(SHR((b),16),((a)&0x0000ffff)),15)) + static int MULT32_32_Q31(int a, int b) { + return ADD32(ADD32(SHL(MULT16_16(SHR((a), 16), SHR((b), 16)), 1), SHR(MULT16_16SU(SHR((a), 16), ((b) & 0x0000ffff)), 15)), SHR(MULT16_16SU(SHR((b), 16), ((a) & 0x0000ffff)), 15)); + } + + // "Compile-time" (not really) conversion of float constant to 16-bit value + static short QCONST16(float x, int bits) { + return ((short) (0.5 + (x) * (((int) 1) << (bits)))); + } + + // "Compile-time" (not really) conversion of float constant to 32-bit value + static int QCONST32(float x, int bits) { + return ((int) (0.5 + (x) * (((int) 1) << (bits)))); + } + + // /** Negate a 16-bit value */ + static short NEG16(short x) { + return (short) (0 - x); + } + + static int NEG16(int x) { + return 0 - x; + } + + // /** Negate a 32-bit value */ + static int NEG32(int x) { + return 0 - x; + } + + // /** Change a 32-bit value into a 16-bit value. The value is assumed to fit in 16-bit, otherwise the result is undefined */ + static short EXTRACT16(int x) { + return ((short) x); + } + + // /** Change a 16-bit value into a 32-bit value */ + static int EXTEND32(short x) { + return (int) x; + } + + static int EXTEND32(int x) { + return x; + } + + // /** Arithmetic shift-right of a 16-bit value */ + static short SHR16(short a, int shift) { + return (short) ((a) >> (shift)); + } + + static int SHR16(int a, int shift) { + return ((a) >> (shift)); + } + + // /** Arithmetic shift-left of a 16-bit value */ + static short SHL16(short a, int shift) { + return ((short) ((a & 0xFFFF) << shift)); + } + + static int SHL16(int a, int shift) { + return (int) (0xFFFFFFFF & ((long) a << shift)); + } + + // /** Arithmetic shift-right of a 32-bit value */ + static int SHR32(int a, int shift) { + return a >> shift; + } + + // /** Arithmetic shift-left of a 32-bit value */ + static int SHL32(int a, int shift) { + return (int) (0xFFFFFFFF & ((long) a << shift)); + } + + // /** 32-bit arithmetic shift right with rounding-to-nearest instead of rounding down */ + static int PSHR32(int a, int shift) { + return (SHR32((a) + ((EXTEND32(1) << ((shift)) >> 1)), shift)); + } + + static short PSHR16(short a, int shift) { + return SHR16((short) (a + (1 << (shift) >> 1)), shift); + } + + static int PSHR16(int a, int shift) { + return SHR32((a + (1 << (shift) >> 1)), shift); + } + + // /** 32-bit arithmetic shift right where the argument can be negative */ + static int VSHR32(int a, int shift) { + return (((shift) > 0) ? SHR32(a, shift) : SHL32(a, -(shift))); + } + + // /** "RAW" macros, should not be used outside of this header file */ + private static int SHR(int a, int shift) { + return ((a) >> (shift)); + } + + private static int SHL(int a, int shift) { + return SHL32(a, shift); + } + + private static int SHR(short a, int shift) { + return ((a) >> (shift)); + } + + private static int SHL(short a, int shift) { + return SHL32(a, shift); + } + + private static int PSHR(int a, int shift) { + return (SHR((a) + ((EXTEND32(1) << ((shift)) >> 1)), shift)); + } + + static int SATURATE(int x, int a) { + return (((x) > (a) ? (a) : (x) < -(a) ? -(a) : (x))); + } + + static short SATURATE16(int x) { + return (EXTRACT16((x) > 32767 ? 32767 : (x) < -32768 ? -32768 : (x))); + } + + // /** Shift by a and round-to-neareast 32-bit value. Result is a 16-bit value */ + static short ROUND16(short x, short a) { + return (EXTRACT16(PSHR32((x), (a)))); + } + + static int ROUND16(int x, int a) { + return PSHR32((x), (a)); + } + + static int PDIV32(int a, int b) { + return a / b; + } + + // /** Divide by two */ + // fixme: can this be optimized? + static short HALF16(short x) { + return (SHR16(x, 1)); + } + + static int HALF16(int x) { + return (SHR32(x, 1)); + } + + static int HALF32(int x) { + return (SHR32(x, 1)); + } + + // /** Add two 16-bit values */ + static short ADD16(short a, short b) { + return ((short) ((short) (a) + (short) (b))); + } + + static int ADD16(int a, int b) { + return (a + b); + } + + // /** Subtract two 16-bit values */ + static short SUB16(short a, short b) { + return ((short) ((short) (a) - (short) (b))); + } + + static int SUB16(int a, int b) { + return (a - b); + } + + // /** Add two 32-bit values */ + static int ADD32(int a, int b) { + return ((int) (a) + (int) (b)); + } + + // /** Subtract two 32-bit values */ + static int SUB32(int a, int b) { + return ((int) (a) - (int) (b)); + } + + // /** 16x16 multiplication where the result fits in 16 bits */ + //#define MULT16_16_16(a,b) ((((opus_val16)(a))*((opus_val16)(b)))) + static short MULT16_16_16(short a, short b) { + return (short) (((((short) (a)) * ((short) (b))))); + } + + static int MULT16_16_16(int a, int b) { + return (a * b); + } + + // /* (opus_val32)(opus_val16) gives TI compiler a hint that it's 16x16->32 multiply */ + // /** 16x16 multiplication where the result fits in 32 bits */ + //#define MULT16_16(a,b) (((opus_val32)(opus_val16)(a))*((opus_val32)(opus_val16)(b))) + static int MULT16_16(int a, int b) { + return a * b; + } + + static int MULT16_16(short a, short b) { + return a * b; + } + + // /** 16x16 multiply-add where the result fits in 32 bits */ + //#define MAC16_16(c,a,b) (ADD32((c),MULT16_16((a),(b)))) + static int MAC16_16(short c, short a, short b) { + return c + (a * b); + } + + static int MAC16_16(int c, short a, short b) { + return c + (a * b); + } + + static int MAC16_16(int c, int a, int b) { + return c + (a * b); + } + + // /** 16x32 multiply, followed by a 15-bit shift right and 32-bit add. + // b must fit in 31 bits. + // Result fits in 32 bits. */ + //#define MAC16_32_Q15(c,a,b) ADD32((c),ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))) + static int MAC16_32_Q15(int c, short a, short b) { + return ADD32((c), ADD32(MULT16_16((a), SHR((b), 15)), SHR(MULT16_16((a), ((b) & 0x00007fff)), 15))); + } + + static int MAC16_32_Q15(int c, int a, int b) { + return ADD32((c), ADD32(MULT16_16((a), SHR((b), 15)), SHR(MULT16_16((a), ((b) & 0x00007fff)), 15))); + } + + // /** 16x32 multiplication, followed by a 16-bit shift right and 32-bit add. + // Results fits in 32 bits */ + //#define MAC16_32_Q16(c,a,b) ADD32((c),ADD32(MULT16_16((a),SHR((b),16)), SHR(MULT16_16SU((a),((b)&0x0000ffff)),16))) + static int MAC16_32_Q16(int c, short a, short b) { + return ADD32((c), ADD32(MULT16_16((a), SHR((b), 16)), SHR(MULT16_16SU((a), ((b) & 0x0000ffff)), 16))); + } + + static int MAC16_32_Q16(int c, int a, int b) { + return ADD32((c), ADD32(MULT16_16((a), SHR((b), 16)), SHR(MULT16_16SU((a), ((b) & 0x0000ffff)), 16))); + } + + //#define MULT16_16_Q11_32(a,b) (SHR(MULT16_16((a),(b)),11)) + static int MULT16_16_Q11_32(short a, short b) { + return (SHR(MULT16_16((a), (b)), 11)); + } + + static int MULT16_16_Q11_32(int a, int b) { + return (SHR(MULT16_16((a), (b)), 11)); + } + + //#define MULT16_16_Q11(a,b) (SHR(MULT16_16((a),(b)),11)) + static short MULT16_16_Q11(short a, short b) { + return (short) ((SHR(MULT16_16((a), (b)), 11))); + } + + static int MULT16_16_Q11(int a, int b) { + return (SHR(MULT16_16((a), (b)), 11)); + } + + //#define MULT16_16_Q13(a,b) (SHR(MULT16_16((a),(b)),13)) + static short MULT16_16_Q13(short a, short b) { + return (short) ((SHR(MULT16_16((a), (b)), 13))); + } + + static int MULT16_16_Q13(int a, int b) { + return (SHR(MULT16_16((a), (b)), 13)); + } + + //#define MULT16_16_Q14(a,b) (SHR(MULT16_16((a),(b)),14)) + static short MULT16_16_Q14(short a, short b) { + return (short) ((SHR(MULT16_16((a), (b)), 14))); + } + + static int MULT16_16_Q14(int a, int b) { + return (SHR(MULT16_16((a), (b)), 14)); + } + + //#define MULT16_16_Q15(a,b) (SHR(MULT16_16((a),(b)),15)) + static short MULT16_16_Q15(short a, short b) { + return (short) ((SHR(MULT16_16((a), (b)), 15))); + } + + static int MULT16_16_Q15(int a, int b) { + return (SHR(MULT16_16((a), (b)), 15)); + } + + //#define MULT16_16_P13(a,b) (SHR(ADD32(4096,MULT16_16((a),(b))),13)) + static short MULT16_16_P13(short a, short b) { + return (short) ((SHR(ADD32(4096, MULT16_16((a), (b))), 13))); + } + + static int MULT16_16_P13(int a, int b) { + return (SHR(ADD32(4096, MULT16_16((a), (b))), 13)); + } + + //#define MULT16_16_P14(a,b) (SHR(ADD32(8192,MULT16_16((a),(b))),14)) + static short MULT16_16_P14(short a, short b) { + return (short) ((SHR(ADD32(8192, MULT16_16((a), (b))), 14))); + } + + static int MULT16_16_P14(int a, int b) { + return (SHR(ADD32(8192, MULT16_16((a), (b))), 14)); + } + + //#define MULT16_16_P15(a,b) (SHR(ADD32(16384,MULT16_16((a),(b))),15)) + static short MULT16_16_P15(short a, short b) { + return (short) ((SHR(ADD32(16384, MULT16_16((a), (b))), 15))); + } + + static int MULT16_16_P15(int a, int b) { + return (SHR(ADD32(16384, MULT16_16((a), (b))), 15)); + } + + // /** Divide a 32-bit value by a 16-bit value. Result fits in 16 bits */ + //#define DIV32_16(a,b) ((opus_val16)(((opus_val32)(a))/((opus_val16)(b)))) + static short DIV32_16(int a, short b) { + return (short) (((short) (((int) (a)) / ((short) (b))))); + } + + static int DIV32_16(int a, int b) { + return a / b; + } + + // /** Divide a 32-bit value by a 32-bit value. Result fits in 32 bits */ + //#define DIV32(a,b) (((opus_val32)(a))/((opus_val32)(b))) + static int DIV32(int a, int b) { + return a / b; + } + + // identical to silk_SAT16 - saturate operation + static short SAT16(int x) { + return (short) (x > 32767 ? 32767 : x < -32768 ? -32768 : (short) x); + } + + static short SIG2WORD16(int x) { + x = PSHR32(x, 12); + x = MAX32(x, -32768); + x = MIN32(x, 32767); + return EXTRACT16(x); + } + + static short MIN(short a, short b) { + return ((a) < (b) ? (a) : (b)); + } + + static short MAX(short a, short b) { + return ((a) > (b) ? (a) : (b)); + } + + static short MIN16(short a, short b) { + return ((a) < (b) ? (a) : (b)); + } + + static short MAX16(short a, short b) { + return ((a) > (b) ? (a) : (b)); + } + + static int MIN16(int a, int b) { + return ((a) < (b) ? (a) : (b)); + } + + static int MAX16(int a, int b) { + return ((a) > (b) ? (a) : (b)); + } + + static float MIN16(float a, float b) { + return ((a) < (b) ? (a) : (b)); + } + + static float MAX16(float a, float b) { + return ((a) > (b) ? (a) : (b)); + } + + static int MIN(int a, int b) { + return ((a) < (b) ? (a) : (b)); + } + + static int MAX(int a, int b) { + return ((a) > (b) ? (a) : (b)); + } + + static int IMIN(int a, int b) { + return ((a) < (b) ? (a) : (b)); + } + + static long IMIN(long a, long b) { + return ((a) < (b) ? (a) : (b)); + } + + static int IMAX(int a, int b) { + return ((a) > (b) ? (a) : (b)); + } + + static int MIN32(int a, int b) { + return ((a) < (b) ? (a) : (b)); + } + + static int MAX32(int a, int b) { + return ((a) > (b) ? (a) : (b)); + } + + static float MIN32(float a, float b) { + return ((a) < (b) ? (a) : (b)); + } + + static float MAX32(float a, float b) { + return ((a) > (b) ? (a) : (b)); + } + + static int ABS16(int x) { + return ((x) < 0 ? (-(x)) : (x)); + } + + static float ABS16(float x) { + return ((x) < 0 ? (-(x)) : (x)); + } + + static short ABS16(short x) { + return (short) (((x) < 0 ? (-(x)) : (x))); + } + + static int ABS32(int x) { + return ((x) < 0 ? (-(x)) : (x)); + } + + static int celt_udiv(int n, int d) { + Inlines.OpusAssert(d > 0); + return n / d; + } + + static int celt_sudiv(int n, int d) { + Inlines.OpusAssert(d > 0); + return n / d; + } + + //#define celt_div(a,b) MULT32_32_Q31((opus_val32)(a),celt_rcp(b)) + static int celt_div(int a, int b) { + return MULT32_32_Q31((int) (a), celt_rcp(b)); + } + + /** + * Integer log in base2. Undefined for zero and negative numbers + */ + static int celt_ilog2(int x) { + Inlines.OpusAssert(x > 0, "celt_ilog2() only defined for strictly positive numbers"); + return (EC_ILOG((long) x) - 1); + } + + /** + * Integer log in base2. Defined for zero, but not for negative numbers + */ + static int celt_zlog2(int x) { + return x <= 0 ? 0 : celt_ilog2(x); + } + + static int celt_maxabs16(int[] x, int x_ptr, int len) { + int i; + int maxval = 0; + int minval = 0; + for (i = x_ptr; i < len + x_ptr; i++) { + maxval = MAX32(maxval, x[i]); + minval = MIN32(minval, x[i]); + } + return MAX32(EXTEND32(maxval), -EXTEND32(minval)); + } + + static int celt_maxabs32(int[] x, int x_ptr, int len) { + int i; + int maxval = 0; + int minval = 0; + for (i = x_ptr; i < x_ptr + len; i++) { + maxval = MAX32(maxval, x[i]); + minval = MIN32(minval, x[i]); + } + return MAX32(maxval, 0 - minval); + } + + static short celt_maxabs32(short[] x, int x_ptr, int len) { + int i; + short maxval = 0; + short minval = 0; + for (i = x_ptr; i < x_ptr + len; i++) { + maxval = MAX16(maxval, x[i]); + minval = MIN16(minval, x[i]); + } + return MAX(maxval, (short) (0 - minval)); + } + + /// + /// Multiplies two 16-bit fractional values. Bit-exactness of this macro is important + /// + /// + /// + /// + static int FRAC_MUL16(int a, int b) { + return ((16384 + ((int) ((short) a * (short) b))) >> 15); + } + + /// + /// Compute floor(sqrt(_val)) with exact arithmetic. + /// This has been tested on all possible 32-bit inputs. + /// + /// + /// + static int isqrt32(long _val) { + int b; + int g; + int bshift; + /*Uses the second method from + http://www.azillionmonkeys.com/qed/sqroot.html + The main idea is to search for the largest binary digit b such that + (g+b)*(g+b) <= _val, and add it to the solution g.*/ + g = 0; + bshift = (EC_ILOG(_val) - 1) >> 1; + b = 1 << bshift; + do { + long t; + t = ((g << 1) + b) << bshift; + if (t <= _val) { + g += b; + _val -= t; + } + b >>= 1; + bshift--; + } while (bshift >= 0); + return g; + } + + private static short[] sqrt_C = {23175, 11561, -3011, 1699, -664}; + + /** + * Sqrt approximation (QX input, QX/2 output) + */ + static int celt_sqrt(int x) { + int k; + short n; + int rt; + + if (x == 0) { + return 0; + } else if (x >= 1073741824) { + return 32767; + } + k = (celt_ilog2(x) >> 1) - 7; + x = VSHR32(x, 2 * k); + n = (short) (x - 32768); + rt = ADD16(sqrt_C[0], MULT16_16_Q15(n, ADD16(sqrt_C[1], MULT16_16_Q15(n, ADD16(sqrt_C[2], + MULT16_16_Q15(n, ADD16(sqrt_C[3], MULT16_16_Q15(n, (sqrt_C[4]))))))))); + rt = VSHR32(rt, 7 - k); + return rt; + } + + /** + * Reciprocal approximation (Q15 input, Q16 output) + */ + static int celt_rcp(int x) { + int i; + int n; + int r; + Inlines.OpusAssert(x > 0, "celt_rcp() only defined for positive values"); + i = celt_ilog2(x); + /* n is Q15 with range [0,1). */ + n = VSHR32(x, i - 15) - 32768; + /* Start with a linear approximation: + r = 1.8823529411764706-0.9411764705882353*n. + The coefficients and the result are Q14 in the range [15420,30840].*/ + r = ADD16(30840, MULT16_16_Q15(-15420, n)); + /* Perform two Newton iterations: + r -= r*((r*n)-1.Q15) + = r*((r*n)+(r-1.Q15)). */ + r = SUB16(r, MULT16_16_Q15(r, + ADD16(MULT16_16_Q15(r, n), ADD16(r, -32768)))); + /* We subtract an extra 1 in the second iteration to avoid overflow; it also + neatly compensates for truncation error in the rest of the process. */ + r = SUB16(r, ADD16(1, MULT16_16_Q15(r, + ADD16(MULT16_16_Q15(r, n), ADD16(r, -32768))))); + /* r is now the Q15 solution to 2/(n+1), with a maximum relative error + of 7.05346E-5, a (relative) RMSE of 2.14418E-5, and a peak absolute + error of 1.24665/32768. */ + return VSHR32(EXTEND32(r), i - 16); + } + + /** + * Reciprocal sqrt approximation in the range [0.25,1) (Q16 in, Q14 out) + */ + static int celt_rsqrt_norm(int x) { + int n; + int r; + int r2; + int y; + /* Range of n is [-16384,32767] ([-0.5,1) in Q15). */ + n = x - 32768; + /* Get a rough initial guess for the root. + The optimal minimax quadratic approximation (using relative error) is + r = 1.437799046117536+n*(-0.823394375837328+n*0.4096419668459485). + Coefficients here, and the final result r, are Q14.*/ + r = ADD16(23557, MULT16_16_Q15(n, ADD16(-13490, MULT16_16_Q15(n, 6713)))); + /* We want y = x*r*r-1 in Q15, but x is 32-bit Q16 and r is Q14. + We can compute the result from n and r using Q15 multiplies with some + adjustment, carefully done to avoid overflow. + Range of y is [-1564,1594]. */ + r2 = MULT16_16_Q15(r, r); + y = SHL16(SUB16(ADD16(MULT16_16_Q15(r2, n), r2), 16384), 1); + /* Apply a 2nd-order Householder iteration: r += r*y*(y*0.375-0.5). + This yields the Q14 reciprocal square root of the Q16 x, with a maximum + relative error of 1.04956E-4, a (relative) RMSE of 2.80979E-5, and a + peak absolute error of 2.26591/16384. */ + return ADD16(r, MULT16_16_Q15(r, MULT16_16_Q15(y, + SUB16(MULT16_16_Q15(y, 12288), 16384)))); + } + + static int frac_div32(int a, int b) { + int rcp; + int result, rem; + int shift = celt_ilog2(b) - 29; + a = VSHR32(a, shift); + b = VSHR32(b, shift); + /* 16-bit reciprocal */ + rcp = ROUND16(celt_rcp(ROUND16(b, 16)), 3); + result = MULT16_32_Q15(rcp, a); + rem = PSHR32(a, 2) - MULT32_32_Q31(result, b); + result = ADD32(result, SHL32(MULT16_32_Q15(rcp, rem), 2)); + if (result >= 536870912) /* 2^29 */ { + return 2147483647; /* 2^31 - 1 */ + } else if (result <= -536870912) /* -2^29 */ { + return -2147483647; /* -2^31 */ + } else { + return SHL32(result, 2); + } + } + + private static short log2_C0 = -6801 + (1 << (3)); + + /** + * Base-2 logarithm approximation (log2(x)). (Q14 input, Q10 output) + */ + static int celt_log2(int x) { + int i; + int n, frac; + /* -0.41509302963303146, 0.9609890551383969, -0.31836011537636605, + 0.15530808010959576, -0.08556153059057618 */ + if (x == 0) { + return -32767; + } + i = celt_ilog2(x); + n = VSHR32(x, i - 15) - 32768 - 16384; + frac = ADD16(log2_C0, MULT16_16_Q15(n, ADD16(15746, MULT16_16_Q15(n, ADD16(-5217, MULT16_16_Q15(n, ADD16(2545, MULT16_16_Q15(n, -1401)))))))); + return SHL16((short) (i - 13), 10) + SHR16(frac, 4); + } + + static int celt_exp2_frac(int x) { + int frac; + frac = SHL16(x, 4); + return ADD16(16383, MULT16_16_Q15(frac, ADD16(22804, MULT16_16_Q15(frac, ADD16(14819, MULT16_16_Q15(10204, frac)))))); + } + + /** + * Base-2 exponential approximation (2^x). (Q10 input, Q16 output) + */ + static int celt_exp2(int x) { + int integer; + int frac; + integer = SHR16(x, 10); + if (integer > 14) { + return 0x7f000000; + } else if (integer < -15) { + return 0; + } + frac = (short) (celt_exp2_frac((short) (x - SHL16((short) (integer), 10)))); + return VSHR32(EXTEND32(frac), -integer - 2); + } + + /* Atan approximation using a 4th order polynomial. Input is in Q15 format + and normalized by pi/4. Output is in Q15 format */ + static int celt_atan01(int x) { + return MULT16_16_P15(x, ADD32(32767, MULT16_16_P15(x, ADD32(-21, MULT16_16_P15(x, ADD32(-11943, MULT16_16_P15(4936, x))))))); + } + + /* atan2() approximation valid for positive input values */ + static int celt_atan2p(int y, int x) { + if (y < x) { + int arg; + arg = celt_div(SHL32(EXTEND32(y), 15), x); + if (arg >= 32767) { + arg = 32767; + } + return SHR32(celt_atan01(EXTRACT16(arg)), 1); + } else { + int arg; + arg = celt_div(SHL32(EXTEND32(x), 15), y); + if (arg >= 32767) { + arg = 32767; + } + return 25736 - SHR16(celt_atan01(EXTRACT16(arg)), 1); + } + } + + static int celt_cos_norm(int x) { + x = x & 0x0001ffff; + if (x > SHL32(EXTEND32(1), 16)) { + x = SUB32(SHL32(EXTEND32(1), 17), x); + } + if ((x & 0x00007fff) != 0) { + if (x < SHL32(EXTEND32(1), 15)) { + return _celt_cos_pi_2(EXTRACT16(x)); + } else { + return NEG32(_celt_cos_pi_2(EXTRACT16(65536 - x))); // opus bug: should be neg32? + } + } else if ((x & 0x0000ffff) != 0) { + return 0; + } else if ((x & 0x0001ffff) != 0) { + return -32767; + } else { + return 32767; + } + } + + static int _celt_cos_pi_2(int x) { + int x2; + + x2 = MULT16_16_P15(x, x); + return ADD32(1, MIN32(32766, ADD32(SUB16(32767, x2), MULT16_16_P15(x2, ADD32(-7651, MULT16_16_P15(x2, ADD32(8277, MULT16_16_P15(-626, x2)))))))); + } + + static short FLOAT2INT16(float x) { + x = x * CeltConstants.CELT_SIG_SCALE; + if (x < Short.MIN_VALUE) { + x = Short.MIN_VALUE; + } + if (x > Short.MAX_VALUE) { + x = Short.MAX_VALUE; + } + return (short) x; + } + + // SILK-SPECIFIC INLINES + /// + /// Rotate a32 right by 'rot' bits. Negative rot values result in rotating + /// left. Output is 32bit int. + /// + /// + /// + /// + static int silk_ROR32(int a32, int rot) { + int m = (0 - rot); + if (rot == 0) { + return a32; + } else if (rot < 0) { + return ((a32 << m) | (a32 >> (32 - m))); + } else { + return ((a32 << (32 - rot)) | (a32 >> rot)); + } + } + + static int silk_MUL(int a32, int b32) { + int ret = a32 * b32; + return ret; + } + + static int silk_MLA(int a32, int b32, int c32) { + int ret = silk_ADD32((a32), ((b32) * (c32))); + Inlines.OpusAssert((long) ret == (long) a32 + (long) b32 * (long) c32); + return ret; + } + + /// + /// ((a32 >> 16) * (b32 >> 16)) + /// + /// + /// + /// + static int silk_SMULTT(int a32, int b32) { + return ((a32 >> 16) * (b32 >> 16)); + } + + static int silk_SMLATT(int a32, int b32, int c32) { + return silk_ADD32((a32), ((b32) >> 16) * ((c32) >> 16)); + } + + static long silk_SMLALBB(long a64, short b16, short c16) { + return silk_ADD64((a64), (long) ((int) (b16) * (int) (c16))); + } + + static long silk_SMULL(int a32, int b32) { + return (long) a32 * (long) b32; + } + + /// + /// Adds two signed 32-bit values in a way that can overflow, while not relying on undefined behaviour + /// (just standard two's complement implementation-specific behaviour) + /// + /// + /// + /// + static int silk_ADD32_ovflw(int a, int b) { + return (int) ((long) a + b); + } + + static int silk_ADD32_ovflw(long a, long b) { + return ((int) (a + b)); + } + + /// + /// Subtracts two signed 32-bit values in a way that can overflow, while not relying on undefined behaviour + /// (just standard two's complement implementation-specific behaviour) + /// + /// + /// + /// + static int silk_SUB32_ovflw(int a, int b) { + return (int) ((long) a - b); + } + + /// + /// Multiply-accumulate macros that allow overflow in the addition (ie, no asserts in debug mode) + /// + /// + /// + /// + /// + static int silk_MLA_ovflw(int a32, int b32, int c32) { + return (int) silk_ADD32_ovflw((long) a32, (long) b32 * c32); + } + + static int silk_SMLABB_ovflw(int a32, int b32, int c32) { + return ((silk_ADD32_ovflw((a32), ((int) ((short) (b32))) * (int) ((short) (c32))))); + } + + static int silk_SMULBB(int a32, int b32) { + return ((int) ((short) a32) * (int) ((short) b32)); + } + + /// + /// (a32 * (int)((short)(b32))) >> 16 output have to be 32bit int + /// + /// + /// + /// + static int silk_SMULWB(int a32, int b32) { + return ((int) (((a32 * (long) (((short) b32))) >> 16))); + } + + static int silk_SMLABB(int a32, int b32, int c32) { + return ((a32) + ((int) ((short) b32)) * (int) ((short) c32)); + } + + static int silk_DIV32_16(int a32, int b32) { + return a32 / b32; + } + + static int silk_DIV32(int a32, int b32) { + return a32 / b32; + } + + static short silk_ADD16(short a, short b) { + short ret = (short) (a + b); + return ret; + } + + static int silk_ADD32(int a, int b) { + int ret = a + b; + return ret; + } + + static long silk_ADD64(long a, long b) { + long ret = a + b; + Inlines.OpusAssert(ret == silk_ADD_SAT64(a, b)); + return ret; + } + + static short silk_SUB16(short a, short b) { + short ret = (short) (a - b); + Inlines.OpusAssert(ret == silk_SUB_SAT16(a, b)); + return ret; + } + + static int silk_SUB32(int a, int b) { + int ret = a - b; + Inlines.OpusAssert(ret == silk_SUB_SAT32(a, b)); + return ret; + } + + static long silk_SUB64(long a, long b) { + long ret = a - b; + Inlines.OpusAssert(ret == silk_SUB_SAT64(a, b)); + return ret; + } + + static int silk_SAT8(int a) { + return a > Byte.MAX_VALUE ? Byte.MAX_VALUE : ((a) < Byte.MIN_VALUE ? Byte.MIN_VALUE : (a)); + } + + static int silk_SAT16(int a) { + return a > Short.MAX_VALUE ? Short.MAX_VALUE : ((a) < Short.MIN_VALUE ? Short.MIN_VALUE : (a)); + } + + static int silk_SAT32(long a) { + return a > Integer.MAX_VALUE ? Integer.MAX_VALUE : ((a) < Integer.MIN_VALUE ? Integer.MIN_VALUE : (int) (a)); + } + + /// + /// ////////////////// + /// + /// + /// + /// + static short silk_ADD_SAT16(short a16, short b16) { + short res = (short) silk_SAT16(silk_ADD32((int) (a16), (b16))); + Inlines.OpusAssert(res == silk_SAT16((int) a16 + (int) b16)); + return res; + } + + static int silk_ADD_SAT32(int a32, int b32) { + int res = ((((long) (a32) + (long) (b32)) & 0x80000000) == 0 + ? ((((a32) & (b32)) & 0x80000000) != 0 ? Integer.MIN_VALUE : (a32) + (b32)) + : ((((a32) | (b32)) & 0x80000000) == 0 ? Integer.MAX_VALUE : (a32) + (b32))); + Inlines.OpusAssert(res == silk_SAT32((long) a32 + (long) b32)); + return res; + } + + static long silk_ADD_SAT64(long a64, long b64) { + long res; + res = (((a64 + b64) & Long.MIN_VALUE) == 0 + ? (((a64 & b64) & Long.MIN_VALUE) != 0 ? Long.MIN_VALUE : a64 + b64) + : (((a64 | b64) & Long.MIN_VALUE) == 0 ? Long.MAX_VALUE : a64 + b64)); + return res; + } + + static short silk_SUB_SAT16(short a16, short b16) { + short res = (short) silk_SAT16(silk_SUB32((int) (a16), (b16))); + Inlines.OpusAssert(res == silk_SAT16((int) a16 - (int) b16)); + return res; + } + + static int silk_SUB_SAT32(int a32, int b32) { + int res = ((((long) a32 - b32) & 0x80000000) == 0 + ? (((a32) & ((b32) ^ 0x80000000) & 0x80000000) != 0 ? Integer.MIN_VALUE : (a32) - (b32)) + : ((((a32) ^ 0x80000000) & (b32) & 0x80000000) != 0 ? Integer.MAX_VALUE : (a32) - (b32))); + Inlines.OpusAssert(res == silk_SAT32((long) a32 - (long) b32)); + return res; + } + + static long silk_SUB_SAT64(long a64, long b64) { + long res; + res = (((long) ((a64) - (b64)) & Long.MIN_VALUE) == 0 + ? (((long) (a64) & ((long) (b64) ^ Long.MIN_VALUE) & Long.MIN_VALUE) != 0 ? Long.MIN_VALUE : (a64) - (b64)) + : ((((long) (a64) ^ Long.MIN_VALUE) & (long) (b64) & Long.MIN_VALUE) != 0 ? Long.MAX_VALUE : (a64) - (b64))); + return res; + } + + ///* Saturation for positive input values */ + //#define silk_POS_SAT32(a) ((a) > int_MAX ? int_MAX : (a)) + /// + /// Add with saturation for positive input values + /// + /// + /// + /// + static byte silk_ADD_POS_SAT8(byte a, byte b) { + return (byte) ((((a + b) & 0x80) != 0) ? Byte.MAX_VALUE : (a + b)); + } + + /// + /// Add with saturation for positive input values + /// + /// + /// + /// + static short silk_ADD_POS_SAT16(short a, short b) { + return (short) ((((a + b) & 0x8000) != 0) ? Short.MAX_VALUE : (a + b)); + } + + /// + /// Add with saturation for positive input values + /// + /// + /// + /// + static int silk_ADD_POS_SAT32(int a, int b) { + return ((((a + b) & 0x80000000) != 0) ? Integer.MAX_VALUE : (a + b)); + } + + /// + /// Add with saturation for positive input values + /// + /// + /// + /// + static long silk_ADD_POS_SAT64(long a, long b) { + return ((((long) (a + b) & Long.MIN_VALUE) != 0) ? Long.MAX_VALUE : (a + b)); + } + + static byte silk_LSHIFT8(byte a, int shift) { + byte ret = (byte) (a << shift); + return ret; + } + + static short silk_LSHIFT16(short a, int shift) { + short ret = (short) (a << shift); + return ret; + } + + static int silk_LSHIFT32(int a, int shift) { + int ret = a << shift; + return ret; + } + + static long silk_LSHIFT64(long a, int shift) { + long ret = a << shift; + return ret; + } + + static int silk_LSHIFT(int a, int shift) { + int ret = a << shift; + return ret; + } + + static int silk_LSHIFT_ovflw(int a, int shift) { + return a << shift; + } + + /// + /// saturates before shifting + /// + /// + /// + /// + static int silk_LSHIFT_SAT32(int a, int shift) { + return (silk_LSHIFT32(silk_LIMIT((a), silk_RSHIFT32(Integer.MIN_VALUE, (shift)), silk_RSHIFT32(Integer.MAX_VALUE, (shift))), (shift))); + } + + static byte silk_RSHIFT8(byte a, int shift) { + return (byte) (a >> shift); + } + + static short silk_RSHIFT16(short a, int shift) { + return (short) (a >> shift); + } + + static int silk_RSHIFT32(int a, int shift) { + return a >> shift; + } + + static int silk_RSHIFT(int a, int shift) { + return a >> shift; + } + + static long silk_RSHIFT64(long a, int shift) { + return a >> shift; + } + + static long silk_RSHIFT_uint(long a, int shift) { + return CapToUInt32(a) >> shift; + } + + static int silk_ADD_LSHIFT(int a, int b, int shift) { + int ret = a + (b << shift); + return ret; + /* shift >= 0 */ + } + + static int silk_ADD_LSHIFT32(int a, int b, int shift) { + int ret = a + (b << shift); + return ret; + /* shift >= 0 */ + } + + static int silk_ADD_RSHIFT(int a, int b, int shift) { + int ret = a + (b >> shift); + return ret; + /* shift > 0 */ + } + + static int silk_ADD_RSHIFT32(int a, int b, int shift) { + int ret = a + (b >> shift); + return ret; + /* shift > 0 */ + } + + static long silk_ADD_RSHIFT_uint(long a, long b, int shift) { + long ret = CapToUInt32(a + (CapToUInt32(b) >> shift)); + return ret; + /* shift > 0 */ + } + + static int silk_SUB_LSHIFT32(int a, int b, int shift) { + int ret; + ret = a - (b << shift); + return ret; + /* shift >= 0 */ + } + + static int silk_SUB_RSHIFT32(int a, int b, int shift) { + int ret; + ret = a - (b >> shift); + return ret; + /* shift > 0 */ + } + + static int silk_RSHIFT_ROUND(int a, int shift) { + int ret; + ret = shift == 1 ? (a >> 1) + (a & 1) : ((a >> (shift - 1)) + 1) >> 1; + return ret; + } + + static long silk_RSHIFT_ROUND64(long a, int shift) { + long ret; + ret = shift == 1 ? (a >> 1) + (a & 1) : ((a >> (shift - 1)) + 1) >> 1; + return ret; + } + + static int silk_min(int a, int b) { + return ((a) < (b)) ? (a) : (b); + } + + static int silk_max(int a, int b) { + return ((a) > (b)) ? (a) : (b); + } + + static float silk_min(float a, float b) { + return ((a) < (b)) ? (a) : (b); + } + + static float silk_max(float a, float b) { + return ((a) > (b)) ? (a) : (b); + } + + /// + /// Macro to convert floating-point constants to fixed-point by applying a scalar factor + /// Because of limitations of the JIT, this macro is actually evaluated at runtime and therefore should not be used if you want to maximize performance + /// + static int SILK_CONST(float number, int scale) { + return ((int) ((number) * ((long) 1 << (scale)) + 0.5)); + } + + /* silk_min() versions with typecast in the function call */ + static int silk_min_int(int a, int b) { + return (((a) < (b)) ? (a) : (b)); + } + + static short silk_min_16(short a, short b) { + return (((a) < (b)) ? (a) : (b)); + } + + static int silk_min_32(int a, int b) { + return (((a) < (b)) ? (a) : (b)); + } + + static long silk_min_64(long a, long b) { + return (((a) < (b)) ? (a) : (b)); + } + + /* silk_min() versions with typecast in the function call */ + static int silk_max_int(int a, int b) { + return (((a) > (b)) ? (a) : (b)); + } + + static short silk_max_16(short a, short b) { + return (((a) > (b)) ? (a) : (b)); + } + + static int silk_max_32(int a, int b) { + return (((a) > (b)) ? (a) : (b)); + } + + static long silk_max_64(long a, long b) { + return (((a) > (b)) ? (a) : (b)); + } + + static float silk_LIMIT(float a, float limit1, float limit2) { + return ((limit1) > (limit2) ? ((a) > (limit1) ? (limit1) : ((a) < (limit2) ? (limit2) : (a))) : ((a) > (limit2) ? (limit2) : ((a) < (limit1) ? (limit1) : (a)))); + } + + static int silk_LIMIT(int a, int limit1, int limit2) { + return silk_LIMIT_32(a, limit1, limit2); + } + + static int silk_LIMIT_int(int a, int limit1, int limit2) { + return silk_LIMIT_32(a, limit1, limit2); + } + + static short silk_LIMIT_16(short a, short limit1, short limit2) { + return ((limit1) > (limit2) ? ((a) > (limit1) ? (limit1) : ((a) < (limit2) ? (limit2) : (a))) : ((a) > (limit2) ? (limit2) : ((a) < (limit1) ? (limit1) : (a)))); + } + + static int silk_LIMIT_32(int a, int limit1, int limit2) { + return ((limit1) > (limit2) ? ((a) > (limit1) ? (limit1) : ((a) < (limit2) ? (limit2) : (a))) : ((a) > (limit2) ? (limit2) : ((a) < (limit1) ? (limit1) : (a)))); + } + + static int silk_abs(int a) { + // Be careful, silk_abs returns wrong when input equals to silk_intXX_MIN + return ((a) > 0) ? (a) : -(a); + } + + static int silk_abs_int16(int a) { + return (a ^ (a >> 15)) - (a >> 15); + } + + static int silk_abs_int32(int a) { + return (a ^ (a >> 31)) - (a >> 31); + } + + static long silk_abs_int64(long a) { + return ((a) > 0) ? (a) : -(a); + } + + static long silk_sign(int a) { + return (a) > 0 ? 1 : ((a) < 0 ? -1 : 0); + } + + /// + /// PSEUDO-RANDOM GENERATOR + /// Make sure to store the result as the seed for the next call (also in between + /// frames), otherwise result won't be random at all. When only using some of the + /// bits, take the most significant bits by right-shifting. + /// + static int silk_RAND(int seed) { + return silk_MLA_ovflw(907633515, seed, 196314165); + } + + /// + /// silk_SMMUL: Signed top word multiply. + /// + /// + /// + /// + static int silk_SMMUL(int a32, int b32) { + return (int) silk_RSHIFT64(silk_SMULL((a32), (b32)), 32); + } + + /* a32 + (b32 * (c32 >> 16)) >> 16 */ + static int silk_SMLAWT(int a32, int b32, int c32) { + int ret = a32 + ((b32 >> 16) * (c32 >> 16)) + (((b32 & 0x0000FFFF) * ((c32 >> 16)) >> 16)); + return ret; + } + + /// + /// Divide two int32 values and return result as int32 in a given Q-domain + /// + /// I numerator (Q0) + /// I denominator (Q0) + /// I Q-domain of result (>= 0) + /// O returns a good approximation of "(a32 << Qres) / b32" + static int silk_DIV32_varQ(int a32, int b32, int Qres) { + int a_headrm, b_headrm, lshift; + int b32_inv, a32_nrm, b32_nrm, result; + + Inlines.OpusAssert(b32 != 0); + Inlines.OpusAssert(Qres >= 0); + + /* Compute number of bits head room and normalize inputs */ + a_headrm = silk_CLZ32(silk_abs(a32)) - 1; + a32_nrm = silk_LSHIFT(a32, a_headrm); + /* Q: a_headrm */ + b_headrm = silk_CLZ32(silk_abs(b32)) - 1; + b32_nrm = silk_LSHIFT(b32, b_headrm); + /* Q: b_headrm */ + + /* Inverse of b32, with 14 bits of precision */ + b32_inv = silk_DIV32_16(Integer.MAX_VALUE >> 2, silk_RSHIFT(b32_nrm, 16)); + /* Q: 29 + 16 - b_headrm */ + + /* First approximation */ + result = silk_SMULWB(a32_nrm, b32_inv); + /* Q: 29 + a_headrm - b_headrm */ + + /* Compute residual by subtracting product of denominator and first approximation */ + /* It's OK to overflow because the final value of a32_nrm should always be small */ + a32_nrm = silk_SUB32_ovflw(a32_nrm, silk_LSHIFT_ovflw(silk_SMMUL(b32_nrm, result), 3)); + /* Q: a_headrm */ + + /* Refinement */ + result = silk_SMLAWB(result, a32_nrm, b32_inv); + /* Q: 29 + a_headrm - b_headrm */ + + /* Convert to Qres domain */ + lshift = 29 + a_headrm - b_headrm - Qres; + if (lshift < 0) { + return silk_LSHIFT_SAT32(result, -lshift); + } else if (lshift < 32) { + return silk_RSHIFT(result, lshift); + } else { + /* Avoid undefined result */ + return 0; + } + } + + /// + /// Invert int32 value and return result as int32 in a given Q-domain + /// + /// I denominator (Q0) + /// I Q-domain of result (> 0) + /// a good approximation of "(1 << Qres) / b32" + static int silk_INVERSE32_varQ(int b32, int Qres) { + int b_headrm, lshift; + int b32_inv, b32_nrm, err_Q32, result; + + Inlines.OpusAssert(b32 != 0); + Inlines.OpusAssert(Qres > 0); + + /* Compute number of bits head room and normalize input */ + b_headrm = silk_CLZ32(silk_abs(b32)) - 1; + b32_nrm = silk_LSHIFT(b32, b_headrm); + /* Q: b_headrm */ + + /* Inverse of b32, with 14 bits of precision */ + b32_inv = silk_DIV32_16(Integer.MAX_VALUE >> 2, (short) (silk_RSHIFT(b32_nrm, 16))); + /* Q: 29 + 16 - b_headrm */ + + /* First approximation */ + result = silk_LSHIFT(b32_inv, 16); + /* Q: 61 - b_headrm */ + + /* Compute residual by subtracting product of denominator and first approximation from one */ + err_Q32 = silk_LSHIFT(((int) 1 << 29) - silk_SMULWB(b32_nrm, b32_inv), 3); + /* Q32 */ + + /* Refinement */ + result = silk_SMLAWW(result, err_Q32, b32_inv); + /* Q: 61 - b_headrm */ + + /* Convert to Qres domain */ + lshift = 61 - b_headrm - Qres; + if (lshift <= 0) { + return silk_LSHIFT_SAT32(result, -lshift); + } else if (lshift < 32) { + return silk_RSHIFT(result, lshift); + } else { + /* Avoid undefined result */ + return 0; + } + } + + //////////////////////// from macros.h ///////////////////////////////////////////// + /// + /// a32 + (b32 * (int)((short)(c32))) >> 16 output have to be 32bit int + /// + // fixme: This method should be as optimized as possible + static int silk_SMLAWB(int a32, int b32, int c32) { + //return (int)(a32 + ((b32 * (long)((short)c32)) >> 16)); + int ret; + ret = a32 + silk_SMULWB(b32, c32); + return ret; + } + + ///* (a32 * (b32 >> 16)) >> 16 */ + static int silk_SMULWT(int a32, int b32) { + return (((a32) >> 16) * ((b32) >> 16) + ((((a32) & 0x0000FFFF) * ((b32) >> 16)) >> 16)); + } + + ///* (int)((short)(a32)) * (b32 >> 16) */ + static int silk_SMULBT(int a32, int b32) { + return ((int) ((short) (a32)) * ((b32) >> 16)); + } + + ///* a32 + (int)((short)(b32)) * (c32 >> 16) */ + static int silk_SMLABT(int a32, int b32, int c32) { + return ((a32) + ((int) ((short) (b32))) * ((c32) >> 16)); + } + + ///* a64 + (b32 * c32) */ + static long silk_SMLAL(long a64, int b32, int c32) { + return (silk_ADD64((a64), ((long) (b32) * (long) (c32)))); + } + + static int MatrixGetPointer(int row, int column, int N) { + return (row * N) + column; + } + + static int MatrixGet(int[] Matrix_base_adr, int row, int column, int N) { + return Matrix_base_adr[((row) * (N)) + (column)]; + } + + static short MatrixGet(short[] Matrix_base_adr, int row, int column, int N) { + return Matrix_base_adr[((row) * (N)) + (column)]; + } + + static PitchAnalysisCore.silk_pe_stage3_vals MatrixGet(PitchAnalysisCore.silk_pe_stage3_vals[] Matrix_base_adr, int row, int column, int N) { + return Matrix_base_adr[((row) * (N)) + (column)]; + } + + static int MatrixGet(int[] Matrix_base_adr, int matrix_ptr, int row, int column, int N) { + return Matrix_base_adr[matrix_ptr + (row * N) + column]; + } + + static short MatrixGet(short[] Matrix_base_adr, int matrix_ptr, int row, int column, int N) { + return Matrix_base_adr[matrix_ptr + (row * N) + column]; + } + + static void MatrixSet(int[] Matrix_base_adr, int matrix_ptr, int row, int column, int N, int value) { + Matrix_base_adr[matrix_ptr + (row * N) + column] = value; + } + + static void MatrixSet(short[] Matrix_base_adr, int matrix_ptr, int row, int column, int N, short value) { + Matrix_base_adr[matrix_ptr + (row * N) + column] = value; + } + + static void MatrixSet(int[] Matrix_base_adr, int row, int column, int N, int value) { + Matrix_base_adr[((row) * (N)) + (column)] = value; + } + + static void MatrixSet(short[] Matrix_base_adr, int row, int column, int N, short value) { + Matrix_base_adr[((row) * (N)) + (column)] = value; + } + + /// + /// (a32 * b32) >> 16 + /// + static int silk_SMULWW(int a32, int b32) { + //return CHOP32(((long)(a32) * (b32)) >> 16); + return silk_MLA(silk_SMULWB((a32), (b32)), (a32), silk_RSHIFT_ROUND((b32), 16)); + } + + /// + /// a32 + ((b32 * c32) >> 16) + /// + static int silk_SMLAWW(int a32, int b32, int c32) { + //return CHOP32(((a32) + (((long)(b32) * (c32)) >> 16))); + return silk_MLA(silk_SMLAWB((a32), (b32), (c32)), (b32), silk_RSHIFT_ROUND((c32), 16)); + } + + /* count leading zeros of opus_int64 */ + static int silk_CLZ64(long input) { + int in_upper; + + in_upper = (int) silk_RSHIFT64(input, 32); + if (in_upper == 0) { + /* Search in the lower 32 bits */ + return 32 + silk_CLZ32(((int) input)); + } else { + /* Search in the upper 32 bits */ + return silk_CLZ32(in_upper); + } + } + + static int silk_CLZ32(int in32) { + return in32 == 0 ? 32 : 32 - EC_ILOG(in32); + } + + /// + /// Get number of leading zeros and fractional part (the bits right after the leading one) + /// + /// input + /// number of leading zeros + /// the 7 bits right after the leading one + static void silk_CLZ_FRAC(int input, BoxedValueInt lz, BoxedValueInt frac_Q7) { + int lzeros = silk_CLZ32(input); + + lz.Val = lzeros; + frac_Q7.Val = silk_ROR32(input, 24 - lzeros) & 0x7f; + } + + /// + /// Approximation of square root. + /// Accuracy: +/- 10% for output values > 15 + /// +/- 2.5% for output values > 120 + /// + /// + /// + static int silk_SQRT_APPROX(int x) { + int y; + int lz; + int frac_Q7; + + if (x <= 0) { + return 0; + } + + BoxedValueInt boxed_lz = new BoxedValueInt(0); + BoxedValueInt boxed_frac_Q7 = new BoxedValueInt(0); + silk_CLZ_FRAC(x, boxed_lz, boxed_frac_Q7); + lz = boxed_lz.Val; + frac_Q7 = boxed_frac_Q7.Val; + + if ((lz & 1) != 0) { + y = 32768; + } else { + y = 46214; // 46214 = sqrt(2) * 32768 + } + + // get scaling right + y >>= silk_RSHIFT(lz, 1); + + // increment using fractional part of input + y = silk_SMLAWB(y, y, silk_SMULBB(213, frac_Q7)); + + return y; + } + + static int MUL32_FRAC_Q(int a32, int b32, int Q) { + return ((int) (silk_RSHIFT_ROUND64(silk_SMULL(a32, b32), Q))); + } + + /// + /// Approximation of 128 * log2() (very close inverse of silk_log2lin()) + /// Convert input to a log scale + /// + /// (I) input in linear scale + /// + static int silk_lin2log(int inLin) { + BoxedValueInt lz = new BoxedValueInt(0); + BoxedValueInt frac_Q7 = new BoxedValueInt(0); + + silk_CLZ_FRAC(inLin, lz, frac_Q7); + + // Piece-wise parabolic approximation + return silk_LSHIFT(31 - lz.Val, 7) + silk_SMLAWB(frac_Q7.Val, silk_MUL(frac_Q7.Val, 128 - frac_Q7.Val), 179); + } + + /// + /// Approximation of 2^() (very close inverse of silk_lin2log()) + /// Convert input to a linear scale + /// + /// input on log scale + /// Linearized value + static int silk_log2lin(int inLog_Q7) { + int output, frac_Q7; + + if (inLog_Q7 < 0) { + return 0; + } else if (inLog_Q7 >= 3967) { + return Integer.MAX_VALUE; + } + + output = silk_LSHIFT(1, silk_RSHIFT(inLog_Q7, 7)); + frac_Q7 = inLog_Q7 & 0x7F; + + if (inLog_Q7 < 2048) { + /* Piece-wise parabolic approximation */ + output = silk_ADD_RSHIFT32(output, silk_MUL(output, silk_SMLAWB(frac_Q7, silk_SMULBB(frac_Q7, 128 - frac_Q7), -174)), 7); + } else { + /* Piece-wise parabolic approximation */ + output = silk_MLA(output, silk_RSHIFT(output, 7), silk_SMLAWB(frac_Q7, silk_SMULBB(frac_Q7, 128 - frac_Q7), -174)); + } + + return output; + } + + /// + /// Interpolate two vectors + /// + /// (O) interpolated vector [MAX_LPC_ORDER] + /// (I) first vector [MAX_LPC_ORDER] + /// (I) second vector [MAX_LPC_ORDER] + /// (I) interp. factor, weight on 2nd vector + /// (I) number of parameters + static void silk_interpolate( + short[] xi, + short[] x0, + short[] x1, + int ifact_Q2, + int d) { + int i; + + Inlines.OpusAssert(ifact_Q2 >= 0); + Inlines.OpusAssert(ifact_Q2 <= 4); + + for (i = 0; i < d; i++) { + xi[i] = (short) silk_ADD_RSHIFT(x0[i], silk_SMULBB(x1[i] - x0[i], ifact_Q2), 2); + } + } + + /// + /// Inner product with bit-shift + /// + /// I input vector 1 + /// I input vector 2 + /// I number of bits to shift + /// I vector lengths + /// + static int silk_inner_prod_aligned_scale( + short[] inVec1, + short[] inVec2, + int scale, + int len) { + int i, sum = 0; + for (i = 0; i < len; i++) { + sum = silk_ADD_RSHIFT32(sum, silk_SMULBB(inVec1[i], inVec2[i]), scale); + } + + return sum; + } + + /* Copy and multiply a vector by a constant */ + static void silk_scale_copy_vector16( + short[] data_out, + int data_out_ptr, + short[] data_in, + int data_in_ptr, + int gain_Q16, /* I Gain in Q16 */ + int dataSize /* I Length */ + ) { + for (int i = 0; i < dataSize; i++) { + data_out[data_out_ptr + i] = (short) (silk_SMULWB(gain_Q16, data_in[data_in_ptr + i])); + } + } + + /* Multiply a vector by a constant */ + static void silk_scale_vector32_Q26_lshift_18( + int[] data1, /* I/O Q0/Q18 */ + int data1_ptr, + int gain_Q26, /* I Q26 */ + int dataSize /* I length */ + ) { + for (int i = data1_ptr; i < data1_ptr + dataSize; i++) { + data1[i] = (int) (silk_RSHIFT64(silk_SMULL(data1[i], gain_Q26), 8)); + /* OUTPUT: Q18 */ + } + } + + /* sum = for(i=0;i + /// returns the value that has fewer higher-order bits, ignoring sign bit (? I think?) + /// + /// + /// + /// + static long EC_MINI(long a, long b) { + return (a + ((b - a) & ((b < a) ? 0xFFFFFFFF : 0))); + } + + static int EC_ILOG(long x) { + if (x == 0) { + return 1; + } + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + long y = x - ((x >> 1) & 0x55555555); + y = (((y >> 2) & 0x33333333) + (y & 0x33333333)); + y = (((y >> 4) + y) & 0x0f0f0f0f); + y += (y >> 8); + y += (y >> 16); + y = (y & 0x0000003f); + return (int) y; + } + + static int abs(int a) { + if (a < 0) { + return 0 - a; + } + return a; + } + + static int SignedByteToUnsignedInt(byte b) { + return (b & 0xFF); + } +} diff --git a/app/src/main/java/org/concentus/K2A.java b/app/src/main/java/org/concentus/K2A.java new file mode 100644 index 0000000000..6e3d0c9d7d --- /dev/null +++ b/app/src/main/java/org/concentus/K2A.java @@ -0,0 +1,75 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class K2A { + + /* Step up function, converts reflection coefficients to prediction coefficients */ + static void silk_k2a( + int[] A_Q24, /* O Prediction coefficients [order] Q24 */ + short[] rc_Q15, /* I Reflection coefficients [order] Q15 */ + int order /* I Prediction order */ + ) { + int k, n; + int[] Atmp = new int[SilkConstants.SILK_MAX_ORDER_LPC]; + + for (k = 0; k < order; k++) { + for (n = 0; n < k; n++) { + Atmp[n] = A_Q24[n]; + } + for (n = 0; n < k; n++) { + A_Q24[n] = Inlines.silk_SMLAWB(A_Q24[n], Inlines.silk_LSHIFT(Atmp[k - n - 1], 1), rc_Q15[k]); + } + A_Q24[k] = 0 - Inlines.silk_LSHIFT((int) rc_Q15[k], 9); + } + } + + /* Step up function, converts reflection coefficients to prediction coefficients */ + static void silk_k2a_Q16( + int[] A_Q24, /* O Prediction coefficients [order] Q24 */ + int[] rc_Q16, /* I Reflection coefficients [order] Q16 */ + int order /* I Prediction order */ + ) { + int k, n; + int[] Atmp = new int[SilkConstants.SILK_MAX_ORDER_LPC]; + + for (k = 0; k < order; k++) { + for (n = 0; n < k; n++) { + Atmp[n] = A_Q24[n]; + } + for (n = 0; n < k; n++) { + A_Q24[n] = Inlines.silk_SMLAWW(A_Q24[n], Atmp[k - n - 1], rc_Q16[k]); + } + A_Q24[k] = 0 - Inlines.silk_LSHIFT(rc_Q16[k], 8); + } + } +} diff --git a/app/src/main/java/org/concentus/Kernels.java b/app/src/main/java/org/concentus/Kernels.java new file mode 100644 index 0000000000..2a14b4e597 --- /dev/null +++ b/app/src/main/java/org/concentus/Kernels.java @@ -0,0 +1,350 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class Kernels { + + static void celt_fir( + short[] x, + int x_ptr, + short[] num, + short[] y, + int y_ptr, + int N, + int ord, + short[] mem + ) { + int i, j; + short[] rnum = new short[ord]; + short[] local_x = new short[N + ord]; + + for (i = 0; i < ord; i++) { + rnum[i] = num[ord - i - 1]; + } + + for (i = 0; i < ord; i++) { + local_x[i] = mem[ord - i - 1]; + } + + for (i = 0; i < N; i++) { + local_x[i + ord] = x[x_ptr + i]; + } + + for (i = 0; i < ord; i++) { + mem[i] = x[x_ptr + N - i - 1]; + } + + BoxedValueInt sum0 = new BoxedValueInt(0); + BoxedValueInt sum1 = new BoxedValueInt(0); + BoxedValueInt sum2 = new BoxedValueInt(0); + BoxedValueInt sum3 = new BoxedValueInt(0); + + for (i = 0; i < N - 3; i += 4) { + sum0.Val = 0; + sum1.Val = 0; + sum2.Val = 0; + sum3.Val = 0; + xcorr_kernel(rnum, 0, local_x, i, sum0, sum1, sum2, sum3, ord); + y[y_ptr + i] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i]), Inlines.PSHR32(sum0.Val, CeltConstants.SIG_SHIFT)))); + y[y_ptr + i + 1] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 1]), Inlines.PSHR32(sum1.Val, CeltConstants.SIG_SHIFT)))); + y[y_ptr + i + 2] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 2]), Inlines.PSHR32(sum2.Val, CeltConstants.SIG_SHIFT)))); + y[y_ptr + i + 3] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 3]), Inlines.PSHR32(sum3.Val, CeltConstants.SIG_SHIFT)))); + } + + for (; i < N; i++) { + int sum = 0; + + for (j = 0; j < ord; j++) { + sum = Inlines.MAC16_16(sum, rnum[j], local_x[i + j]); + } + + y[y_ptr + i] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i]), Inlines.PSHR32(sum, CeltConstants.SIG_SHIFT)))); + } + } + + static void celt_fir( + int[] x, + int x_ptr, + int[] num, + int num_ptr, + int[] y, + int y_ptr, + int N, + int ord, + int[] mem + ) { + int i, j; + int[] rnum = new int[ord]; + int[] local_x = new int[N + ord]; + + for (i = 0; i < ord; i++) { + rnum[i] = num[num_ptr + ord - i - 1]; + } + + for (i = 0; i < ord; i++) { + local_x[i] = mem[ord - i - 1]; + } + + for (i = 0; i < N; i++) { + local_x[i + ord] = x[x_ptr + i]; + } + + for (i = 0; i < ord; i++) { + mem[i] = x[x_ptr + N - i - 1]; + } + + BoxedValueInt sum0 = new BoxedValueInt(0); + BoxedValueInt sum1 = new BoxedValueInt(0); + BoxedValueInt sum2 = new BoxedValueInt(0); + BoxedValueInt sum3 = new BoxedValueInt(0); + + for (i = 0; i < N - 3; i += 4) { + sum0.Val = 0; + sum1.Val = 0; + sum2.Val = 0; + sum3.Val = 0; + xcorr_kernel(rnum, local_x, i, sum0, sum1, sum2, sum3, ord); + y[y_ptr + i] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i]), Inlines.PSHR32(sum0.Val, CeltConstants.SIG_SHIFT)))); + y[y_ptr + i + 1] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 1]), Inlines.PSHR32(sum1.Val, CeltConstants.SIG_SHIFT)))); + y[y_ptr + i + 2] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 2]), Inlines.PSHR32(sum2.Val, CeltConstants.SIG_SHIFT)))); + y[y_ptr + i + 3] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 3]), Inlines.PSHR32(sum3.Val, CeltConstants.SIG_SHIFT)))); + } + + for (; i < N; i++) { + int sum = 0; + + for (j = 0; j < ord; j++) { + sum = Inlines.MAC16_16(sum, rnum[j], local_x[i + j]); + } + + y[y_ptr + i] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i]), Inlines.PSHR32(sum, CeltConstants.SIG_SHIFT)))); + } + } + + /// + /// OPT: This is the kernel you really want to optimize. It gets used a lot by the prefilter and by the PLC. + /// + /// + /// + /// + /// + static void xcorr_kernel(short[] x, int x_ptr, short[] y, int y_ptr, BoxedValueInt _sum0, BoxedValueInt _sum1, BoxedValueInt _sum2, BoxedValueInt _sum3, int len) { + int sum0 = _sum0.Val; + int sum1 = _sum1.Val; + int sum2 = _sum2.Val; + int sum3 = _sum3.Val; + int j; + short y_0, y_1, y_2, y_3; + Inlines.OpusAssert(len >= 3); + y_3 = 0; + /* gcc doesn't realize that y_3 can't be used uninitialized */ + y_0 = y[y_ptr++]; + y_1 = y[y_ptr++]; + y_2 = y[y_ptr++]; + for (j = 0; j < len - 3; j += 4) { + short tmp; + tmp = x[x_ptr++]; + y_3 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_0); + sum1 = Inlines.MAC16_16(sum1, tmp, y_1); + sum2 = Inlines.MAC16_16(sum2, tmp, y_2); + sum3 = Inlines.MAC16_16(sum3, tmp, y_3); + tmp = x[x_ptr++]; + y_0 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_1); + sum1 = Inlines.MAC16_16(sum1, tmp, y_2); + sum2 = Inlines.MAC16_16(sum2, tmp, y_3); + sum3 = Inlines.MAC16_16(sum3, tmp, y_0); + tmp = x[x_ptr++]; + y_1 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_2); + sum1 = Inlines.MAC16_16(sum1, tmp, y_3); + sum2 = Inlines.MAC16_16(sum2, tmp, y_0); + sum3 = Inlines.MAC16_16(sum3, tmp, y_1); + tmp = x[x_ptr++]; + y_2 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_3); + sum1 = Inlines.MAC16_16(sum1, tmp, y_0); + sum2 = Inlines.MAC16_16(sum2, tmp, y_1); + sum3 = Inlines.MAC16_16(sum3, tmp, y_2); + } + if (j++ < len) { + short tmp; + tmp = x[x_ptr++]; + y_3 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_0); + sum1 = Inlines.MAC16_16(sum1, tmp, y_1); + sum2 = Inlines.MAC16_16(sum2, tmp, y_2); + sum3 = Inlines.MAC16_16(sum3, tmp, y_3); + } + if (j++ < len) { + short tmp; + tmp = x[x_ptr++]; + y_0 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_1); + sum1 = Inlines.MAC16_16(sum1, tmp, y_2); + sum2 = Inlines.MAC16_16(sum2, tmp, y_3); + sum3 = Inlines.MAC16_16(sum3, tmp, y_0); + } + if (j < len) { + short tmp; + tmp = x[x_ptr++]; + y_1 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_2); + sum1 = Inlines.MAC16_16(sum1, tmp, y_3); + sum2 = Inlines.MAC16_16(sum2, tmp, y_0); + sum3 = Inlines.MAC16_16(sum3, tmp, y_1); + } + + _sum0.Val = sum0; + _sum1.Val = sum1; + _sum2.Val = sum2; + _sum3.Val = sum3; + } + + static void xcorr_kernel(int[] x, int[] y, int y_ptr, BoxedValueInt _sum0, BoxedValueInt _sum1, BoxedValueInt _sum2, BoxedValueInt _sum3, int len) { + int sum0 = _sum0.Val; + int sum1 = _sum1.Val; + int sum2 = _sum2.Val; + int sum3 = _sum3.Val; + int j; + int y_0, y_1, y_2, y_3; + int x_ptr = 0; + Inlines.OpusAssert(len >= 3); + y_3 = 0; + /* gcc doesn't realize that y_3 can't be used uninitialized */ + y_0 = y[y_ptr++]; + y_1 = y[y_ptr++]; + y_2 = y[y_ptr++]; + for (j = 0; j < len - 3; j += 4) { + int tmp; + tmp = x[x_ptr++]; + y_3 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_0); + sum1 = Inlines.MAC16_16(sum1, tmp, y_1); + sum2 = Inlines.MAC16_16(sum2, tmp, y_2); + sum3 = Inlines.MAC16_16(sum3, tmp, y_3); + tmp = x[x_ptr++]; + y_0 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_1); + sum1 = Inlines.MAC16_16(sum1, tmp, y_2); + sum2 = Inlines.MAC16_16(sum2, tmp, y_3); + sum3 = Inlines.MAC16_16(sum3, tmp, y_0); + tmp = x[x_ptr++]; + y_1 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_2); + sum1 = Inlines.MAC16_16(sum1, tmp, y_3); + sum2 = Inlines.MAC16_16(sum2, tmp, y_0); + sum3 = Inlines.MAC16_16(sum3, tmp, y_1); + tmp = x[x_ptr++]; + y_2 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_3); + sum1 = Inlines.MAC16_16(sum1, tmp, y_0); + sum2 = Inlines.MAC16_16(sum2, tmp, y_1); + sum3 = Inlines.MAC16_16(sum3, tmp, y_2); + } + if (j++ < len) { + int tmp; + tmp = x[x_ptr++]; + y_3 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_0); + sum1 = Inlines.MAC16_16(sum1, tmp, y_1); + sum2 = Inlines.MAC16_16(sum2, tmp, y_2); + sum3 = Inlines.MAC16_16(sum3, tmp, y_3); + } + if (j++ < len) { + int tmp; + tmp = x[x_ptr++]; + y_0 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_1); + sum1 = Inlines.MAC16_16(sum1, tmp, y_2); + sum2 = Inlines.MAC16_16(sum2, tmp, y_3); + sum3 = Inlines.MAC16_16(sum3, tmp, y_0); + } + if (j < len) { + int tmp; + tmp = x[x_ptr++]; + y_1 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_2); + sum1 = Inlines.MAC16_16(sum1, tmp, y_3); + sum2 = Inlines.MAC16_16(sum2, tmp, y_0); + sum3 = Inlines.MAC16_16(sum3, tmp, y_1); + } + + _sum0.Val = sum0; + _sum1.Val = sum1; + _sum2.Val = sum2; + _sum3.Val = sum3; + } + + static int celt_inner_prod(short[] x, int x_ptr, short[] y, int y_ptr, int N) { + int i; + int xy = 0; + for (i = 0; i < N; i++) { + xy = Inlines.MAC16_16(xy, x[x_ptr + i], y[y_ptr + i]); + } + return xy; + } + + static int celt_inner_prod(short[] x, short[] y, int y_ptr, int N) { + int i; + int xy = 0; + for (i = 0; i < N; i++) { + xy = Inlines.MAC16_16(xy, x[i], y[y_ptr + i]); + } + return xy; + } + + static int celt_inner_prod(int[] x, int x_ptr, int[] y, int y_ptr, int N) { + int i; + int xy = 0; + for (i = 0; i < N; i++) { + xy = Inlines.MAC16_16(xy, x[x_ptr + i], y[y_ptr + i]); + } + return xy; + } + + static void dual_inner_prod(int[] x, int x_ptr, int[] y01, int y01_ptr, int[] y02, int y02_ptr, int N, BoxedValueInt xy1, BoxedValueInt xy2) { + int i; + int xy01 = 0; + int xy02 = 0; + for (i = 0; i < N; i++) { + xy01 = Inlines.MAC16_16(xy01, x[x_ptr + i], y01[y01_ptr + i]); + xy02 = Inlines.MAC16_16(xy02, x[x_ptr + i], y02[y02_ptr + i]); + } + xy1.Val = xy01; + xy2.Val = xy02; + } +} diff --git a/app/src/main/java/org/concentus/KissFFT.java b/app/src/main/java/org/concentus/KissFFT.java new file mode 100644 index 0000000000..072085c215 --- /dev/null +++ b/app/src/main/java/org/concentus/KissFFT.java @@ -0,0 +1,414 @@ +/* Copyright (c) 2003-2004, Mark Borgerding + Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Modified from KISS-FFT by Jean-Marc Valin + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + /* This code is originally from Mark Borgerding's KISS-FFT but has been + heavily modified to better suit Opus */ +package org.concentus; + +class KissFFT { + //public final int SAMP_MAX = 2147483647; + //public final int SAMP_MIN = 0 - SAMP_MAX; + //public final int TWID_MAX = 32767; + //public final int TRIG_UPSCALE = 1; + + static final int MAXFACTORS = 8; + + static int S_MUL(int a, int b) { + return Inlines.MULT16_32_Q15(b, a); + } + + static int S_MUL(int a, short b) { + return Inlines.MULT16_32_Q15(b, a); + } + + static int HALF_OF(int x) { + return x >> 1; + } + + static void kf_bfly2(int[] Fout, int fout_ptr, int m, int N) { + int Fout2; + int i; + { + short tw; + tw = ((short) (0.5 + (0.7071067812f) * (((int) 1) << (15))))/*Inlines.QCONST16(0.7071067812f, 15)*/; + /* We know that m==4 here because the radix-2 is just after a radix-4 */ + Inlines.OpusAssert(m == 4); + for (i = 0; i < N; i++) { + int t_r, t_i; + Fout2 = fout_ptr + 8; + t_r = Fout[Fout2 + 0]; + t_i = Fout[Fout2 + 1]; + Fout[Fout2 + 0] = Fout[fout_ptr + 0] - t_r; + Fout[Fout2 + 1] = Fout[fout_ptr + 1] - t_i; + Fout[fout_ptr + 0] += t_r; + Fout[fout_ptr + 1] += t_i; + + t_r = S_MUL(Fout[Fout2 + 2] + Fout[Fout2 + 3], tw); + t_i = S_MUL(Fout[Fout2 + 3] - Fout[Fout2 + 2], tw); + Fout[Fout2 + 2] = Fout[fout_ptr + 2] - t_r; + Fout[Fout2 + 3] = Fout[fout_ptr + 3] - t_i; + Fout[fout_ptr + 2] += t_r; + Fout[fout_ptr + 3] += t_i; + + t_r = Fout[Fout2 + 5]; + t_i = 0 - Fout[Fout2 + 4]; + Fout[Fout2 + 4] = Fout[fout_ptr + 4] - t_r; + Fout[Fout2 + 5] = Fout[fout_ptr + 5] - t_i; + Fout[fout_ptr + 4] += t_r; + Fout[fout_ptr + 5] += t_i; + + t_r = S_MUL(Fout[Fout2 + 7] - Fout[Fout2 + 6], tw); + t_i = S_MUL(0 - Fout[Fout2 + 7] - Fout[Fout2 + 6], tw); + Fout[Fout2 + 6] = Fout[fout_ptr + 6] - t_r; + Fout[Fout2 + 7] = Fout[fout_ptr + 7] - t_i; + Fout[fout_ptr + 6] += t_r; + Fout[fout_ptr + 7] += t_i; + + fout_ptr += 16; + } + } + } + + static void kf_bfly4( + int[] Fout, + int fout_ptr, + int fstride, + FFTState st, + int m, + int N, + int mm) { + int i; + + if (m == 1) { + /* Degenerate case where all the twiddles are 1. */ + int scratch0, scratch1, scratch2, scratch3; + for (i = 0; i < N; i++) { + scratch0 = Fout[fout_ptr + 0] - Fout[fout_ptr + 4]; + scratch1 = Fout[fout_ptr + 1] - Fout[fout_ptr + 5]; + Fout[fout_ptr + 0] += Fout[fout_ptr + 4]; + Fout[fout_ptr + 1] += Fout[fout_ptr + 5]; + scratch2 = Fout[fout_ptr + 2] + Fout[fout_ptr + 6]; + scratch3 = Fout[fout_ptr + 3] + Fout[fout_ptr + 7]; + Fout[fout_ptr + 4] = Fout[fout_ptr + 0] - scratch2; + Fout[fout_ptr + 5] = Fout[fout_ptr + 1] - scratch3; + Fout[fout_ptr + 0] += scratch2; + Fout[fout_ptr + 1] += scratch3; + scratch2 = Fout[fout_ptr + 2] - Fout[fout_ptr + 6]; + scratch3 = Fout[fout_ptr + 3] - Fout[fout_ptr + 7]; + Fout[fout_ptr + 2] = scratch0 + scratch3; + Fout[fout_ptr + 3] = scratch1 - scratch2; + Fout[fout_ptr + 6] = scratch0 - scratch3; + Fout[fout_ptr + 7] = scratch1 + scratch2; + fout_ptr += 8; + } + } else { + int j; + int scratch0, scratch1, scratch2, scratch3, scratch4, scratch5, scratch6, scratch7, scratch8, scratch9, scratch10, scratch11; + int tw1, tw2, tw3; + int Fout_beg = fout_ptr; + for (i = 0; i < N; i++) { + fout_ptr = Fout_beg + 2 * i * mm; + int m1 = fout_ptr + (2 * m); + int m2 = fout_ptr + (4 * m); + int m3 = fout_ptr + (6 * m); + tw3 = tw2 = tw1 = 0; + /* m is guaranteed to be a multiple of 4. */ + for (j = 0; j < m; j++) { + scratch0 = (S_MUL(Fout[m1], st.twiddles[tw1]) - S_MUL(Fout[m1 + 1], st.twiddles[tw1 + 1])); + scratch1 = (S_MUL(Fout[m1], st.twiddles[tw1 + 1]) + S_MUL(Fout[m1 + 1], st.twiddles[tw1])); + scratch2 = (S_MUL(Fout[m2], st.twiddles[tw2]) - S_MUL(Fout[m2 + 1], st.twiddles[tw2 + 1])); + scratch3 = (S_MUL(Fout[m2], st.twiddles[tw2 + 1]) + S_MUL(Fout[m2 + 1], st.twiddles[tw2])); + scratch4 = (S_MUL(Fout[m3], st.twiddles[tw3]) - S_MUL(Fout[m3 + 1], st.twiddles[tw3 + 1])); + scratch5 = (S_MUL(Fout[m3], st.twiddles[tw3 + 1]) + S_MUL(Fout[m3 + 1], st.twiddles[tw3])); + scratch10 = Fout[fout_ptr] - scratch2; + scratch11 = Fout[fout_ptr + 1] - scratch3; + Fout[fout_ptr] += scratch2; + Fout[fout_ptr + 1] += scratch3; + scratch6 = scratch0 + scratch4; + scratch7 = scratch1 + scratch5; + scratch8 = scratch0 - scratch4; + scratch9 = scratch1 - scratch5; + Fout[m2] = Fout[fout_ptr] - scratch6; + Fout[m2 + 1] = Fout[fout_ptr + 1] - scratch7; + tw1 += fstride * 2; + tw2 += fstride * 4; + tw3 += fstride * 6; + Fout[fout_ptr] += scratch6; + Fout[fout_ptr + 1] += scratch7; + Fout[m1] = scratch10 + scratch9; + Fout[m1 + 1] = scratch11 - scratch8; + Fout[m3] = scratch10 - scratch9; + Fout[m3 + 1] = scratch11 + scratch8; + fout_ptr += 2; + m1 += 2; + m2 += 2; + m3 += 2; + } + } + } + } + + static void kf_bfly3( + int[] Fout, + int fout_ptr, + int fstride, + FFTState st, + int m, + int N, + int mm + ) { + int i; + int k; + int m1 = 2 * m; + int m2 = 4 * m; + int tw1, tw2; + int scratch0, scratch1, scratch2, scratch3, scratch4, scratch5, scratch6, scratch7; + + int Fout_beg = fout_ptr; + + for (i = 0; i < N; i++) { + fout_ptr = Fout_beg + 2 * i * mm; + tw1 = tw2 = 0; + /* For non-custom modes, m is guaranteed to be a multiple of 4. */ + k = m; + do { + scratch2 = (S_MUL(Fout[fout_ptr + m1], st.twiddles[tw1]) - S_MUL(Fout[fout_ptr + m1 + 1], st.twiddles[tw1 + 1])); + scratch3 = (S_MUL(Fout[fout_ptr + m1], st.twiddles[tw1 + 1]) + S_MUL(Fout[fout_ptr + m1 + 1], st.twiddles[tw1])); + scratch4 = (S_MUL(Fout[fout_ptr + m2], st.twiddles[tw2]) - S_MUL(Fout[fout_ptr + m2 + 1], st.twiddles[tw2 + 1])); + scratch5 = (S_MUL(Fout[fout_ptr + m2], st.twiddles[tw2 + 1]) + S_MUL(Fout[fout_ptr + m2 + 1], st.twiddles[tw2])); + + scratch6 = scratch2 + scratch4; + scratch7 = scratch3 + scratch5; + scratch0 = scratch2 - scratch4; + scratch1 = scratch3 - scratch5; + + tw1 += fstride * 2; + tw2 += fstride * 4; + + Fout[fout_ptr + m1] = Fout[fout_ptr + 0] - HALF_OF(scratch6); + Fout[fout_ptr + m1 + 1] = Fout[fout_ptr + 1] - HALF_OF(scratch7); + + scratch0 = S_MUL(scratch0, -28378); + scratch1 = S_MUL(scratch1, -28378); + + Fout[fout_ptr + 0] += scratch6; + Fout[fout_ptr + 1] += scratch7; + + Fout[fout_ptr + m2] = Fout[fout_ptr + m1] + scratch1; + Fout[fout_ptr + m2 + 1] = Fout[fout_ptr + m1 + 1] - scratch0; + + Fout[fout_ptr + m1] -= scratch1; + Fout[fout_ptr + m1 + 1] += scratch0; + + fout_ptr += 2; + } while ((--k) != 0); + } + } + + static void kf_bfly5( + int[] Fout, + int fout_ptr, + int fstride, + FFTState st, + int m, + int N, + int mm + ) { + int Fout0, Fout1, Fout2, Fout3, Fout4; + int i, u; + int scratch0, scratch1, scratch2, scratch3, scratch4, scratch5, + scratch6, scratch7, scratch8, scratch9, scratch10, scratch11, + scratch12, scratch13, scratch14, scratch15, scratch16, scratch17, + scratch18, scratch19, scratch20, scratch21, scratch22, scratch23, + scratch24, scratch25; + + int Fout_beg = fout_ptr; + + short ya_r = 10126; + short ya_i = -31164; + short yb_r = -26510; + short yb_i = -19261; + int tw1, tw2, tw3, tw4; + + for (i = 0; i < N; i++) { + tw1 = tw2 = tw3 = tw4 = 0; + fout_ptr = Fout_beg + 2 * i * mm; + Fout0 = fout_ptr; + Fout1 = fout_ptr + (2 * m); + Fout2 = fout_ptr + (4 * m); + Fout3 = fout_ptr + (6 * m); + Fout4 = fout_ptr + (8 * m); + + /* For non-custom modes, m is guaranteed to be a multiple of 4. */ + for (u = 0; u < m; ++u) { + scratch0 = Fout[Fout0 + 0]; + scratch1 = Fout[Fout0 + 1]; + + scratch2 = (S_MUL(Fout[Fout1 + 0], st.twiddles[tw1]) - S_MUL(Fout[Fout1 + 1], st.twiddles[tw1 + 1])); + scratch3 = (S_MUL(Fout[Fout1 + 0], st.twiddles[tw1 + 1]) + S_MUL(Fout[Fout1 + 1], st.twiddles[tw1])); + scratch4 = (S_MUL(Fout[Fout2 + 0], st.twiddles[tw2]) - S_MUL(Fout[Fout2 + 1], st.twiddles[tw2 + 1])); + scratch5 = (S_MUL(Fout[Fout2 + 0], st.twiddles[tw2 + 1]) + S_MUL(Fout[Fout2 + 1], st.twiddles[tw2])); + scratch6 = (S_MUL(Fout[Fout3 + 0], st.twiddles[tw3]) - S_MUL(Fout[Fout3 + 1], st.twiddles[tw3 + 1])); + scratch7 = (S_MUL(Fout[Fout3 + 0], st.twiddles[tw3 + 1]) + S_MUL(Fout[Fout3 + 1], st.twiddles[tw3])); + scratch8 = (S_MUL(Fout[Fout4 + 0], st.twiddles[tw4]) - S_MUL(Fout[Fout4 + 1], st.twiddles[tw4 + 1])); + scratch9 = (S_MUL(Fout[Fout4 + 0], st.twiddles[tw4 + 1]) + S_MUL(Fout[Fout4 + 1], st.twiddles[tw4])); + + tw1 += (2 * fstride); + tw2 += (4 * fstride); + tw3 += (6 * fstride); + tw4 += (8 * fstride); + + scratch14 = scratch2 + scratch8; + scratch15 = scratch3 + scratch9; + scratch20 = scratch2 - scratch8; + scratch21 = scratch3 - scratch9; + scratch16 = scratch4 + scratch6; + scratch17 = scratch5 + scratch7; + scratch18 = scratch4 - scratch6; + scratch19 = scratch5 - scratch7; + + Fout[Fout0 + 0] += scratch14 + scratch16; + Fout[Fout0 + 1] += scratch15 + scratch17; + + scratch10 = scratch0 + S_MUL(scratch14, ya_r) + S_MUL(scratch16, yb_r); + scratch11 = scratch1 + S_MUL(scratch15, ya_r) + S_MUL(scratch17, yb_r); + + scratch12 = S_MUL(scratch21, ya_i) + S_MUL(scratch19, yb_i); + scratch13 = 0 - S_MUL(scratch20, ya_i) - S_MUL(scratch18, yb_i); + + Fout[Fout1 + 0] = scratch10 - scratch12; + Fout[Fout1 + 1] = scratch11 - scratch13; + Fout[Fout4 + 0] = scratch10 + scratch12; + Fout[Fout4 + 1] = scratch11 + scratch13; + + scratch22 = scratch0 + S_MUL(scratch14, yb_r) + S_MUL(scratch16, ya_r); + scratch23 = scratch1 + S_MUL(scratch15, yb_r) + S_MUL(scratch17, ya_r); + scratch24 = 0 - S_MUL(scratch21, yb_i) + S_MUL(scratch19, ya_i); + scratch25 = S_MUL(scratch20, yb_i) - S_MUL(scratch18, ya_i); + + Fout[Fout2 + 0] = scratch22 + scratch24; + Fout[Fout2 + 1] = scratch23 + scratch25; + Fout[Fout3 + 0] = scratch22 - scratch24; + Fout[Fout3 + 1] = scratch23 - scratch25; + + Fout0 += 2; + Fout1 += 2; + Fout2 += 2; + Fout3 += 2; + Fout4 += 2; + } + } + } + + static void opus_fft_impl(FFTState st, int[] fout, int fout_ptr) { + int m2, m; + int p; + int L; + int[] fstride = new int[MAXFACTORS]; + int i; + int shift; + + /* st.shift can be -1 */ + shift = st.shift > 0 ? st.shift : 0; + + fstride[0] = 1; + L = 0; + do { + p = st.factors[2 * L]; + m = st.factors[2 * L + 1]; + fstride[L + 1] = fstride[L] * p; + L++; + } while (m != 1); + + m = st.factors[2 * L - 1]; + for (i = L - 1; i >= 0; i--) { + if (i != 0) { + m2 = st.factors[2 * i - 1]; + } else { + m2 = 1; + } + switch (st.factors[2 * i]) { + case 2: + kf_bfly2(fout, fout_ptr, m, fstride[i]); + break; + case 4: + kf_bfly4(fout, fout_ptr, fstride[i] << shift, st, m, fstride[i], m2); + break; + case 3: + kf_bfly3(fout, fout_ptr, fstride[i] << shift, st, m, fstride[i], m2); + break; + case 5: + kf_bfly5(fout, fout_ptr, fstride[i] << shift, st, m, fstride[i], m2); + break; + } + m = m2; + } + } + + static void opus_fft(FFTState st, int[] fin, int[] fout) { + int i; + /* Allows us to scale with MULT16_32_Q16() */ + int scale_shift = st.scale_shift - 1; + short scale = st.scale; + + Inlines.OpusAssert(fin != fout, "In-place FFT not supported"); + + /* Bit-reverse the input */ + for (i = 0; i < st.nfft; i++) { + fout[(2 * st.bitrev[i])] = Inlines.SHR32(Inlines.MULT16_32_Q16(scale, fin[(2 * i)]), scale_shift); + fout[(2 * st.bitrev[i] + 1)] = Inlines.SHR32(Inlines.MULT16_32_Q16(scale, fin[(2 * i) + 1]), scale_shift); + } + + opus_fft_impl(st, fout, 0); + } + + //static void opus_ifft(FFTState st, Pointer fin, Pointer fout) + //{ + // int i; + // Inlines.OpusAssert(fin != fout, "In-place iFFT not supported"); + // /* Bit-reverse the input */ + // for (i = 0; i < st.nfft * 2; i++) + // { + // fout[st.bitrev[i]] = fin[i]; + // } + // for (i = 1; i < st.nfft * 2; i += 2) + // { + // fout[i] = -fout[i]; + // } + // opus_fft_impl(st, fout.Data, fout.Offset); + // for (i = 1; i < st.nfft * 2; i += 2) + // fout[i] = -fout[i]; + //} +} diff --git a/app/src/main/java/org/concentus/LPCInversePredGain.java b/app/src/main/java/org/concentus/LPCInversePredGain.java new file mode 100644 index 0000000000..54117f8ff0 --- /dev/null +++ b/app/src/main/java/org/concentus/LPCInversePredGain.java @@ -0,0 +1,150 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class LPCInversePredGain { + + private static final int QA = 24; + private static final int A_LIMIT = ((int) ((0.99975f) * ((long) 1 << (QA)) + 0.5))/*Inlines.SILK_CONST(0.99975f, QA)*/; + + /* Compute inverse of LPC prediction gain, and */ + /* test if LPC coefficients are stable (all poles within unit circle) */ + static int LPC_inverse_pred_gain_QA( /* O Returns inverse prediction gain in energy domain, Q30 */ + int[][] A_QA, /* I Prediction coefficients [ 2 ][SILK_MAX_ORDER_LPC] */ + int order /* I Prediction order */ + ) { + int k, n, mult2Q; + int invGain_Q30, rc_Q31, rc_mult1_Q30, rc_mult2, tmp_QA; + int[] Aold_QA; + int[] Anew_QA; + + Anew_QA = A_QA[order & 1]; + + invGain_Q30 = (int) 1 << 30; + for (k = order - 1; k > 0; k--) { + /* Check for stability */ + if ((Anew_QA[k] > A_LIMIT) || (Anew_QA[k] < -A_LIMIT)) { + return 0; + } + + /* Set RC equal to negated AR coef */ + rc_Q31 = 0 - Inlines.silk_LSHIFT(Anew_QA[k], 31 - QA); + + /* rc_mult1_Q30 range: [ 1 : 2^30 ] */ + rc_mult1_Q30 = ((int) 1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31); + Inlines.OpusAssert(rc_mult1_Q30 > (1 << 15)); + /* reduce A_LIMIT if fails */ + Inlines.OpusAssert(rc_mult1_Q30 <= (1 << 30)); + + /* rc_mult2 range: [ 2^30 : silk_int32_MAX ] */ + mult2Q = 32 - Inlines.silk_CLZ32(Inlines.silk_abs(rc_mult1_Q30)); + rc_mult2 = Inlines.silk_INVERSE32_varQ(rc_mult1_Q30, mult2Q + 30); + + /* Update inverse gain */ + /* invGain_Q30 range: [ 0 : 2^30 ] */ + invGain_Q30 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, rc_mult1_Q30), 2); + Inlines.OpusAssert(invGain_Q30 >= 0); + Inlines.OpusAssert(invGain_Q30 <= (1 << 30)); + + /* Swap pointers */ + Aold_QA = Anew_QA; + Anew_QA = A_QA[k & 1]; + + /* Update AR coefficient */ + for (n = 0; n < k; n++) { + tmp_QA = Aold_QA[n] - Inlines.MUL32_FRAC_Q(Aold_QA[k - n - 1], rc_Q31, 31); + Anew_QA[n] = Inlines.MUL32_FRAC_Q(tmp_QA, rc_mult2, mult2Q); + } + } + + /* Check for stability */ + if ((Anew_QA[0] > A_LIMIT) || (Anew_QA[0] < -A_LIMIT)) { + return 0; + } + + /* Set RC equal to negated AR coef */ + rc_Q31 = 0 - Inlines.silk_LSHIFT(Anew_QA[0], 31 - QA); + + /* Range: [ 1 : 2^30 ] */ + rc_mult1_Q30 = ((int) 1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31); + + /* Update inverse gain */ + /* Range: [ 0 : 2^30 ] */ + invGain_Q30 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, rc_mult1_Q30), 2); + Inlines.OpusAssert(invGain_Q30 >= 0); + Inlines.OpusAssert(invGain_Q30 <= 1 << 30); + + return invGain_Q30; + } + + /* For input in Q12 domain */ + static int silk_LPC_inverse_pred_gain( /* O Returns inverse prediction gain in energy domain, Q30 */ + short[] A_Q12, /* I Prediction coefficients, Q12 [order] */ + int order /* I Prediction order */ + ) { + int k; + int[][] Atmp_QA = Arrays.InitTwoDimensionalArrayInt(2, SilkConstants.SILK_MAX_ORDER_LPC); + int[] Anew_QA; + int DC_resp = 0; + + Anew_QA = Atmp_QA[order & 1]; + + /* Increase Q domain of the AR coefficients */ + for (k = 0; k < order; k++) { + DC_resp += (int) A_Q12[k]; + Anew_QA[k] = Inlines.silk_LSHIFT32((int) A_Q12[k], QA - 12); + } + /* If the DC is unstable, we don't even need to do the full calculations */ + if (DC_resp >= 4096) { + return 0; + } + return LPC_inverse_pred_gain_QA(Atmp_QA, order); + } + + static int silk_LPC_inverse_pred_gain_Q24( /* O Returns inverse prediction gain in energy domain, Q30 */ + int[] A_Q24, /* I Prediction coefficients [order] */ + int order /* I Prediction order */ + ) { + int k; + int[][] Atmp_QA = Arrays.InitTwoDimensionalArrayInt(2, SilkConstants.SILK_MAX_ORDER_LPC); + int[] Anew_QA; + + Anew_QA = Atmp_QA[order & 1]; + + /* Increase Q domain of the AR coefficients */ + for (k = 0; k < order; k++) { + Anew_QA[k] = Inlines.silk_RSHIFT32(A_Q24[k], 24 - QA); + } + + return LPC_inverse_pred_gain_QA(Atmp_QA, order); + } +} diff --git a/app/src/main/java/org/concentus/LTPAnalysisFilter.java b/app/src/main/java/org/concentus/LTPAnalysisFilter.java new file mode 100644 index 0000000000..25fa30f3de --- /dev/null +++ b/app/src/main/java/org/concentus/LTPAnalysisFilter.java @@ -0,0 +1,93 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class LTPAnalysisFilter { + + static void silk_LTP_analysis_filter( + short[] LTP_res, /* O LTP residual signal of length SilkConstants.MAX_NB_SUBFR * ( pre_length + subfr_length ) */ + short[] x, /* I Pointer to input signal with at least max( pitchL ) preceding samples */ + int x_ptr, + short[] LTPCoef_Q14,/* I LTP_ORDER LTP coefficients for each MAX_NB_SUBFR subframe [SilkConstants.LTP_ORDER * SilkConstants.MAX_NB_SUBFR] */ + int[] pitchL, /* I Pitch lag, one for each subframe [SilkConstants.MAX_NB_SUBFR] */ + int[] invGains_Q16, /* I Inverse quantization gains, one for each subframe [SilkConstants.MAX_NB_SUBFR] */ + int subfr_length, /* I Length of each subframe */ + int nb_subfr, /* I Number of subframes */ + int pre_length /* I Length of the preceding samples starting at &x[0] for each subframe */ + ) { + int x_ptr2, x_lag_ptr; + short[] Btmp_Q14 = new short[SilkConstants.LTP_ORDER]; + int LTP_res_ptr; + int k, i; + int LTP_est; + + x_ptr2 = x_ptr; + LTP_res_ptr = 0; + for (k = 0; k < nb_subfr; k++) { + x_lag_ptr = x_ptr2 - pitchL[k]; + + Btmp_Q14[0] = LTPCoef_Q14[k * SilkConstants.LTP_ORDER]; + Btmp_Q14[1] = LTPCoef_Q14[k * SilkConstants.LTP_ORDER + 1]; + Btmp_Q14[2] = LTPCoef_Q14[k * SilkConstants.LTP_ORDER + 2]; + Btmp_Q14[3] = LTPCoef_Q14[k * SilkConstants.LTP_ORDER + 3]; + Btmp_Q14[4] = LTPCoef_Q14[k * SilkConstants.LTP_ORDER + 4]; + + /* LTP analysis FIR filter */ + for (i = 0; i < subfr_length + pre_length; i++) { + int LTP_res_ptri = LTP_res_ptr + i; + LTP_res[LTP_res_ptri] = x[x_ptr2 + i]; + + /* Long-term prediction */ + LTP_est = Inlines.silk_SMULBB(x[x_lag_ptr + SilkConstants.LTP_ORDER / 2], Btmp_Q14[0]); + LTP_est = Inlines.silk_SMLABB_ovflw(LTP_est, x[x_lag_ptr + 1], Btmp_Q14[1]); + LTP_est = Inlines.silk_SMLABB_ovflw(LTP_est, x[x_lag_ptr], Btmp_Q14[2]); + LTP_est = Inlines.silk_SMLABB_ovflw(LTP_est, x[x_lag_ptr - 1], Btmp_Q14[3]); + LTP_est = Inlines.silk_SMLABB_ovflw(LTP_est, x[x_lag_ptr - 2], Btmp_Q14[4]); + + LTP_est = Inlines.silk_RSHIFT_ROUND(LTP_est, 14); + /* round and . Q0*/ + + /* Subtract long-term prediction */ + LTP_res[LTP_res_ptri] = (short) Inlines.silk_SAT16((int) x[x_ptr2 + i] - LTP_est); + + /* Scale residual */ + LTP_res[LTP_res_ptri] = (short) (Inlines.silk_SMULWB(invGains_Q16[k], LTP_res[LTP_res_ptri])); + + x_lag_ptr++; + } + + /* Update pointers */ + LTP_res_ptr += subfr_length + pre_length; + x_ptr2 += subfr_length; + } + } +} diff --git a/app/src/main/java/org/concentus/LTPScaleControl.java b/app/src/main/java/org/concentus/LTPScaleControl.java new file mode 100644 index 0000000000..6c7292ae9f --- /dev/null +++ b/app/src/main/java/org/concentus/LTPScaleControl.java @@ -0,0 +1,55 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class LTPScaleControl { + + /* Calculation of LTP state scaling */ + static void silk_LTP_scale_ctrl( + SilkChannelEncoder psEnc, /* I/O encoder state */ + SilkEncoderControl psEncCtrl, /* I/O encoder control */ + int condCoding /* I The type of conditional coding to use */ + ) { + int round_loss; + + if (condCoding == SilkConstants.CODE_INDEPENDENTLY) { + /* Only scale if first frame in packet */ + round_loss = psEnc.PacketLoss_perc + psEnc.nFramesPerPacket; + psEnc.indices.LTP_scaleIndex = (byte) Inlines.silk_LIMIT( + Inlines.silk_SMULWB(Inlines.silk_SMULBB(round_loss, psEncCtrl.LTPredCodGain_Q7), ((int) ((0.1f) * ((long) 1 << (9)) + 0.5))/*Inlines.SILK_CONST(0.1f, 9)*/), 0, 2); + } else { + /* Default is minimum scaling */ + psEnc.indices.LTP_scaleIndex = 0; + } + psEncCtrl.LTP_scale_Q14 = SilkTables.silk_LTPScales_table_Q14[psEnc.indices.LTP_scaleIndex]; + } +} diff --git a/app/src/main/java/org/concentus/Laplace.java b/app/src/main/java/org/concentus/Laplace.java new file mode 100644 index 0000000000..9f77fd0450 --- /dev/null +++ b/app/src/main/java/org/concentus/Laplace.java @@ -0,0 +1,133 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class Laplace { + + /* The minimum probability of an energy delta (out of 32768). */ + private static final int LAPLACE_LOG_MINP = 0; + private static final long LAPLACE_MINP = (1 << LAPLACE_LOG_MINP); + /* The minimum number of guaranteed representable energy deltas (in one + direction). */ + private static final int LAPLACE_NMIN = 16; + + /* When called, decay is positive and at most 11456. */ + static long ec_laplace_get_freq1(long fs0, int decay) { + long ft = Inlines.CapToUInt32(32768 - LAPLACE_MINP * (2 * LAPLACE_NMIN) - fs0); + return (Inlines.CapToUInt32(ft * (16384 - decay)) >> 15); + } + + static void ec_laplace_encode(EntropyCoder enc, BoxedValueInt value, long fs, int decay) { + long fl; + int val = value.Val; + fl = 0; + if (val != 0) { + int s; + int i; + s = 0 - (val < 0 ? 1 : 0); + val = (val + s) ^ s; + fl = fs; + fs = ec_laplace_get_freq1(fs, decay); + + /* Search the decaying part of the PDF.*/ + for (i = 1; fs > 0 && i < val; i++) { + fs *= 2; + fl = Inlines.CapToUInt32(fl + fs + 2 * LAPLACE_MINP); + fs = Inlines.CapToUInt32((fs * (int) decay) >> 15); + } + + /* Everything beyond that has probability LAPLACE_MINP. */ + if (fs == 0) { + int di; + int ndi_max; + ndi_max = (int) (32768 - fl + LAPLACE_MINP - 1) >> LAPLACE_LOG_MINP; + ndi_max = (ndi_max - s) >> 1; + di = Inlines.IMIN(val - i, ndi_max - 1); + fl = Inlines.CapToUInt32(fl + (2 * di + 1 + s) * LAPLACE_MINP); + fs = Inlines.IMIN(LAPLACE_MINP, 32768 - fl); + value.Val = (i + di + s) ^ s; + } else { + fs += LAPLACE_MINP; + fl = fl + Inlines.CapToUInt32(fs & ~s); + } + Inlines.OpusAssert(fl + fs <= 32768); + Inlines.OpusAssert(fs > 0); + } + + enc.encode_bin(fl, (fl + fs), 15); + } + + static int ec_laplace_decode(EntropyCoder dec, long fs, int decay) { + int val = 0; + long fl; + long fm; + fm = dec.decode_bin(15); + fl = 0; + + if (fm >= fs) { + val++; + fl = fs; + fs = ec_laplace_get_freq1(fs, decay) + LAPLACE_MINP; + /* Search the decaying part of the PDF.*/ + while (fs > LAPLACE_MINP && fm >= fl + 2 * fs) { + fs *= 2; + fl = Inlines.CapToUInt32(fl + fs); + fs = Inlines.CapToUInt32(((fs - 2 * LAPLACE_MINP) * (int) decay) >> 15); + fs += LAPLACE_MINP; + val++; + } + /* Everything beyond that has probability LAPLACE_MINP. */ + if (fs <= LAPLACE_MINP) { + int di; + di = (int) (fm - fl) >> (LAPLACE_LOG_MINP + 1); + val += di; + fl = Inlines.CapToUInt32(fl + Inlines.CapToUInt32(2 * di * LAPLACE_MINP)); + } + if (fm < fl + fs) { + val = -val; + } else { + fl = Inlines.CapToUInt32(fl + fs); + } + } + + Inlines.OpusAssert(fl < 32768); + Inlines.OpusAssert(fs > 0); + Inlines.OpusAssert(fl <= fm); + Inlines.OpusAssert(fm < Inlines.IMIN(fl + fs, 32768)); + + dec.dec_update(fl, Inlines.IMIN(fl + fs, 32768), 32768); + return val; + } +} diff --git a/app/src/main/java/org/concentus/LinearAlgebra.java b/app/src/main/java/org/concentus/LinearAlgebra.java new file mode 100644 index 0000000000..b72b3e171a --- /dev/null +++ b/app/src/main/java/org/concentus/LinearAlgebra.java @@ -0,0 +1,239 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class LinearAlgebra { + + /* Solves Ax = b, assuming A is symmetric */ + static void silk_solve_LDL( + int[] A, /* I Pointer to symetric square matrix A */ + int A_ptr, + int M, /* I Size of matrix */ + int[] b, /* I Pointer to b vector */ + int[] x_Q16 /* O Pointer to x solution vector */ + ) { + Inlines.OpusAssert(M <= SilkConstants.MAX_MATRIX_SIZE); + int[] L_Q16 = new int[M * M]; + int[] Y = new int[SilkConstants.MAX_MATRIX_SIZE]; + + // [Porting note] This is an interleaved array. Formerly it was an array of data structures laid out thus: + //private struct inv_D_t + //{ + // int Q36_part; + // int Q48_part; + //} + int[] inv_D = new int[SilkConstants.MAX_MATRIX_SIZE * 2]; + + /** + * ************************************************* + * Factorize A by LDL such that A = L*D*L', where L is lower triangular + * with ones on diagonal + *************************************************** + */ + silk_LDL_factorize(A, A_ptr, M, L_Q16, inv_D); + + /** + * ************************************************** + * substitute D*L'*x = Y. ie: L*D*L'*x = b => L*Y = b <=> Y = inv(L)*b + ***************************************************** + */ + silk_LS_SolveFirst(L_Q16, M, b, Y); + + /** + * ************************************************** + * D*L'*x = Y <=> L'*x = inv(D)*Y, because D is diagonal just multiply + * with 1/d_i + *************************************************** + */ + silk_LS_divide_Q16(Y, inv_D, M); + + /** + * ************************************************** + * x = inv(L') * inv(D) * Y + **************************************************** + */ + silk_LS_SolveLast(L_Q16, M, Y, x_Q16); + + } + + /* Factorize square matrix A into LDL form */ + private static void silk_LDL_factorize( + int[] A, /* I/O Pointer to Symetric Square Matrix */ + int A_ptr, + int M, /* I Size of Matrix */ + int[] L_Q16, /* I/O Pointer to Square Upper triangular Matrix */ + int[] inv_D /* I/O Pointer to vector holding inverted diagonal elements of D */ + ) { + int i, j, k, status, loop_count; + int[] scratch1; + int scratch1_ptr; + int[] scratch2; + int scratch2_ptr; + int diag_min_value, tmp_32, err; + int[] v_Q0 = new int[M]; + /*SilkConstants.MAX_MATRIX_SIZE*/ + int[] D_Q0 = new int[M]; + /*SilkConstants.MAX_MATRIX_SIZE*/ + int one_div_diag_Q36, one_div_diag_Q40, one_div_diag_Q48; + + Inlines.OpusAssert(M <= SilkConstants.MAX_MATRIX_SIZE); + + status = 1; + diag_min_value = Inlines.silk_max_32(Inlines.silk_SMMUL(Inlines.silk_ADD_SAT32(A[A_ptr], A[A_ptr + Inlines.silk_SMULBB(M, M) - 1]), ((int) ((TuningParameters.FIND_LTP_COND_FAC) * ((long) 1 << (31)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LTP_COND_FAC, 31)*/), 1 << 9); + for (loop_count = 0; loop_count < M && status == 1; loop_count++) { + status = 0; + for (j = 0; j < M; j++) { + scratch1 = L_Q16; + scratch1_ptr = Inlines.MatrixGetPointer(j, 0, M); + tmp_32 = 0; + for (i = 0; i < j; i++) { + v_Q0[i] = Inlines.silk_SMULWW(D_Q0[i], scratch1[scratch1_ptr + i]); + /* Q0 */ + tmp_32 = Inlines.silk_SMLAWW(tmp_32, v_Q0[i], scratch1[scratch1_ptr + i]); + /* Q0 */ + } + tmp_32 = Inlines.silk_SUB32(Inlines.MatrixGet(A, A_ptr, j, j, M), tmp_32); + + if (tmp_32 < diag_min_value) { + tmp_32 = Inlines.silk_SUB32(Inlines.silk_SMULBB(loop_count + 1, diag_min_value), tmp_32); + /* Matrix not positive semi-definite, or ill conditioned */ + for (i = 0; i < M; i++) { + Inlines.MatrixSet(A, A_ptr, i, i, M, Inlines.silk_ADD32(Inlines.MatrixGet(A, A_ptr, i, i, M), tmp_32)); + } + status = 1; + break; + } + D_Q0[j] = tmp_32; + /* always < max(Correlation) */ + + /* two-step division */ + one_div_diag_Q36 = Inlines.silk_INVERSE32_varQ(tmp_32, 36); + /* Q36 */ + one_div_diag_Q40 = Inlines.silk_LSHIFT(one_div_diag_Q36, 4); + /* Q40 */ + err = Inlines.silk_SUB32((int) 1 << 24, Inlines.silk_SMULWW(tmp_32, one_div_diag_Q40)); + /* Q24 */ + one_div_diag_Q48 = Inlines.silk_SMULWW(err, one_div_diag_Q40); + /* Q48 */ + + /* Save 1/Ds */ + inv_D[(j * 2) + 0] = one_div_diag_Q36; + inv_D[(j * 2) + 1] = one_div_diag_Q48; + + Inlines.MatrixSet(L_Q16, j, j, M, 65536); + /* 1.0 in Q16 */ + scratch1 = A; + scratch1_ptr = Inlines.MatrixGetPointer(j, 0, M) + A_ptr; + scratch2 = L_Q16; + scratch2_ptr = Inlines.MatrixGetPointer(j + 1, 0, M); + for (i = j + 1; i < M; i++) { + tmp_32 = 0; + for (k = 0; k < j; k++) { + tmp_32 = Inlines.silk_SMLAWW(tmp_32, v_Q0[k], scratch2[scratch2_ptr + k]); + /* Q0 */ + } + tmp_32 = Inlines.silk_SUB32(scratch1[scratch1_ptr + i], tmp_32); + /* always < max(Correlation) */ + + /* tmp_32 / D_Q0[j] : Divide to Q16 */ + Inlines.MatrixSet(L_Q16, i, j, M, Inlines.silk_ADD32(Inlines.silk_SMMUL(tmp_32, one_div_diag_Q48), + Inlines.silk_RSHIFT(Inlines.silk_SMULWW(tmp_32, one_div_diag_Q36), 4))); + + /* go to next column */ + scratch2_ptr += M; + } + } + } + + Inlines.OpusAssert(status == 0); + } + + private static void silk_LS_divide_Q16( + int[] T, /* I/O Numenator vector */ + int[] inv_D, /* I 1 / D vector */ + int M /* I dimension */ + ) { + int i; + int tmp_32; + int one_div_diag_Q36, one_div_diag_Q48; + + for (i = 0; i < M; i++) { + one_div_diag_Q36 = inv_D[(i * 2) + 0]; + one_div_diag_Q48 = inv_D[(i * 2) + 1]; + + tmp_32 = T[i]; + T[i] = Inlines.silk_ADD32(Inlines.silk_SMMUL(tmp_32, one_div_diag_Q48), Inlines.silk_RSHIFT(Inlines.silk_SMULWW(tmp_32, one_div_diag_Q36), 4)); + } + } + + /* Solve Lx = b, when L is lower triangular and has ones on the diagonal */ + private static void silk_LS_SolveFirst( + int[] L_Q16, /* I Pointer to Lower Triangular Matrix */ + int M, /* I Dim of Matrix equation */ + int[] b, /* I b Vector */ + int[] x_Q16 /* O x Vector */ + ) { + int i, j; + int ptr32; + int tmp_32; + + for (i = 0; i < M; i++) { + ptr32 = Inlines.MatrixGetPointer(i, 0, M); + tmp_32 = 0; + for (j = 0; j < i; j++) { + tmp_32 = Inlines.silk_SMLAWW(tmp_32, L_Q16[ptr32 + j], x_Q16[j]); + } + x_Q16[i] = Inlines.silk_SUB32(b[i], tmp_32); + } + } + + /* Solve L^t*x = b, where L is lower triangular with ones on the diagonal */ + private static void silk_LS_SolveLast( + int[] L_Q16, /* I Pointer to Lower Triangular Matrix */ + int M, /* I Dim of Matrix equation */ + int[] b, /* I b Vector */ + int[] x_Q16 /* O x Vector */ + ) { + int i, j; + int ptr32; + int tmp_32; + + for (i = M - 1; i >= 0; i--) { + ptr32 = Inlines.MatrixGetPointer(0, i, M); + tmp_32 = 0; + for (j = M - 1; j > i; j--) { + tmp_32 = Inlines.silk_SMLAWW(tmp_32, L_Q16[ptr32 + Inlines.silk_SMULBB(j, M)], x_Q16[j]); + } + x_Q16[i] = Inlines.silk_SUB32(b[i], tmp_32); + } + } +} diff --git a/app/src/main/java/org/concentus/MDCT.java b/app/src/main/java/org/concentus/MDCT.java new file mode 100644 index 0000000000..f6772b2ef2 --- /dev/null +++ b/app/src/main/java/org/concentus/MDCT.java @@ -0,0 +1,230 @@ +/* Copyright (c) 2003-2004, Mark Borgerding + Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Modified from KISS-FFT by Jean-Marc Valin + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class MDCT { + + /* Forward MDCT trashes the input array */ + static void clt_mdct_forward(MDCTLookup l, int[] input, int input_ptr, int[] output, int output_ptr, + int[] window, int overlap, int shift, int stride) { + int i; + int N, N2, N4; + int[] f; + int[] f2; + FFTState st = l.kfft[shift]; + short[] trig; + int trig_ptr = 0; + int scale; + + int scale_shift = st.scale_shift - 1; + scale = st.scale; + + N = l.n; + trig = l.trig; + for (i = 0; i < shift; i++) { + N = N >> 1; + trig_ptr += N; + } + N2 = N >> 1; + N4 = N >> 2; + + f = new int[N2]; + f2 = new int[N4 * 2]; + + /* Consider the input to be composed of four blocks: [a, b, c, d] */ + /* Window, shuffle, fold */ + { + /* Temp pointers to make it really clear to the compiler what we're doing */ + int xp1 = input_ptr + (overlap >> 1); + int xp2 = input_ptr + N2 - 1 + (overlap >> 1); + int yp = 0; + int wp1 = (overlap >> 1); + int wp2 = ((overlap >> 1) - 1); + for (i = 0; i < ((overlap + 3) >> 2); i++) { + /* Real part arranged as -d-cR, Imag part arranged as -b+aR*/ + f[yp++] = Inlines.MULT16_32_Q15(window[wp2], input[xp1 + N2]) + Inlines.MULT16_32_Q15(window[wp1], input[xp2]); + f[yp++] = Inlines.MULT16_32_Q15(window[wp1], input[xp1]) - Inlines.MULT16_32_Q15(window[wp2], input[xp2 - N2]); + xp1 += 2; + xp2 -= 2; + wp1 += 2; + wp2 -= 2; + } + wp1 = 0; + wp2 = (overlap - 1); + for (; i < N4 - ((overlap + 3) >> 2); i++) { + /* Real part arranged as a-bR, Imag part arranged as -c-dR */ + f[yp++] = input[xp2]; + f[yp++] = input[xp1]; + xp1 += 2; + xp2 -= 2; + } + for (; i < N4; i++) { + /* Real part arranged as a-bR, Imag part arranged as -c-dR */ + f[yp++] = Inlines.MULT16_32_Q15(window[wp2], input[xp2]) - Inlines.MULT16_32_Q15(window[wp1], input[xp1 - N2]); + f[yp++] = Inlines.MULT16_32_Q15(window[wp2], input[xp1]) + Inlines.MULT16_32_Q15(window[wp1], input[xp2 + N2]); + xp1 += 2; + xp2 -= 2; + wp1 += 2; + wp2 -= 2; + } + } + /* Pre-rotation */ + { + int yp = 0; + int t = trig_ptr; + for (i = 0; i < N4; i++) { + short t0, t1; + int re, im, yr, yi; + t0 = trig[t + i]; + t1 = trig[t + N4 + i]; + re = f[yp++]; + im = f[yp++]; + yr = KissFFT.S_MUL(re, t0) - KissFFT.S_MUL(im, t1); + yi = KissFFT.S_MUL(im, t0) + KissFFT.S_MUL(re, t1); + f2[2 * st.bitrev[i]] = Inlines.PSHR32(Inlines.MULT16_32_Q16(scale, yr), scale_shift); + f2[2 * st.bitrev[i] + 1] = Inlines.PSHR32(Inlines.MULT16_32_Q16(scale, yi), scale_shift); + } + } + + /* N/4 complex FFT, does not downscale anymore */ + KissFFT.opus_fft_impl(st, f2, 0); + + /* Post-rotate */ + { + /* Temp pointers to make it really clear to the compiler what we're doing */ + int fp = 0; + int yp1 = output_ptr; + int yp2 = output_ptr + (stride * (N2 - 1)); + int t = trig_ptr; + for (i = 0; i < N4; i++) { + int yr, yi; + yr = KissFFT.S_MUL(f2[fp + 1], trig[t + N4 + i]) - KissFFT.S_MUL(f2[fp], trig[t + i]); + yi = KissFFT.S_MUL(f2[fp], trig[t + N4 + i]) + KissFFT.S_MUL(f2[fp + 1], trig[t + i]); + output[yp1] = yr; + output[yp2] = yi; + fp += 2; + yp1 += (2 * stride); + yp2 -= (2 * stride); + } + } + } + + static void clt_mdct_backward(MDCTLookup l, int[] input, int input_ptr, int[] output, int output_ptr, + int[] window, int overlap, int shift, int stride) { + int i; + int N, N2, N4; + int trig = 0; + int xp1, xp2, yp, yp0, yp1; + + N = l.n; + for (i = 0; i < shift; i++) { + N >>= 1; + trig += N; + } + N2 = N >> 1; + N4 = N >> 2; + + /* Pre-rotate */ + /* Temp pointers to make it really clear to the compiler what we're doing */ + xp2 = input_ptr + (stride * (N2 - 1)); + yp = output_ptr + (overlap >> 1); + short[] bitrev = l.kfft[shift].bitrev; + int bitrav_ptr = 0; + for (i = 0; i < N4; i++) { + int rev = bitrev[bitrav_ptr++]; + int ypr = yp + 2 * rev; + /* We swap real and imag because we use an FFT instead of an IFFT. */ + output[ypr + 1] = KissFFT.S_MUL(input[xp2], l.trig[trig + i]) + KissFFT.S_MUL(input[input_ptr], l.trig[trig + N4 + i]); //yr + output[ypr] = KissFFT.S_MUL(input[input_ptr], l.trig[trig + i]) - KissFFT.S_MUL(input[xp2], l.trig[trig + N4 + i]); //yi + /* Storing the pre-rotation directly in the bitrev order. */ + input_ptr += (2 * stride); + xp2 -= (2 * stride); + } + + KissFFT.opus_fft_impl(l.kfft[shift], output, output_ptr + (overlap >> 1)); + + /* Post-rotate and de-shuffle from both ends of the buffer at once to make + it in-place. */ + yp0 = output_ptr + (overlap >> 1); + yp1 = output_ptr + (overlap >> 1) + N2 - 2; + int t = trig; + + /* Loop to (N4+1)>>1 to handle odd N4. When N4 is odd, the + middle pair will be computed twice. */ + int tN4m1 = t + N4 - 1; + int tN2m1 = t + N2 - 1; + for (i = 0; i < (N4 + 1) >> 1; i++) { + int re, im, yr, yi; + short t0, t1; + /* We swap real and imag because we're using an FFT instead of an IFFT. */ + re = output[yp0 + 1]; + im = output[yp0]; + t0 = l.trig[t + i]; + t1 = l.trig[t + N4 + i]; + /* We'd scale up by 2 here, but instead it's done when mixing the windows */ + yr = KissFFT.S_MUL(re, t0) + KissFFT.S_MUL(im, t1); + yi = KissFFT.S_MUL(re, t1) - KissFFT.S_MUL(im, t0); + /* We swap real and imag because we're using an FFT instead of an IFFT. */ + re = output[yp1 + 1]; + im = output[yp1]; + output[yp0] = yr; + output[yp1 + 1] = yi; + t0 = l.trig[tN4m1 - i]; + t1 = l.trig[tN2m1 - i]; + /* We'd scale up by 2 here, but instead it's done when mixing the windows */ + yr = KissFFT.S_MUL(re, t0) + KissFFT.S_MUL(im, t1); + yi = KissFFT.S_MUL(re, t1) - KissFFT.S_MUL(im, t0); + output[yp1] = yr; + output[yp0 + 1] = yi; + yp0 += 2; + yp1 -= 2; + } + + /* Mirror on both sides for TDAC */ + xp1 = output_ptr + overlap - 1; + yp1 = output_ptr; + int wp1 = 0; + int wp2 = (overlap - 1); + + for (i = 0; i < overlap / 2; i++) { + int x1 = output[xp1]; + int x2 = output[yp1]; + output[yp1++] = Inlines.MULT16_32_Q15(window[wp2], x2) - Inlines.MULT16_32_Q15(window[wp1], x1); + output[xp1--] = Inlines.MULT16_32_Q15(window[wp1], x2) + Inlines.MULT16_32_Q15(window[wp2], x1); + wp1++; + wp2--; + } + } +} diff --git a/app/src/main/java/org/concentus/MDCTLookup.java b/app/src/main/java/org/concentus/MDCTLookup.java new file mode 100644 index 0000000000..b0c83c6eaa --- /dev/null +++ b/app/src/main/java/org/concentus/MDCTLookup.java @@ -0,0 +1,50 @@ +/* Copyright (c) 2003-2004, Mark Borgerding + Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Modified from KISS-FFT by Jean-Marc Valin + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class MDCTLookup { + + int n = 0; + + int maxshift = 0; + + // [porting note] these are pointers to static states defined in tables.cs + FFTState[] kfft = new FFTState[4]; + + short[] trig = null; + + MDCTLookup() { + } +} diff --git a/app/src/main/java/org/concentus/MLP.java b/app/src/main/java/org/concentus/MLP.java new file mode 100644 index 0000000000..7de1718e6f --- /dev/null +++ b/app/src/main/java/org/concentus/MLP.java @@ -0,0 +1,43 @@ +/* Copyright (c) 2008-2011 Octasic Inc. + Originally written by Jean-Marc Valin + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// state object for multi-layer perceptron +/// +class MLPState { + + int layers; + int[] topo; + float[] weights; +} diff --git a/app/src/main/java/org/concentus/MultiLayerPerceptron.java b/app/src/main/java/org/concentus/MultiLayerPerceptron.java new file mode 100644 index 0000000000..00fe1fb845 --- /dev/null +++ b/app/src/main/java/org/concentus/MultiLayerPerceptron.java @@ -0,0 +1,94 @@ +/* Copyright (c) 2008-2011 Octasic Inc. + Originally written by Jean-Marc Valin + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// multi-layer perceptron processor +/// +class MultiLayerPerceptron { + + private static final int MAX_NEURONS = 100; + + static float tansig_approx(float x) { + int i; + float y, dy; + float sign = 1; + /* Tests are reversed to catch NaNs */ + if (!(x < 8)) { + return 1; + } + if (!(x > -8)) { + return -1; + } + if (x < 0) { + x = -x; + sign = -1; + } + i = (int) Math.floor(.5f + 25 * x); + x -= .04f * i; + y = OpusTables.tansig_table[i]; + dy = 1 - y * y; + y = y + x * dy * (1 - y * x); + return sign * y; + } + + static void mlp_process(MLPState m, float[] input, float[] output) { + int j; + float[] hidden = new float[MAX_NEURONS]; + float[] W = m.weights; + int W_ptr = 0; + + /* Copy to tmp_in */ + for (j = 0; j < m.topo[1]; j++) { + int k; + float sum = W[W_ptr]; + W_ptr++; + for (k = 0; k < m.topo[0]; k++) { + sum = sum + input[k] * W[W_ptr]; + W_ptr++; + } + hidden[j] = tansig_approx(sum); + } + + for (j = 0; j < m.topo[2]; j++) { + int k; + float sum = W[W_ptr]; + W_ptr++; + for (k = 0; k < m.topo[1]; k++) { + sum = sum + hidden[k] * W[W_ptr]; + W_ptr++; + } + output[j] = tansig_approx(sum); + } + } +} diff --git a/app/src/main/java/org/concentus/NLSF.java b/app/src/main/java/org/concentus/NLSF.java new file mode 100644 index 0000000000..bf3a064ebf --- /dev/null +++ b/app/src/main/java/org/concentus/NLSF.java @@ -0,0 +1,1160 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Normalized line spectrum frequency processor +/// +class NLSF { + + private static final int MAX_STABILIZE_LOOPS = 20; + + private static final int QA = 16; + + /// + /// Number of binary divisions, when not in low complexity mode + /// + private static final int BIN_DIV_STEPS_A2NLSF = 3; + /* must be no higher than 16 - log2( LSF_COS_TAB_SZ ) */ + + private static final int MAX_ITERATIONS_A2NLSF = 30; + + /// + /// Compute quantization errors for an LPC_order element input vector for a VQ codebook + /// + /// (O) Quantization errors [K] + /// (I) Input vectors to be quantized [LPC_order] + /// (I) Codebook vectors [K*LPC_order] + /// (I) Number of codebook vectors + /// (I) Number of LPCs + static void silk_NLSF_VQ(int[] err_Q26, short[] in_Q15, short[] pCB_Q8, int K, int LPC_order) { + int diff_Q15, sum_error_Q30, sum_error_Q26; + int pCB_idx = 0; + + Inlines.OpusAssert(err_Q26 != null); + Inlines.OpusAssert(LPC_order <= 16); + Inlines.OpusAssert((LPC_order & 1) == 0); + + // Loop over codebook + for (int i = 0; i < K; i++) { + sum_error_Q26 = 0; + + for (int m = 0; m < LPC_order; m += 2) { + // Compute weighted squared quantization error for index m + diff_Q15 = Inlines.silk_SUB_LSHIFT32(in_Q15[m], pCB_Q8[pCB_idx++], 7); // range: [ -32767 : 32767 ] + sum_error_Q30 = Inlines.silk_SMULBB(diff_Q15, diff_Q15); + + // Compute weighted squared quantization error for index m + 1 + diff_Q15 = Inlines.silk_SUB_LSHIFT32(in_Q15[m + 1], pCB_Q8[pCB_idx++], 7); // range: [ -32767 : 32767 ] + sum_error_Q30 = Inlines.silk_SMLABB(sum_error_Q30, diff_Q15, diff_Q15); + + sum_error_Q26 = Inlines.silk_ADD_RSHIFT32(sum_error_Q26, sum_error_Q30, 4); + + Inlines.OpusAssert(sum_error_Q26 >= 0); + Inlines.OpusAssert(sum_error_Q30 >= 0); + } + + err_Q26[i] = sum_error_Q26; + } + } + + /// + /// Laroia low complexity NLSF weights + /// + /// (O) Pointer to input vector weights [D] + /// (I) Pointer to input vector [D] + /// (I) Input vector dimension (even) + static void silk_NLSF_VQ_weights_laroia(short[] pNLSFW_Q_OUT, short[] pNLSF_Q15, int D) { + int k; + int tmp1_int, tmp2_int; + + Inlines.OpusAssert(pNLSFW_Q_OUT != null); + Inlines.OpusAssert(D > 0); + Inlines.OpusAssert((D & 1) == 0); + + // First value + tmp1_int = Inlines.silk_max_int(pNLSF_Q15[0], 1); + tmp1_int = Inlines.silk_DIV32((int) 1 << (15 + SilkConstants.NLSF_W_Q), tmp1_int); + tmp2_int = Inlines.silk_max_int(pNLSF_Q15[1] - pNLSF_Q15[0], 1); + tmp2_int = Inlines.silk_DIV32((int) 1 << (15 + SilkConstants.NLSF_W_Q), tmp2_int); + pNLSFW_Q_OUT[0] = (short) Inlines.silk_min_int(tmp1_int + tmp2_int, Short.MAX_VALUE); + + Inlines.OpusAssert(pNLSFW_Q_OUT[0] > 0); + + // Main loop + for (k = 1; k < D - 1; k += 2) { + tmp1_int = Inlines.silk_max_int(pNLSF_Q15[k + 1] - pNLSF_Q15[k], 1); + tmp1_int = Inlines.silk_DIV32((int) 1 << (15 + SilkConstants.NLSF_W_Q), tmp1_int); + pNLSFW_Q_OUT[k] = (short) Inlines.silk_min_int(tmp1_int + tmp2_int, Short.MAX_VALUE); + Inlines.OpusAssert(pNLSFW_Q_OUT[k] > 0); + + tmp2_int = Inlines.silk_max_int(pNLSF_Q15[k + 2] - pNLSF_Q15[k + 1], 1); + tmp2_int = Inlines.silk_DIV32((int) 1 << (15 + SilkConstants.NLSF_W_Q), tmp2_int); + pNLSFW_Q_OUT[k + 1] = (short) Inlines.silk_min_int(tmp1_int + tmp2_int, Short.MAX_VALUE); + Inlines.OpusAssert(pNLSFW_Q_OUT[k + 1] > 0); + } + + // Last value + tmp1_int = Inlines.silk_max_int((1 << 15) - pNLSF_Q15[D - 1], 1); + tmp1_int = Inlines.silk_DIV32((int) 1 << (15 + SilkConstants.NLSF_W_Q), tmp1_int); + pNLSFW_Q_OUT[D - 1] = (short) Inlines.silk_min_int(tmp1_int + tmp2_int, Short.MAX_VALUE); + + Inlines.OpusAssert(pNLSFW_Q_OUT[D - 1] > 0); + } + + /// + /// Returns RD value in Q30 + /// + /// (O) Output [ order ] + /// (I) Quantization indices [ order ] + /// (I) Backward predictor coefs [ order ] + /// (I) Quantization step size + /// (I) Number of input values + static void silk_NLSF_residual_dequant( + short[] x_Q10, + byte[] indices, + int indices_ptr, + short[] pred_coef_Q8, + int quant_step_size_Q16, + short order) { + int i, pred_Q10; + short out_Q10; + + out_Q10 = 0; + for (i = order - 1; i >= 0; i--) { + pred_Q10 = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(out_Q10, (short) pred_coef_Q8[i]), 8); + out_Q10 = Inlines.silk_LSHIFT16((short) indices[indices_ptr + i], 10); + if (out_Q10 > 0) { + out_Q10 = Inlines.silk_SUB16(out_Q10, (short) (((int) ((SilkConstants.NLSF_QUANT_LEVEL_ADJ) * ((long) 1 << (10)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.NLSF_QUANT_LEVEL_ADJ, 10)*/)); + } else if (out_Q10 < 0) { + out_Q10 = Inlines.silk_ADD16(out_Q10, (short) (((int) ((SilkConstants.NLSF_QUANT_LEVEL_ADJ) * ((long) 1 << (10)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.NLSF_QUANT_LEVEL_ADJ, 10)*/)); + } + out_Q10 = (short) (Inlines.silk_SMLAWB(pred_Q10, (int) out_Q10, quant_step_size_Q16)); + x_Q10[i] = out_Q10; + } + } + + /// + /// Unpack predictor values and indices for entropy coding tables + /// + /// (O) Indices to entropy tables [ LPC_ORDER ] + /// (O) LSF predictor [ LPC_ORDER ] + /// (I) Codebook object + /// (I) Index of vector in first LSF codebook + static void silk_NLSF_unpack(short[] ec_ix, short[] pred_Q8, NLSFCodebook psNLSF_CB, int CB1_index) { + int i; + short entry; + short[] ec_sel = psNLSF_CB.ec_sel; + int ec_sel_ptr = CB1_index * psNLSF_CB.order / 2; + + for (i = 0; i < psNLSF_CB.order; i += 2) { + entry = ec_sel[ec_sel_ptr]; + ec_sel_ptr++; + ec_ix[i] = (short) (Inlines.silk_SMULBB(Inlines.silk_RSHIFT(entry, 1) & 7, 2 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE + 1)); + pred_Q8[i] = psNLSF_CB.pred_Q8[i + (entry & 1) * (psNLSF_CB.order - 1)]; + ec_ix[i + 1] = (short) (Inlines.silk_SMULBB(Inlines.silk_RSHIFT(entry, 5) & 7, 2 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE + 1)); + pred_Q8[i + 1] = psNLSF_CB.pred_Q8[i + (Inlines.silk_RSHIFT(entry, 4) & 1) * (psNLSF_CB.order - 1) + 1]; + } + } + + /// + /// NLSF stabilizer, for a single input data vector + /// + /// (I/O) Unstable/stabilized normalized LSF vector in Q15 [L] + /// (I) Min distance vector, NDeltaMin_Q15[L] must be >= 1 [L+1] + /// (I) Number of NLSF parameters in the input vector + static void silk_NLSF_stabilize(short[] NLSF_Q15, short[] NDeltaMin_Q15, int L) { + int i, I = 0, k, loops; + short center_freq_Q15; + int diff_Q15, min_diff_Q15, min_center_Q15, max_center_Q15; + + // This is necessary to ensure an output within range of a short + Inlines.OpusAssert(NDeltaMin_Q15[L] >= 1); + + for (loops = 0; loops < MAX_STABILIZE_LOOPS; loops++) { + /** + * *********************** + */ + /* Find smallest distance */ + /** + * *********************** + */ + // First element + min_diff_Q15 = NLSF_Q15[0] - NDeltaMin_Q15[0]; + I = 0; + + // Middle elements + for (i = 1; i <= L - 1; i++) { + diff_Q15 = NLSF_Q15[i] - (NLSF_Q15[i - 1] + NDeltaMin_Q15[i]); + if (diff_Q15 < min_diff_Q15) { + min_diff_Q15 = diff_Q15; + I = i; + } + } + + // Last element + diff_Q15 = (1 << 15) - (NLSF_Q15[L - 1] + NDeltaMin_Q15[L]); + if (diff_Q15 < min_diff_Q15) { + min_diff_Q15 = diff_Q15; + I = L; + } + + /** + * ************************************************ + */ + /* Now check if the smallest distance non-negative */ + /** + * ************************************************ + */ + if (min_diff_Q15 >= 0) { + return; + } + + if (I == 0) { + // Move away from lower limit + NLSF_Q15[0] = NDeltaMin_Q15[0]; + } else if (I == L) { + // Move away from higher limit + NLSF_Q15[L - 1] = (short) ((1 << 15) - NDeltaMin_Q15[L]); + } else { + // Find the lower extreme for the location of the current center frequency + min_center_Q15 = 0; + for (k = 0; k < I; k++) { + min_center_Q15 += NDeltaMin_Q15[k]; + } + + min_center_Q15 += Inlines.silk_RSHIFT(NDeltaMin_Q15[I], 1); + + // Find the upper extreme for the location of the current center frequency + max_center_Q15 = 1 << 15; + for (k = L; k > I; k--) { + max_center_Q15 -= NDeltaMin_Q15[k]; + } + + max_center_Q15 -= Inlines.silk_RSHIFT(NDeltaMin_Q15[I], 1); + + // Move apart, sorted by value, keeping the same center frequency + center_freq_Q15 = (short) (Inlines.silk_LIMIT_32(Inlines.silk_RSHIFT_ROUND((int) NLSF_Q15[I - 1] + (int) NLSF_Q15[I], 1), + min_center_Q15, max_center_Q15)); + NLSF_Q15[I - 1] = (short) (center_freq_Q15 - Inlines.silk_RSHIFT(NDeltaMin_Q15[I], 1)); + NLSF_Q15[I] = (short) (NLSF_Q15[I - 1] + NDeltaMin_Q15[I]); + } + } + + // Safe and simple fall back method, which is less ideal than the above + if (loops == MAX_STABILIZE_LOOPS) { + Sort.silk_insertion_sort_increasing_all_values_int16(NLSF_Q15, L); + + // First NLSF should be no less than NDeltaMin[0] + NLSF_Q15[0] = (short) (Inlines.silk_max_int(NLSF_Q15[0], NDeltaMin_Q15[0])); + + // Keep delta_min distance between the NLSFs + for (i = 1; i < L; i++) { + NLSF_Q15[i] = (short) (Inlines.silk_max_int(NLSF_Q15[i], NLSF_Q15[i - 1] + NDeltaMin_Q15[i])); + } + + // Last NLSF should be no higher than 1 - NDeltaMin[L] + NLSF_Q15[L - 1] = (short) (Inlines.silk_min_int(NLSF_Q15[L - 1], (1 << 15) - NDeltaMin_Q15[L])); + + // Keep NDeltaMin distance between the NLSFs + for (i = L - 2; i >= 0; i--) { + NLSF_Q15[i] = (short) (Inlines.silk_min_int(NLSF_Q15[i], NLSF_Q15[i + 1] - NDeltaMin_Q15[i + 1])); + } + } + } + + /// + /// NLSF vector decoder + /// + /// (O) Quantized NLSF vector [ LPC_ORDER ] + /// (I) Codebook path vector [ LPC_ORDER + 1 ] + /// (I) Codebook object + static void silk_NLSF_decode(short[] pNLSF_Q15, byte[] NLSFIndices, NLSFCodebook psNLSF_CB) { + int i; + short[] pred_Q8 = new short[psNLSF_CB.order]; + short[] ec_ix = new short[psNLSF_CB.order]; + short[] res_Q10 = new short[psNLSF_CB.order]; + short[] W_tmp_QW = new short[psNLSF_CB.order]; + int W_tmp_Q9, NLSF_Q15_tmp; + + // Decode first stage + short[] pCB = psNLSF_CB.CB1_NLSF_Q8; + int pCB_element = NLSFIndices[0] * psNLSF_CB.order; + + for (i = 0; i < psNLSF_CB.order; i++) { + pNLSF_Q15[i] = Inlines.silk_LSHIFT16((short) pCB[pCB_element + i], 7); + } + + // Unpack entropy table indices and predictor for current CB1 index + silk_NLSF_unpack(ec_ix, pred_Q8, psNLSF_CB, NLSFIndices[0]); + + // Predictive residual dequantizer + silk_NLSF_residual_dequant(res_Q10, + NLSFIndices, + 1, + pred_Q8, + psNLSF_CB.quantStepSize_Q16, + psNLSF_CB.order); + + // Weights from codebook vector + silk_NLSF_VQ_weights_laroia(W_tmp_QW, pNLSF_Q15, psNLSF_CB.order); + + // Apply inverse square-rooted weights and add to output + for (i = 0; i < psNLSF_CB.order; i++) { + W_tmp_Q9 = Inlines.silk_SQRT_APPROX(Inlines.silk_LSHIFT((int) W_tmp_QW[i], 18 - SilkConstants.NLSF_W_Q)); + NLSF_Q15_tmp = Inlines.silk_ADD32(pNLSF_Q15[i], Inlines.silk_DIV32_16(Inlines.silk_LSHIFT((int) res_Q10[i], 14), (short) (W_tmp_Q9))); + pNLSF_Q15[i] = (short) (Inlines.silk_LIMIT(NLSF_Q15_tmp, 0, 32767)); + } + + // NLSF stabilization + silk_NLSF_stabilize(pNLSF_Q15, psNLSF_CB.deltaMin_Q15, psNLSF_CB.order); + } + + /// + /// Delayed-decision quantizer for NLSF residuals + /// + /// (O) Quantization indices [ order ] + /// (O) Input [ order ] + /// (I) Weights [ order ] + /// (I) Backward predictor coefs [ order ] + /// (I) Indices to entropy coding tables [ order ] + /// (I) Rates [] + /// (I) Quantization step size + /// (I) Inverse quantization step size + /// (I) R/D tradeoff + /// (I) Number of input values + /// RD value in Q25 + /// Fixme: Optimize this method! + static int silk_NLSF_del_dec_quant( + byte[] indices, + short[] x_Q10, + short[] w_Q5, + short[] pred_coef_Q8, + short[] ec_ix, + short[] ec_rates_Q5, + int quant_step_size_Q16, + short inv_quant_step_size_Q6, + int mu_Q20, + short order) { + int i, j, nStates, ind_tmp, ind_min_max, ind_max_min, in_Q10, res_Q10; + int pred_Q10, diff_Q10, out0_Q10, out1_Q10, rate0_Q5, rate1_Q5; + int RD_tmp_Q25, min_Q25, min_max_Q25, max_min_Q25, pred_coef_Q16; + int[] ind_sort = new int[SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + byte[][] ind = new byte[SilkConstants.NLSF_QUANT_DEL_DEC_STATES][]; + for (i = 0; i < SilkConstants.NLSF_QUANT_DEL_DEC_STATES; i++) { + ind[i] = new byte[SilkConstants.MAX_LPC_ORDER]; + } + + short[] prev_out_Q10 = new short[2 * SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + int[] RD_Q25 = new int[2 * SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + int[] RD_min_Q25 = new int[SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + int[] RD_max_Q25 = new int[SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + int rates_Q5; + + int[] out0_Q10_table = new int[2 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT]; + int[] out1_Q10_table = new int[2 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT]; + + for (i = 0 - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT; i <= SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT - 1; i++) { + out0_Q10 = Inlines.silk_LSHIFT(i, 10); + out1_Q10 = Inlines.silk_ADD16((short) (out0_Q10), (short) 1024); + + if (i > 0) { + out0_Q10 = Inlines.silk_SUB16((short) (out0_Q10), (short) (((int) ((SilkConstants.NLSF_QUANT_LEVEL_ADJ) * ((long) 1 << (10)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.NLSF_QUANT_LEVEL_ADJ, 10)*/)); + out1_Q10 = Inlines.silk_SUB16((short) (out1_Q10), (short) (((int) ((SilkConstants.NLSF_QUANT_LEVEL_ADJ) * ((long) 1 << (10)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.NLSF_QUANT_LEVEL_ADJ, 10)*/)); + } else if (i == 0) { + out1_Q10 = Inlines.silk_SUB16((short) (out1_Q10), (short) (((int) ((SilkConstants.NLSF_QUANT_LEVEL_ADJ) * ((long) 1 << (10)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.NLSF_QUANT_LEVEL_ADJ, 10)*/)); + } else if (i == -1) { + out0_Q10 = Inlines.silk_ADD16((short) (out0_Q10), (short) (((int) ((SilkConstants.NLSF_QUANT_LEVEL_ADJ) * ((long) 1 << (10)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.NLSF_QUANT_LEVEL_ADJ, 10)*/)); + } else { + out0_Q10 = Inlines.silk_ADD16((short) (out0_Q10), (short) (((int) ((SilkConstants.NLSF_QUANT_LEVEL_ADJ) * ((long) 1 << (10)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.NLSF_QUANT_LEVEL_ADJ, 10)*/)); + out1_Q10 = Inlines.silk_ADD16((short) (out1_Q10), (short) (((int) ((SilkConstants.NLSF_QUANT_LEVEL_ADJ) * ((long) 1 << (10)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.NLSF_QUANT_LEVEL_ADJ, 10)*/)); + } + + out0_Q10_table[i + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT] = Inlines.silk_SMULWB((int) out0_Q10, quant_step_size_Q16); + out1_Q10_table[i + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT] = Inlines.silk_SMULWB((int) out1_Q10, quant_step_size_Q16); + } + + Inlines.OpusAssert((SilkConstants.NLSF_QUANT_DEL_DEC_STATES & (SilkConstants.NLSF_QUANT_DEL_DEC_STATES - 1)) == 0); // must be power of two + + nStates = 1; + RD_Q25[0] = 0; + prev_out_Q10[0] = 0; + + for (i = order - 1;; i--) { + pred_coef_Q16 = Inlines.silk_LSHIFT((int) pred_coef_Q8[i], 8); + in_Q10 = x_Q10[i]; + + for (j = 0; j < nStates; j++) { + pred_Q10 = Inlines.silk_SMULWB(pred_coef_Q16, prev_out_Q10[j]); + res_Q10 = Inlines.silk_SUB16((short) (in_Q10), (short) (pred_Q10)); + ind_tmp = Inlines.silk_SMULWB((int) inv_quant_step_size_Q6, res_Q10); + ind_tmp = Inlines.silk_LIMIT(ind_tmp, 0 - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT, SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT - 1); + ind[j][i] = (byte) ind_tmp; + rates_Q5 = ec_ix[i] + ind_tmp; + + // compute outputs for ind_tmp and ind_tmp + 1 + out0_Q10 = out0_Q10_table[ind_tmp + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT]; + out1_Q10 = out1_Q10_table[ind_tmp + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT]; + + out0_Q10 = Inlines.silk_ADD16((short) (out0_Q10), (short) (pred_Q10)); + out1_Q10 = Inlines.silk_ADD16((short) (out1_Q10), (short) (pred_Q10)); + prev_out_Q10[j] = (short) (out0_Q10); + prev_out_Q10[j + nStates] = (short) (out1_Q10); + + // compute RD for ind_tmp and ind_tmp + 1 + if (ind_tmp + 1 >= SilkConstants.NLSF_QUANT_MAX_AMPLITUDE) { + if (ind_tmp + 1 == SilkConstants.NLSF_QUANT_MAX_AMPLITUDE) { + rate0_Q5 = ec_rates_Q5[rates_Q5 + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE]; + rate1_Q5 = 280; + } else { + rate0_Q5 = Inlines.silk_SMLABB(280 - (43 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE), 43, ind_tmp); + rate1_Q5 = Inlines.silk_ADD16((short) (rate0_Q5), (short) 43); + } + } else if (ind_tmp <= 0 - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE) { + if (ind_tmp == 0 - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE) { + rate0_Q5 = 280; + rate1_Q5 = ec_rates_Q5[rates_Q5 + 1 + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE]; + } else { + rate0_Q5 = Inlines.silk_SMLABB(280 - 43 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE, -43, ind_tmp); + rate1_Q5 = Inlines.silk_SUB16((short) (rate0_Q5), (short) 43); + } + } else { + rate0_Q5 = ec_rates_Q5[rates_Q5 + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE]; + rate1_Q5 = ec_rates_Q5[rates_Q5 + 1 + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE]; + } + + RD_tmp_Q25 = RD_Q25[j]; + diff_Q10 = Inlines.silk_SUB16((short) (in_Q10), (short) (out0_Q10)); + RD_Q25[j] = Inlines.silk_SMLABB(Inlines.silk_MLA(RD_tmp_Q25, Inlines.silk_SMULBB(diff_Q10, diff_Q10), w_Q5[i]), mu_Q20, rate0_Q5); + diff_Q10 = Inlines.silk_SUB16((short) (in_Q10), (short) (out1_Q10)); + RD_Q25[j + nStates] = Inlines.silk_SMLABB(Inlines.silk_MLA(RD_tmp_Q25, Inlines.silk_SMULBB(diff_Q10, diff_Q10), w_Q5[i]), mu_Q20, rate1_Q5); + } + + if (nStates <= (SilkConstants.NLSF_QUANT_DEL_DEC_STATES >> 1)) { + // double number of states and copy + for (j = 0; j < nStates; j++) { + ind[j + nStates][i] = (byte) (ind[j][i] + 1); + } + nStates = Inlines.silk_LSHIFT(nStates, 1); + + for (j = nStates; j < SilkConstants.NLSF_QUANT_DEL_DEC_STATES; j++) { + ind[j][i] = ind[j - nStates][i]; + } + } else if (i > 0) { + // sort lower and upper half of RD_Q25, pairwise + for (j = 0; j < SilkConstants.NLSF_QUANT_DEL_DEC_STATES; j++) { + if (RD_Q25[j] > RD_Q25[j + SilkConstants.NLSF_QUANT_DEL_DEC_STATES]) { + RD_max_Q25[j] = RD_Q25[j]; + RD_min_Q25[j] = RD_Q25[j + SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + RD_Q25[j] = RD_min_Q25[j]; + RD_Q25[j + SilkConstants.NLSF_QUANT_DEL_DEC_STATES] = RD_max_Q25[j]; + + // swap prev_out values + out0_Q10 = prev_out_Q10[j]; + prev_out_Q10[j] = prev_out_Q10[j + SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + prev_out_Q10[j + SilkConstants.NLSF_QUANT_DEL_DEC_STATES] = (short) (out0_Q10); + ind_sort[j] = j + SilkConstants.NLSF_QUANT_DEL_DEC_STATES; + } else { + RD_min_Q25[j] = RD_Q25[j]; + RD_max_Q25[j] = RD_Q25[j + SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + ind_sort[j] = j; + } + } + + // compare the highest RD values of the winning half with the lowest one in the losing half, and copy if necessary + // afterwards ind_sort[] will contain the indices of the NLSF_QUANT_DEL_DEC_STATES winning RD values + while (true) { + min_max_Q25 = Integer.MAX_VALUE; + max_min_Q25 = 0; + ind_min_max = 0; + ind_max_min = 0; + + for (j = 0; j < SilkConstants.NLSF_QUANT_DEL_DEC_STATES; j++) { + if (min_max_Q25 > RD_max_Q25[j]) { + min_max_Q25 = RD_max_Q25[j]; + ind_min_max = j; + } + if (max_min_Q25 < RD_min_Q25[j]) { + max_min_Q25 = RD_min_Q25[j]; + ind_max_min = j; + } + } + + if (min_max_Q25 >= max_min_Q25) { + break; + } + + // copy ind_min_max to ind_max_min + ind_sort[ind_max_min] = ind_sort[ind_min_max] ^ SilkConstants.NLSF_QUANT_DEL_DEC_STATES; + RD_Q25[ind_max_min] = RD_Q25[ind_min_max + SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + prev_out_Q10[ind_max_min] = prev_out_Q10[ind_min_max + SilkConstants.NLSF_QUANT_DEL_DEC_STATES]; + RD_min_Q25[ind_max_min] = 0; + RD_max_Q25[ind_min_max] = Integer.MAX_VALUE; + System.arraycopy(ind[ind_min_max], 0, ind[ind_max_min], 0, order); + } + + // increment index if it comes from the upper half + for (j = 0; j < SilkConstants.NLSF_QUANT_DEL_DEC_STATES; j++) { + byte x = (byte) Inlines.silk_RSHIFT(ind_sort[j], SilkConstants.NLSF_QUANT_DEL_DEC_STATES_LOG2); + ind[j][i] += x; + } + } else { + // i == 0 + break; + } + } + + // last sample: find winner, copy indices and return RD value + ind_tmp = 0; + min_Q25 = Integer.MAX_VALUE; + for (j = 0; j < 2 * SilkConstants.NLSF_QUANT_DEL_DEC_STATES; j++) { + if (min_Q25 > RD_Q25[j]) { + min_Q25 = RD_Q25[j]; + ind_tmp = j; + } + } + + for (j = 0; j < order; j++) { + indices[j] = ind[ind_tmp & (SilkConstants.NLSF_QUANT_DEL_DEC_STATES - 1)][j]; + Inlines.OpusAssert(indices[j] >= 0 - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT); + Inlines.OpusAssert(indices[j] <= SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT); + } + + indices[0] = (byte) (indices[0] + Inlines.silk_RSHIFT(ind_tmp, SilkConstants.NLSF_QUANT_DEL_DEC_STATES_LOG2)); + Inlines.OpusAssert(indices[0] <= SilkConstants.NLSF_QUANT_MAX_AMPLITUDE_EXT); + Inlines.OpusAssert(min_Q25 >= 0); + return min_Q25; + } + + /// + /// NLSF vector encoder + /// + /// (I) Codebook path vector [ LPC_ORDER + 1 ] + /// (I/O) Quantized NLSF vector [ LPC_ORDER ] + /// (I) Codebook object + /// (I) NLSF weight vector [ LPC_ORDER ] + /// (I) Rate weight for the RD optimization + /// (I) Max survivors after first stage + /// (I) Signal type: 0/1/2 + /// RD value in Q25 + static int silk_NLSF_encode( + byte[] NLSFIndices, + short[] pNLSF_Q15, + NLSFCodebook psNLSF_CB, + short[] pW_QW, + int NLSF_mu_Q20, + int nSurvivors, + int signalType) { + int i, s, ind1, prob_Q8, bits_q7; + int W_tmp_Q9; + int[] err_Q26; + int[] RD_Q25; + int[] tempIndices1; + byte[][] tempIndices2; + short[] res_Q15 = new short[psNLSF_CB.order]; + short[] res_Q10 = new short[psNLSF_CB.order]; + short[] NLSF_tmp_Q15 = new short[psNLSF_CB.order]; + short[] W_tmp_QW = new short[psNLSF_CB.order]; + short[] W_adj_Q5 = new short[psNLSF_CB.order]; + short[] pred_Q8 = new short[psNLSF_CB.order]; + short[] ec_ix = new short[psNLSF_CB.order]; + short[] pCB = psNLSF_CB.CB1_NLSF_Q8; + int iCDF_ptr; + int pCB_element; + + Inlines.OpusAssert(nSurvivors <= SilkConstants.NLSF_VQ_MAX_SURVIVORS); + Inlines.OpusAssert(signalType >= 0 && signalType <= 2); + Inlines.OpusAssert(NLSF_mu_Q20 <= 32767 && NLSF_mu_Q20 >= 0); + + // NLSF stabilization + silk_NLSF_stabilize(pNLSF_Q15, psNLSF_CB.deltaMin_Q15, psNLSF_CB.order); + + // First stage: VQ + err_Q26 = new int[psNLSF_CB.nVectors]; + silk_NLSF_VQ(err_Q26, pNLSF_Q15, psNLSF_CB.CB1_NLSF_Q8, psNLSF_CB.nVectors, psNLSF_CB.order); + + // Sort the quantization errors + tempIndices1 = new int[nSurvivors]; + Sort.silk_insertion_sort_increasing(err_Q26, tempIndices1, psNLSF_CB.nVectors, nSurvivors); + + RD_Q25 = new int[nSurvivors]; + tempIndices2 = Arrays.InitTwoDimensionalArrayByte(nSurvivors, SilkConstants.MAX_LPC_ORDER); + + // Loop over survivors + for (s = 0; s < nSurvivors; s++) { + ind1 = tempIndices1[s]; + + // Residual after first stage + pCB_element = ind1 * psNLSF_CB.order; // opt: potential 1:2 partitioned buffer + for (i = 0; i < psNLSF_CB.order; i++) { + NLSF_tmp_Q15[i] = Inlines.silk_LSHIFT16((short) pCB[pCB_element + i], 7); + res_Q15[i] = (short) (pNLSF_Q15[i] - NLSF_tmp_Q15[i]); + } + + // Weights from codebook vector + silk_NLSF_VQ_weights_laroia(W_tmp_QW, NLSF_tmp_Q15, psNLSF_CB.order); + + // Apply square-rooted weights + for (i = 0; i < psNLSF_CB.order; i++) { + W_tmp_Q9 = Inlines.silk_SQRT_APPROX(Inlines.silk_LSHIFT((int) W_tmp_QW[i], 18 - SilkConstants.NLSF_W_Q)); + res_Q10[i] = (short) Inlines.silk_RSHIFT(Inlines.silk_SMULBB(res_Q15[i], W_tmp_Q9), 14); + } + + // Modify input weights accordingly + for (i = 0; i < psNLSF_CB.order; i++) { + W_adj_Q5[i] = (short) (Inlines.silk_DIV32_16(Inlines.silk_LSHIFT((int) pW_QW[i], 5), W_tmp_QW[i])); + } + + // Unpack entropy table indices and predictor for current CB1 index + silk_NLSF_unpack(ec_ix, pred_Q8, psNLSF_CB, ind1); + + // Trellis quantizer + RD_Q25[s] = silk_NLSF_del_dec_quant( + tempIndices2[s], + res_Q10, + W_adj_Q5, + pred_Q8, + ec_ix, + psNLSF_CB.ec_Rates_Q5, + psNLSF_CB.quantStepSize_Q16, + psNLSF_CB.invQuantStepSize_Q6, + NLSF_mu_Q20, + psNLSF_CB.order); + + // Add rate for first stage + iCDF_ptr = (signalType >> 1) * psNLSF_CB.nVectors; + + if (ind1 == 0) { + prob_Q8 = 256 - psNLSF_CB.CB1_iCDF[iCDF_ptr + ind1]; + } else { + prob_Q8 = psNLSF_CB.CB1_iCDF[iCDF_ptr + ind1 - 1] - psNLSF_CB.CB1_iCDF[iCDF_ptr + ind1]; + } + + bits_q7 = (8 << 7) - Inlines.silk_lin2log(prob_Q8); + RD_Q25[s] = Inlines.silk_SMLABB(RD_Q25[s], bits_q7, Inlines.silk_RSHIFT(NLSF_mu_Q20, 2)); + } + + // Find the lowest rate-distortion error + int[] bestIndex = new int[1]; + Sort.silk_insertion_sort_increasing(RD_Q25, bestIndex, nSurvivors, 1); + + NLSFIndices[0] = (byte) tempIndices1[bestIndex[0]]; + System.arraycopy(tempIndices2[bestIndex[0]], 0, NLSFIndices, 1, psNLSF_CB.order); + + // Decode + silk_NLSF_decode(pNLSF_Q15, NLSFIndices, psNLSF_CB); + + return RD_Q25[0]; + } + + /// + /// helper function for NLSF2A(..) + /// + /// (O) intermediate polynomial, QA [dd+1] + /// (I) vector of interleaved 2*cos(LSFs), QA [d] + /// (I) polynomial order (= 1/2 * filter order) + static void silk_NLSF2A_find_poly( + int[] o, + int[] cLSF, + int cLSF_ptr, + int dd) { + int k, n, ftmp; + + o[0] = Inlines.silk_LSHIFT(1, QA); + o[1] = 0 - cLSF[cLSF_ptr]; + for (k = 1; k < dd; k++) { + ftmp = cLSF[cLSF_ptr + (2 * k)]; + /* QA*/ + o[k + 1] = Inlines.silk_LSHIFT(o[k - 1], 1) - (int) Inlines.silk_RSHIFT_ROUND64(Inlines.silk_SMULL(ftmp, o[k]), QA); + for (n = k; n > 1; n--) { + o[n] += o[n - 2] - (int) Inlines.silk_RSHIFT_ROUND64(Inlines.silk_SMULL(ftmp, o[n - 1]), QA); + } + o[1] -= ftmp; + } + } + + /* This ordering was found to maximize quality. It improves numerical accuracy of + silk_NLSF2A_find_poly() compared to "standard" ordering. */ + private static final byte[] ordering16 = {0, 15, 8, 7, 4, 11, 12, 3, 2, 13, 10, 5, 6, 9, 14, 1}; + private static final byte[] ordering10 = {0, 9, 6, 3, 4, 5, 8, 1, 2, 7}; + + /// + /// compute whitening filter coefficients from normalized line spectral frequencies + /// + /// (O) monic whitening filter coefficients in Q12, [ d ] + /// (I) normalized line spectral frequencies in Q15, [ d ] + /// (I) filter order (should be even) + static void silk_NLSF2A( + short[] a_Q12, + short[] NLSF, + int d) { + + byte[] ordering; + int k, i, dd; + int[] cos_LSF_QA = new int[d]; + int[] P = new int[d / 2 + 1]; + int[] Q = new int[d / 2 + 1]; + int[] a32_QA1 = new int[d]; + + int Ptmp, Qtmp, f_int, f_frac, cos_val, delta; + int maxabs, absval, idx = 0, sc_Q16; + + Inlines.OpusAssert(SilkConstants.LSF_COS_TAB_SZ == 128); + Inlines.OpusAssert(d == 10 || d == 16); + + /* convert LSFs to 2*cos(LSF), using piecewise linear curve from table */ + ordering = d == 16 ? ordering16 : ordering10; + + for (k = 0; k < d; k++) { + Inlines.OpusAssert(NLSF[k] >= 0); + + /* f_int on a scale 0-127 (rounded down) */ + f_int = Inlines.silk_RSHIFT(NLSF[k], 15 - 7); + + /* f_frac, range: 0..255 */ + f_frac = NLSF[k] - Inlines.silk_LSHIFT(f_int, 15 - 7); + + Inlines.OpusAssert(f_int >= 0); + Inlines.OpusAssert(f_int < SilkConstants.LSF_COS_TAB_SZ); + + /* Read start and end value from table */ + cos_val = SilkTables.silk_LSFCosTab_Q12[f_int]; + /* Q12 */ + delta = SilkTables.silk_LSFCosTab_Q12[f_int + 1] - cos_val; + /* Q12, with a range of 0..200 */ + + /* Linear interpolation */ + cos_LSF_QA[ordering[k]] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_LSHIFT(cos_val, 8) + Inlines.silk_MUL(delta, f_frac), 20 - QA); + /* QA */ + } + + dd = Inlines.silk_RSHIFT(d, 1); + + /* generate even and odd polynomials using convolution */ + silk_NLSF2A_find_poly(P, cos_LSF_QA, 0, dd); + silk_NLSF2A_find_poly(Q, cos_LSF_QA, 1, dd); + + /* convert even and odd polynomials to opus_int32 Q12 filter coefs */ + for (k = 0; k < dd; k++) { + Ptmp = P[k + 1] + P[k]; + Qtmp = Q[k + 1] - Q[k]; + + /* the Ptmp and Qtmp values at this stage need to fit in int32 */ + a32_QA1[k] = -Qtmp - Ptmp; + /* QA+1 */ + a32_QA1[d - k - 1] = Qtmp - Ptmp; + /* QA+1 */ + } + + /* Limit the maximum absolute value of the prediction coefficients, so that they'll fit in int16 */ + for (i = 0; i < 10; i++) { + /* Find maximum absolute value and its index */ + maxabs = 0; + for (k = 0; k < d; k++) { + absval = Inlines.silk_abs(a32_QA1[k]); + if (absval > maxabs) { + maxabs = absval; + idx = k; + } + } + + maxabs = Inlines.silk_RSHIFT_ROUND(maxabs, QA + 1 - 12); + /* QA+1 . Q12 */ + + if (maxabs > Short.MAX_VALUE) { + /* Reduce magnitude of prediction coefficients */ + maxabs = Inlines.silk_min(maxabs, 163838); + /* ( silk_int32_MAX >> 14 ) + silk_int16_MAX = 163838 */ + sc_Q16 = ((int) ((0.999f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.999f, 16)*/ - Inlines.silk_DIV32(Inlines.silk_LSHIFT(maxabs - Short.MAX_VALUE, 14), + Inlines.silk_RSHIFT32(Inlines.silk_MUL(maxabs, idx + 1), 2)); + Filters.silk_bwexpander_32(a32_QA1, d, sc_Q16); + } else { + break; + } + } + + if (i == 10) { + /* Reached the last iteration, clip the coefficients */ + for (k = 0; k < d; k++) { + a_Q12[k] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(a32_QA1[k], QA + 1 - 12)); + /* QA+1 . Q12 */ + a32_QA1[k] = Inlines.silk_LSHIFT((int) a_Q12[k], QA + 1 - 12); + } + } else { + for (k = 0; k < d; k++) { + a_Q12[k] = (short) Inlines.silk_RSHIFT_ROUND(a32_QA1[k], QA + 1 - 12); + /* QA+1 . Q12 */ + } + } + + for (i = 0; i < SilkConstants.MAX_LPC_STABILIZE_ITERATIONS; i++) { + if (Filters.silk_LPC_inverse_pred_gain(a_Q12, d) < ((int) ((1.0f / SilkConstants.MAX_PREDICTION_POWER_GAIN) * ((long) 1 << (30)) + 0.5))/*Inlines.SILK_CONST(1.0f / SilkConstants.MAX_PREDICTION_POWER_GAIN, 30)*/) { + /* Prediction coefficients are (too close to) unstable; apply bandwidth expansion */ + /* on the unscaled coefficients, convert to Q12 and measure again */ + Filters.silk_bwexpander_32(a32_QA1, d, 65536 - Inlines.silk_LSHIFT(2, i)); + + for (k = 0; k < d; k++) { + a_Q12[k] = (short) Inlines.silk_RSHIFT_ROUND(a32_QA1[k], QA + 1 - 12); + /* QA+1 . Q12 */ + } + } else { + break; + } + } + } + + /// + /// Helper function for A2NLSF(..) Transforms polynomials from cos(n*f) to cos(f)^n + /// + /// (I/O) Polynomial + /// (I) Polynomial order (= filter order / 2 ) + static void silk_A2NLSF_trans_poly(int[] p, int dd) { + int k, n; + + for (k = 2; k <= dd; k++) { + for (n = dd; n > k; n--) { + p[n - 2] -= p[n]; + } + p[k - 2] -= Inlines.silk_LSHIFT(p[k], 1); + } + } + + /// + /// Helper function for A2NLSF(..) Polynomial evaluation + /// + /// (I) Polynomial, Q16 + /// (I) Evaluation point, Q12 + /// (I) Order + /// the polynomial evaluation, in Q16 + static int silk_A2NLSF_eval_poly(int[] p, int x, int dd) { + int n; + int x_Q16, y32; + + y32 = p[dd]; + /* Q16 */ + x_Q16 = Inlines.silk_LSHIFT(x, 4); + + if (8 == dd) { + y32 = Inlines.silk_SMLAWW(p[7], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[6], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[5], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[4], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[3], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[2], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[1], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[0], y32, x_Q16); + } else { + for (n = dd - 1; n >= 0; n--) { + y32 = Inlines.silk_SMLAWW(p[n], y32, x_Q16); + /* Q16 */ + } + } + + return y32; + } + + static void silk_A2NLSF_init( + int[] a_Q16, + int[] P, + int[] Q, + int dd) { + int k; + + /* Convert filter coefs to even and odd polynomials */ + P[dd] = Inlines.silk_LSHIFT(1, 16); + Q[dd] = Inlines.silk_LSHIFT(1, 16); + for (k = 0; k < dd; k++) { + P[k] = -a_Q16[dd - k - 1] - a_Q16[dd + k]; + /* Q16 */ + Q[k] = -a_Q16[dd - k - 1] + a_Q16[dd + k]; + /* Q16 */ + } + + /* Divide out zeros as we have that for even filter orders, */ + /* z = 1 is always a root in Q, and */ + /* z = -1 is always a root in P */ + for (k = dd; k > 0; k--) { + P[k - 1] -= P[k]; + Q[k - 1] += Q[k]; + } + + /* Transform polynomials from cos(n*f) to cos(f)^n */ + silk_A2NLSF_trans_poly(P, dd); + silk_A2NLSF_trans_poly(Q, dd); + } + + /// + /// Compute Normalized Line Spectral Frequencies (NLSFs) from whitening filter coefficients + /// If not all roots are found, the a_Q16 coefficients are bandwidth expanded until convergence. + /// + /// (O) Normalized Line Spectral Frequencies in Q15 (0..2^15-1) [d] + /// (I/O) Monic whitening filter coefficients in Q16 [d] + /// (I) Filter order (must be even) + static void silk_A2NLSF(short[] NLSF, int[] a_Q16, int d) { + int i, k, m, dd, root_ix, ffrac; + int xlo, xhi, xmid; + int ylo, yhi, ymid, thr; + int nom, den; + int[] P = new int[SilkConstants.SILK_MAX_ORDER_LPC / 2 + 1]; + int[] Q = new int[SilkConstants.SILK_MAX_ORDER_LPC / 2 + 1]; + int[][] PQ = new int[2][]; + int[] p; + + /* Store pointers to array */ + PQ[0] = P; + PQ[1] = Q; + + dd = Inlines.silk_RSHIFT(d, 1); + + silk_A2NLSF_init(a_Q16, P, Q, dd); + + /* Find roots, alternating between P and Q */ + p = P; + /* Pointer to polynomial */ + + xlo = SilkTables.silk_LSFCosTab_Q12[0]; + /* Q12*/ + ylo = silk_A2NLSF_eval_poly(p, xlo, dd); + + if (ylo < 0) { + /* Set the first NLSF to zero and move on to the next */ + NLSF[0] = 0; + p = Q; + /* Pointer to polynomial */ + ylo = silk_A2NLSF_eval_poly(p, xlo, dd); + root_ix = 1; + /* Index of current root */ + } else { + root_ix = 0; + /* Index of current root */ + } + k = 1; + /* Loop counter */ + i = 0; + /* Counter for bandwidth expansions applied */ + thr = 0; + while (true) { + /* Evaluate polynomial */ + xhi = SilkTables.silk_LSFCosTab_Q12[k]; + /* Q12 */ + yhi = silk_A2NLSF_eval_poly(p, xhi, dd); + + /* Detect zero crossing */ + if ((ylo <= 0 && yhi >= thr) || (ylo >= 0 && yhi <= -thr)) { + if (yhi == 0) { + /* If the root lies exactly at the end of the current */ + /* interval, look for the next root in the next interval */ + thr = 1; + } else { + thr = 0; + } + /* Binary division */ + ffrac = -256; + for (m = 0; m < BIN_DIV_STEPS_A2NLSF; m++) { + /* Evaluate polynomial */ + xmid = Inlines.silk_RSHIFT_ROUND(xlo + xhi, 1); + ymid = silk_A2NLSF_eval_poly(p, xmid, dd); + + /* Detect zero crossing */ + if ((ylo <= 0 && ymid >= 0) || (ylo >= 0 && ymid <= 0)) { + /* Reduce frequency */ + xhi = xmid; + yhi = ymid; + } else { + /* Increase frequency */ + xlo = xmid; + ylo = ymid; + ffrac = Inlines.silk_ADD_RSHIFT(ffrac, 128, m); + } + } + + /* Interpolate */ + if (Inlines.silk_abs(ylo) < 65536) { + /* Avoid dividing by zero */ + den = ylo - yhi; + nom = Inlines.silk_LSHIFT(ylo, 8 - BIN_DIV_STEPS_A2NLSF) + Inlines.silk_RSHIFT(den, 1); + if (den != 0) { + ffrac += Inlines.silk_DIV32(nom, den); + } + } else { + /* No risk of dividing by zero because abs(ylo - yhi) >= abs(ylo) >= 65536 */ + ffrac += Inlines.silk_DIV32(ylo, Inlines.silk_RSHIFT(ylo - yhi, 8 - BIN_DIV_STEPS_A2NLSF)); + } + NLSF[root_ix] = (short) Inlines.silk_min_32(Inlines.silk_LSHIFT((int) k, 8) + ffrac, Short.MAX_VALUE); + + Inlines.OpusAssert(NLSF[root_ix] >= 0); + + root_ix++; + /* Next root */ + if (root_ix >= d) { + /* Found all roots */ + break; + } + + /* Alternate pointer to polynomial */ + p = PQ[root_ix & 1]; + + /* Evaluate polynomial */ + xlo = SilkTables.silk_LSFCosTab_Q12[k - 1]; + /* Q12*/ + ylo = Inlines.silk_LSHIFT(1 - (root_ix & 2), 12); + } else { + /* Increment loop counter */ + k++; + xlo = xhi; + ylo = yhi; + thr = 0; + + if (k > SilkConstants.LSF_COS_TAB_SZ) { + i++; + if (i > MAX_ITERATIONS_A2NLSF) { + /* Set NLSFs to white spectrum and exit */ + NLSF[0] = (short) Inlines.silk_DIV32_16(1 << 15, (short) (d + 1)); + for (k = 1; k < d; k++) { + NLSF[k] = (short) Inlines.silk_SMULBB(k + 1, NLSF[0]); + } + return; + } + + /* Error: Apply progressively more bandwidth expansion and run again */ + Filters.silk_bwexpander_32(a_Q16, d, 65536 - Inlines.silk_SMULBB(10 + i, i)); + /* 10_Q16 = 0.00015*/ + + silk_A2NLSF_init(a_Q16, P, Q, dd); + p = P; + /* Pointer to polynomial */ + xlo = SilkTables.silk_LSFCosTab_Q12[0]; + /* Q12*/ + ylo = silk_A2NLSF_eval_poly(p, xlo, dd); + if (ylo < 0) { + /* Set the first NLSF to zero and move on to the next */ + NLSF[0] = 0; + p = Q; + /* Pointer to polynomial */ + ylo = silk_A2NLSF_eval_poly(p, xlo, dd); + root_ix = 1; + /* Index of current root */ + } else { + root_ix = 0; + /* Index of current root */ + } + k = 1; + /* Reset loop counter */ + } + } + } + } + + /// + /// Limit, stabilize, convert and quantize NLSFs + /// + /// I/O Encoder state + /// O Prediction coefficients [ 2 ][MAX_LPC_ORDER] + /// I/O Normalized LSFs (quant out) (0 - (2^15-1)) [MAX_LPC_ORDER] + /// I Previous Normalized LSFs (0 - (2^15-1)) [MAX_LPC_ORDER] + static void silk_process_NLSFs( + SilkChannelEncoder psEncC, + short[][] PredCoef_Q12, + short[] pNLSF_Q15, + short[] prev_NLSFq_Q15) { + int i; + boolean doInterpolate; + int NLSF_mu_Q20; + int i_sqr_Q15; + short[] pNLSF0_temp_Q15 = new short[SilkConstants.MAX_LPC_ORDER]; + short[] pNLSFW_QW = new short[SilkConstants.MAX_LPC_ORDER]; + short[] pNLSFW0_temp_QW = new short[SilkConstants.MAX_LPC_ORDER]; + + Inlines.OpusAssert(psEncC.speech_activity_Q8 >= 0); + Inlines.OpusAssert(psEncC.speech_activity_Q8 <= ((int) ((1.0f) * ((long) 1 << (8)) + 0.5))/*Inlines.SILK_CONST(1.0f, 8)*/); + Inlines.OpusAssert(psEncC.useInterpolatedNLSFs == 1 || psEncC.indices.NLSFInterpCoef_Q2 == (1 << 2)); + + /** + * ******************** + */ + /* Calculate mu values */ + /** + * ******************** + */ + /* NLSF_mu = 0.003 - 0.0015 * psEnc.speech_activity; */ + NLSF_mu_Q20 = Inlines.silk_SMLAWB(((int) ((0.003f) * ((long) 1 << (20)) + 0.5))/*Inlines.SILK_CONST(0.003f, 20)*/, ((int) ((-0.001f) * ((long) 1 << (28)) + 0.5))/*Inlines.SILK_CONST(-0.001f, 28)*/, psEncC.speech_activity_Q8); + if (psEncC.nb_subfr == 2) { + /* Multiply by 1.5 for 10 ms packets */ + NLSF_mu_Q20 = Inlines.silk_ADD_RSHIFT(NLSF_mu_Q20, NLSF_mu_Q20, 1); + } + + Inlines.OpusAssert(NLSF_mu_Q20 > 0); + Inlines.OpusAssert(NLSF_mu_Q20 <= ((int) ((0.005f) * ((long) 1 << (20)) + 0.5))/*Inlines.SILK_CONST(0.005f, 20)*/); + + /* Calculate NLSF weights */ + silk_NLSF_VQ_weights_laroia(pNLSFW_QW, pNLSF_Q15, psEncC.predictLPCOrder); + + /* Update NLSF weights for interpolated NLSFs */ + doInterpolate = (psEncC.useInterpolatedNLSFs == 1) && (psEncC.indices.NLSFInterpCoef_Q2 < 4); + if (doInterpolate) { + /* Calculate the interpolated NLSF vector for the first half */ + Inlines.silk_interpolate(pNLSF0_temp_Q15, prev_NLSFq_Q15, pNLSF_Q15, + psEncC.indices.NLSFInterpCoef_Q2, psEncC.predictLPCOrder); + + /* Calculate first half NLSF weights for the interpolated NLSFs */ + silk_NLSF_VQ_weights_laroia(pNLSFW0_temp_QW, pNLSF0_temp_Q15, psEncC.predictLPCOrder); + + /* Update NLSF weights with contribution from first half */ + i_sqr_Q15 = Inlines.silk_LSHIFT(Inlines.silk_SMULBB(psEncC.indices.NLSFInterpCoef_Q2, psEncC.indices.NLSFInterpCoef_Q2), 11); + + for (i = 0; i < psEncC.predictLPCOrder; i++) { + pNLSFW_QW[i] = (short) (Inlines.silk_SMLAWB(Inlines.silk_RSHIFT(pNLSFW_QW[i], 1), (int) pNLSFW0_temp_QW[i], i_sqr_Q15)); + Inlines.OpusAssert(pNLSFW_QW[i] >= 1); + } + } + + ////////////////////////////////////////////////////////////////////////// + silk_NLSF_encode(psEncC.indices.NLSFIndices, pNLSF_Q15, psEncC.psNLSF_CB, pNLSFW_QW, + NLSF_mu_Q20, psEncC.NLSF_MSVQ_Survivors, psEncC.indices.signalType); + + /* Convert quantized NLSFs back to LPC coefficients */ + silk_NLSF2A(PredCoef_Q12[1], pNLSF_Q15, psEncC.predictLPCOrder); + + if (doInterpolate) { + /* Calculate the interpolated, quantized LSF vector for the first half */ + Inlines.silk_interpolate(pNLSF0_temp_Q15, prev_NLSFq_Q15, pNLSF_Q15, + psEncC.indices.NLSFInterpCoef_Q2, psEncC.predictLPCOrder); + + /* Convert back to LPC coefficients */ + silk_NLSF2A(PredCoef_Q12[0], pNLSF0_temp_Q15, psEncC.predictLPCOrder); + + } else { + /* Copy LPC coefficients for first half from second half */ + System.arraycopy(PredCoef_Q12[1], 0, PredCoef_Q12[0], 0, psEncC.predictLPCOrder); + } + } +} diff --git a/app/src/main/java/org/concentus/NLSFCodebook.java b/app/src/main/java/org/concentus/NLSFCodebook.java new file mode 100644 index 0000000000..ff1146a308 --- /dev/null +++ b/app/src/main/java/org/concentus/NLSFCodebook.java @@ -0,0 +1,101 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Structure containing NLSF codebook +/// +class NLSFCodebook { + + short nVectors = 0; + + short order = 0; + + /// + /// Quantization step size + /// + short quantStepSize_Q16 = 0; + + /// + /// Inverse quantization step size + /// + short invQuantStepSize_Q6 = 0; + + /// + /// POINTER + /// + short[] CB1_NLSF_Q8 = null; + + /// + /// POINTER + /// + short[] CB1_iCDF = null; + + /// + /// POINTER to Backward predictor coefs [ order ] + /// + short[] pred_Q8 = null; + + /// + /// POINTER to Indices to entropy coding tables [ order ] + /// + short[] ec_sel = null; + + /// + /// POINTER + /// + short[] ec_iCDF = null; + + /// + /// POINTER + /// + short[] ec_Rates_Q5 = null; + + /// + /// POINTER + /// + short[] deltaMin_Q15 = null; + + void Reset() { + nVectors = 0; + order = 0; + quantStepSize_Q16 = 0; + invQuantStepSize_Q6 = 0; + CB1_NLSF_Q8 = null; + CB1_iCDF = null; + pred_Q8 = null; + ec_sel = null; + ec_iCDF = null; + ec_Rates_Q5 = null; + deltaMin_Q15 = null; + } +} diff --git a/app/src/main/java/org/concentus/NoiseShapeAnalysis.java b/app/src/main/java/org/concentus/NoiseShapeAnalysis.java new file mode 100644 index 0000000000..fe108e2fc7 --- /dev/null +++ b/app/src/main/java/org/concentus/NoiseShapeAnalysis.java @@ -0,0 +1,491 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class NoiseShapeAnalysis { + + /* Compute gain to make warped filter coefficients have a zero mean log frequency response on a */ + /* non-warped frequency scale. (So that it can be implemented with a minimum-phase monic filter.) */ + /* Note: A monic filter is one with the first coefficient equal to 1.0. In Silk we omit the first */ + /* coefficient in an array of coefficients, for monic filters. */ + static int warped_gain( /* gain in Q16*/ + int[] coefs_Q24, + int lambda_Q16, + int order + ) { + int i; + int gain_Q24; + + lambda_Q16 = -lambda_Q16; + gain_Q24 = coefs_Q24[order - 1]; + for (i = order - 2; i >= 0; i--) { + gain_Q24 = Inlines.silk_SMLAWB(coefs_Q24[i], gain_Q24, lambda_Q16); + } + gain_Q24 = Inlines.silk_SMLAWB(((int) ((1.0f) * ((long) 1 << (24)) + 0.5))/*Inlines.SILK_CONST(1.0f, 24)*/, gain_Q24, -lambda_Q16); + return Inlines.silk_INVERSE32_varQ(gain_Q24, 40); + } + + /* Convert warped filter coefficients to monic pseudo-warped coefficients and limit maximum */ + /* amplitude of monic warped coefficients by using bandwidth expansion on the true coefficients */ + static void limit_warped_coefs( + int[] coefs_syn_Q24, + int[] coefs_ana_Q24, + int lambda_Q16, + int limit_Q24, + int order + ) { + int i, iter, ind = 0; + int tmp, maxabs_Q24, chirp_Q16, gain_syn_Q16, gain_ana_Q16; + int nom_Q16, den_Q24; + + /* Convert to monic coefficients */ + lambda_Q16 = -lambda_Q16; + for (i = order - 1; i > 0; i--) { + coefs_syn_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_syn_Q24[i - 1], coefs_syn_Q24[i], lambda_Q16); + coefs_ana_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_ana_Q24[i - 1], coefs_ana_Q24[i], lambda_Q16); + } + lambda_Q16 = -lambda_Q16; + nom_Q16 = Inlines.silk_SMLAWB(((int) ((1.0f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/, -(int) lambda_Q16, lambda_Q16); + den_Q24 = Inlines.silk_SMLAWB(((int) ((1.0f) * ((long) 1 << (24)) + 0.5))/*Inlines.SILK_CONST(1.0f, 24)*/, coefs_syn_Q24[0], lambda_Q16); + gain_syn_Q16 = Inlines.silk_DIV32_varQ(nom_Q16, den_Q24, 24); + den_Q24 = Inlines.silk_SMLAWB(((int) ((1.0f) * ((long) 1 << (24)) + 0.5))/*Inlines.SILK_CONST(1.0f, 24)*/, coefs_ana_Q24[0], lambda_Q16); + gain_ana_Q16 = Inlines.silk_DIV32_varQ(nom_Q16, den_Q24, 24); + for (i = 0; i < order; i++) { + coefs_syn_Q24[i] = Inlines.silk_SMULWW(gain_syn_Q16, coefs_syn_Q24[i]); + coefs_ana_Q24[i] = Inlines.silk_SMULWW(gain_ana_Q16, coefs_ana_Q24[i]); + } + + for (iter = 0; iter < 10; iter++) { + /* Find maximum absolute value */ + maxabs_Q24 = -1; + for (i = 0; i < order; i++) { + tmp = Inlines.silk_max(Inlines.silk_abs_int32(coefs_syn_Q24[i]), Inlines.silk_abs_int32(coefs_ana_Q24[i])); + if (tmp > maxabs_Q24) { + maxabs_Q24 = tmp; + ind = i; + } + } + if (maxabs_Q24 <= limit_Q24) { + /* Coefficients are within range - done */ + return; + } + + /* Convert back to true warped coefficients */ + for (i = 1; i < order; i++) { + coefs_syn_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_syn_Q24[i - 1], coefs_syn_Q24[i], lambda_Q16); + coefs_ana_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_ana_Q24[i - 1], coefs_ana_Q24[i], lambda_Q16); + } + gain_syn_Q16 = Inlines.silk_INVERSE32_varQ(gain_syn_Q16, 32); + gain_ana_Q16 = Inlines.silk_INVERSE32_varQ(gain_ana_Q16, 32); + for (i = 0; i < order; i++) { + coefs_syn_Q24[i] = Inlines.silk_SMULWW(gain_syn_Q16, coefs_syn_Q24[i]); + coefs_ana_Q24[i] = Inlines.silk_SMULWW(gain_ana_Q16, coefs_ana_Q24[i]); + } + + /* Apply bandwidth expansion */ + chirp_Q16 = ((int) ((0.99f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.99f, 16)*/ - Inlines.silk_DIV32_varQ( + Inlines.silk_SMULWB(maxabs_Q24 - limit_Q24, Inlines.silk_SMLABB(((int) ((0.8f) * ((long) 1 << (10)) + 0.5))/*Inlines.SILK_CONST(0.8f, 10)*/, ((int) ((0.1f) * ((long) 1 << (10)) + 0.5))/*Inlines.SILK_CONST(0.1f, 10)*/, iter)), + Inlines.silk_MUL(maxabs_Q24, ind + 1), 22); + BWExpander.silk_bwexpander_32(coefs_syn_Q24, order, chirp_Q16); + BWExpander.silk_bwexpander_32(coefs_ana_Q24, order, chirp_Q16); + + /* Convert to monic warped coefficients */ + lambda_Q16 = -lambda_Q16; + for (i = order - 1; i > 0; i--) { + coefs_syn_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_syn_Q24[i - 1], coefs_syn_Q24[i], lambda_Q16); + coefs_ana_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_ana_Q24[i - 1], coefs_ana_Q24[i], lambda_Q16); + } + lambda_Q16 = -lambda_Q16; + nom_Q16 = Inlines.silk_SMLAWB(((int) ((1.0f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/, -(int) lambda_Q16, lambda_Q16); + den_Q24 = Inlines.silk_SMLAWB(((int) ((1.0f) * ((long) 1 << (24)) + 0.5))/*Inlines.SILK_CONST(1.0f, 24)*/, coefs_syn_Q24[0], lambda_Q16); + gain_syn_Q16 = Inlines.silk_DIV32_varQ(nom_Q16, den_Q24, 24); + den_Q24 = Inlines.silk_SMLAWB(((int) ((1.0f) * ((long) 1 << (24)) + 0.5))/*Inlines.SILK_CONST(1.0f, 24)*/, coefs_ana_Q24[0], lambda_Q16); + gain_ana_Q16 = Inlines.silk_DIV32_varQ(nom_Q16, den_Q24, 24); + for (i = 0; i < order; i++) { + coefs_syn_Q24[i] = Inlines.silk_SMULWW(gain_syn_Q16, coefs_syn_Q24[i]); + coefs_ana_Q24[i] = Inlines.silk_SMULWW(gain_ana_Q16, coefs_ana_Q24[i]); + } + } + Inlines.OpusAssert(false); + } + + /** + * *********************************************************** + */ + /* Compute noise shaping coefficients and initial gain values */ + /** + * *********************************************************** + */ + static void silk_noise_shape_analysis( + SilkChannelEncoder psEnc, /* I/O Encoder state FIX */ + SilkEncoderControl psEncCtrl, /* I/O Encoder control FIX */ + short[] pitch_res, /* I LPC residual from pitch analysis */ + int pitch_res_ptr, + short[] x, /* I Input signal [ frame_length + la_shape ] */ + int x_ptr + ) { + SilkShapeState psShapeSt = psEnc.sShape; + int k, i, nSamples, Qnrg, b_Q14, warping_Q16, scale = 0; + int SNR_adj_dB_Q7, HarmBoost_Q16, HarmShapeGain_Q16, Tilt_Q16, tmp32; + int nrg, pre_nrg_Q30, log_energy_Q7, log_energy_prev_Q7, energy_variation_Q7; + int delta_Q16, BWExp1_Q16, BWExp2_Q16, gain_mult_Q16, gain_add_Q16, strength_Q16, b_Q8; + int[] auto_corr = new int[SilkConstants.MAX_SHAPE_LPC_ORDER + 1]; + int[] refl_coef_Q16 = new int[SilkConstants.MAX_SHAPE_LPC_ORDER]; + int[] AR1_Q24 = new int[SilkConstants.MAX_SHAPE_LPC_ORDER]; + int[] AR2_Q24 = new int[SilkConstants.MAX_SHAPE_LPC_ORDER]; + short[] x_windowed; + int pitch_res_ptr2; + int x_ptr2; + + /* Point to start of first LPC analysis block */ + x_ptr2 = x_ptr - psEnc.la_shape; + + /** + * ************* + */ + /* GAIN CONTROL */ + /** + * ************* + */ + SNR_adj_dB_Q7 = psEnc.SNR_dB_Q7; + + /* Input quality is the average of the quality in the lowest two VAD bands */ + psEncCtrl.input_quality_Q14 = (int) Inlines.silk_RSHIFT((int) psEnc.input_quality_bands_Q15[0] + + psEnc.input_quality_bands_Q15[1], 2); + + /* Coding quality level, between 0.0_Q0 and 1.0_Q0, but in Q14 */ + psEncCtrl.coding_quality_Q14 = Inlines.silk_RSHIFT(Sigmoid.silk_sigm_Q15(Inlines.silk_RSHIFT_ROUND(SNR_adj_dB_Q7 + - ((int) ((20.0f) * ((long) 1 << (7)) + 0.5))/*Inlines.SILK_CONST(20.0f, 7)*/, 4)), 1); + + /* Reduce coding SNR during low speech activity */ + if (psEnc.useCBR == 0) { + b_Q8 = ((int) ((1.0f) * ((long) 1 << (8)) + 0.5))/*Inlines.SILK_CONST(1.0f, 8)*/ - psEnc.speech_activity_Q8; + b_Q8 = Inlines.silk_SMULWB(Inlines.silk_LSHIFT(b_Q8, 8), b_Q8); + SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7, + Inlines.silk_SMULBB(((int) ((0 - TuningParameters.BG_SNR_DECR_dB) * ((long) 1 << (7)) + 0.5))/*Inlines.SILK_CONST(0 - TuningParameters.BG_SNR_DECR_dB, 7)*/ >> (4 + 1), b_Q8), /* Q11*/ + Inlines.silk_SMULWB(((int) ((1.0f) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/ + psEncCtrl.input_quality_Q14, psEncCtrl.coding_quality_Q14)); + /* Q12*/ + } + + if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED) { + /* Reduce gains for periodic signals */ + SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7, ((int) ((TuningParameters.HARM_SNR_INCR_dB) * ((long) 1 << (8)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HARM_SNR_INCR_dB, 8)*/, psEnc.LTPCorr_Q15); + } else { + /* For unvoiced signals and low-quality input, adjust the quality slower than SNR_dB setting */ + SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7, + Inlines.silk_SMLAWB(((int) ((6.0f) * ((long) 1 << (9)) + 0.5))/*Inlines.SILK_CONST(6.0f, 9)*/, -((int) ((0.4f) * ((long) 1 << (18)) + 0.5))/*Inlines.SILK_CONST(0.4f, 18)*/, psEnc.SNR_dB_Q7), + ((int) ((1.0f) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/ - psEncCtrl.input_quality_Q14); + } + + /** + * ********************** + */ + /* SPARSENESS PROCESSING */ + /** + * ********************** + */ + /* Set quantizer offset */ + if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED) { + /* Initially set to 0; may be overruled in process_gains(..) */ + psEnc.indices.quantOffsetType = 0; + psEncCtrl.sparseness_Q8 = 0; + } else { + /* Sparseness measure, based on relative fluctuations of energy per 2 milliseconds */ + nSamples = Inlines.silk_LSHIFT(psEnc.fs_kHz, 1); + energy_variation_Q7 = 0; + log_energy_prev_Q7 = 0; + pitch_res_ptr2 = pitch_res_ptr; + BoxedValueInt boxed_nrg = new BoxedValueInt(0); + BoxedValueInt boxed_scale = new BoxedValueInt(0); + for (k = 0; k < Inlines.silk_SMULBB(SilkConstants.SUB_FRAME_LENGTH_MS, psEnc.nb_subfr) / 2; k++) { + SumSqrShift.silk_sum_sqr_shift(boxed_nrg, boxed_scale, pitch_res, pitch_res_ptr2, nSamples); + nrg = boxed_nrg.Val; + scale = boxed_scale.Val; + nrg += Inlines.silk_RSHIFT(nSamples, scale); + /* Q(-scale)*/ + + log_energy_Q7 = Inlines.silk_lin2log(nrg); + if (k > 0) { + energy_variation_Q7 += Inlines.silk_abs(log_energy_Q7 - log_energy_prev_Q7); + } + log_energy_prev_Q7 = log_energy_Q7; + pitch_res_ptr2 += nSamples; + } + + psEncCtrl.sparseness_Q8 = Inlines.silk_RSHIFT(Sigmoid.silk_sigm_Q15(Inlines.silk_SMULWB(energy_variation_Q7 + - ((int) ((5.0f) * ((long) 1 << (7)) + 0.5))/*Inlines.SILK_CONST(5.0f, 7)*/, ((int) ((0.1f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.1f, 16)*/)), 7); + + /* Set quantization offset depending on sparseness measure */ + if (psEncCtrl.sparseness_Q8 > ((int) ((TuningParameters.SPARSENESS_THRESHOLD_QNT_OFFSET) * ((long) 1 << (8)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SPARSENESS_THRESHOLD_QNT_OFFSET, 8)*/) { + psEnc.indices.quantOffsetType = 0; + } else { + psEnc.indices.quantOffsetType = 1; + } + + /* Increase coding SNR for sparse signals */ + SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7, ((int) ((TuningParameters.SPARSE_SNR_INCR_dB) * ((long) 1 << (15)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SPARSE_SNR_INCR_dB, 15)*/, psEncCtrl.sparseness_Q8 - ((int) ((0.5f) * ((long) 1 << (8)) + 0.5))/*Inlines.SILK_CONST(0.5f, 8)*/); + } + + /** + * **************************** + */ + /* Control bandwidth expansion */ + /** + * **************************** + */ + /* More BWE for signals with high prediction gain */ + strength_Q16 = Inlines.silk_SMULWB(psEncCtrl.predGain_Q16, ((int) ((TuningParameters.FIND_PITCH_WHITE_NOISE_FRACTION) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_PITCH_WHITE_NOISE_FRACTION, 16)*/); + BWExp1_Q16 = BWExp2_Q16 = Inlines.silk_DIV32_varQ(((int) ((TuningParameters.BANDWIDTH_EXPANSION) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.BANDWIDTH_EXPANSION, 16)*/, + Inlines.silk_SMLAWW(((int) ((1.0f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/, strength_Q16, strength_Q16), 16); + delta_Q16 = Inlines.silk_SMULWB(((int) ((1.0f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/ - Inlines.silk_SMULBB(3, psEncCtrl.coding_quality_Q14), + ((int) ((TuningParameters.LOW_RATE_BANDWIDTH_EXPANSION_DELTA) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LOW_RATE_BANDWIDTH_EXPANSION_DELTA, 16)*/); + BWExp1_Q16 = Inlines.silk_SUB32(BWExp1_Q16, delta_Q16); + BWExp2_Q16 = Inlines.silk_ADD32(BWExp2_Q16, delta_Q16); + /* BWExp1 will be applied after BWExp2, so make it relative */ + BWExp1_Q16 = Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(BWExp1_Q16, 14), Inlines.silk_RSHIFT(BWExp2_Q16, 2)); + + if (psEnc.warping_Q16 > 0) { + /* Slightly more warping in analysis will move quantization noise up in frequency, where it's better masked */ + warping_Q16 = Inlines.silk_SMLAWB(psEnc.warping_Q16, (int) psEncCtrl.coding_quality_Q14, ((int) ((0.01f) * ((long) 1 << (18)) + 0.5))/*Inlines.SILK_CONST(0.01f, 18)*/); + } else { + warping_Q16 = 0; + } + + /** + * ***************************************** + */ + /* Compute noise shaping AR coefs and gains */ + /** + * ***************************************** + */ + x_windowed = new short[psEnc.shapeWinLength]; + for (k = 0; k < psEnc.nb_subfr; k++) { + /* Apply window: sine slope followed by flat part followed by cosine slope */ + int shift, slope_part, flat_part; + flat_part = psEnc.fs_kHz * 3; + slope_part = Inlines.silk_RSHIFT(psEnc.shapeWinLength - flat_part, 1); + + ApplySineWindow.silk_apply_sine_window(x_windowed, 0, x, x_ptr2, 1, slope_part); + shift = slope_part; + System.arraycopy(x, x_ptr2 + shift, x_windowed, shift, flat_part); + shift += flat_part; + ApplySineWindow.silk_apply_sine_window(x_windowed, shift, x, x_ptr2 + shift, 2, slope_part); + + /* Update pointer: next LPC analysis block */ + x_ptr2 += psEnc.subfr_length; + BoxedValueInt scale_boxed = new BoxedValueInt(scale); + if (psEnc.warping_Q16 > 0) { + /* Calculate warped auto correlation */ + Autocorrelation.silk_warped_autocorrelation(auto_corr, scale_boxed, x_windowed, warping_Q16, psEnc.shapeWinLength, psEnc.shapingLPCOrder); + } else { + /* Calculate regular auto correlation */ + Autocorrelation.silk_autocorr(auto_corr, scale_boxed, x_windowed, psEnc.shapeWinLength, psEnc.shapingLPCOrder + 1); + } + scale = scale_boxed.Val; + + /* Add white noise, as a fraction of energy */ + auto_corr[0] = Inlines.silk_ADD32(auto_corr[0], Inlines.silk_max_32(Inlines.silk_SMULWB(Inlines.silk_RSHIFT(auto_corr[0], 4), + ((int) ((TuningParameters.SHAPE_WHITE_NOISE_FRACTION) * ((long) 1 << (20)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SHAPE_WHITE_NOISE_FRACTION, 20)*/), 1)); + + /* Calculate the reflection coefficients using schur */ + nrg = Schur.silk_schur64(refl_coef_Q16, auto_corr, psEnc.shapingLPCOrder); + Inlines.OpusAssert(nrg >= 0); + + /* Convert reflection coefficients to prediction coefficients */ + K2A.silk_k2a_Q16(AR2_Q24, refl_coef_Q16, psEnc.shapingLPCOrder); + + Qnrg = -scale; + /* range: -12...30*/ + Inlines.OpusAssert(Qnrg >= -12); + Inlines.OpusAssert(Qnrg <= 30); + + /* Make sure that Qnrg is an even number */ + if ((Qnrg & 1) != 0) { + Qnrg -= 1; + nrg >>= 1; + } + + tmp32 = Inlines.silk_SQRT_APPROX(nrg); + Qnrg >>= 1; + /* range: -6...15*/ + + psEncCtrl.Gains_Q16[k] = Inlines.silk_LSHIFT_SAT32(tmp32, 16 - Qnrg); + + if (psEnc.warping_Q16 > 0) { + /* Adjust gain for warping */ + gain_mult_Q16 = warped_gain(AR2_Q24, warping_Q16, psEnc.shapingLPCOrder); + Inlines.OpusAssert(psEncCtrl.Gains_Q16[k] >= 0); + if (Inlines.silk_SMULWW(Inlines.silk_RSHIFT_ROUND(psEncCtrl.Gains_Q16[k], 1), gain_mult_Q16) >= (Integer.MAX_VALUE >> 1)) { + psEncCtrl.Gains_Q16[k] = Integer.MAX_VALUE; + } else { + psEncCtrl.Gains_Q16[k] = Inlines.silk_SMULWW(psEncCtrl.Gains_Q16[k], gain_mult_Q16); + } + } + + /* Bandwidth expansion for synthesis filter shaping */ + BWExpander.silk_bwexpander_32(AR2_Q24, psEnc.shapingLPCOrder, BWExp2_Q16); + + /* Compute noise shaping filter coefficients */ + System.arraycopy(AR2_Q24, 0, AR1_Q24, 0, psEnc.shapingLPCOrder); + + /* Bandwidth expansion for analysis filter shaping */ + Inlines.OpusAssert(BWExp1_Q16 <= ((int) ((1.0f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/); + BWExpander.silk_bwexpander_32(AR1_Q24, psEnc.shapingLPCOrder, BWExp1_Q16); + + /* Ratio of prediction gains, in energy domain */ + pre_nrg_Q30 = LPCInversePredGain.silk_LPC_inverse_pred_gain_Q24(AR2_Q24, psEnc.shapingLPCOrder); + nrg = LPCInversePredGain.silk_LPC_inverse_pred_gain_Q24(AR1_Q24, psEnc.shapingLPCOrder); + + /*psEncCtrl.GainsPre[ k ] = 1.0f - 0.7f * ( 1.0f - pre_nrg / nrg ) = 0.3f + 0.7f * pre_nrg / nrg;*/ + pre_nrg_Q30 = Inlines.silk_LSHIFT32(Inlines.silk_SMULWB(pre_nrg_Q30, ((int) ((0.7f) * ((long) 1 << (15)) + 0.5))/*Inlines.SILK_CONST(0.7f, 15)*/), 1); + psEncCtrl.GainsPre_Q14[k] = (int) ((int) ((0.3f) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.3f, 14)*/ + Inlines.silk_DIV32_varQ(pre_nrg_Q30, nrg, 14); + + /* Convert to monic warped prediction coefficients and limit absolute values */ + limit_warped_coefs(AR2_Q24, AR1_Q24, warping_Q16, ((int) ((3.999f) * ((long) 1 << (24)) + 0.5))/*Inlines.SILK_CONST(3.999f, 24)*/, psEnc.shapingLPCOrder); + + /* Convert from Q24 to Q13 and store in int16 */ + for (i = 0; i < psEnc.shapingLPCOrder; i++) { + psEncCtrl.AR1_Q13[k * SilkConstants.MAX_SHAPE_LPC_ORDER + i] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(AR1_Q24[i], 11)); + psEncCtrl.AR2_Q13[k * SilkConstants.MAX_SHAPE_LPC_ORDER + i] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(AR2_Q24[i], 11)); + } + } + + /** + * ************** + */ + /* Gain tweaking */ + /** + * ************** + */ + /* Increase gains during low speech activity and put lower limit on gains */ + gain_mult_Q16 = Inlines.silk_log2lin(-Inlines.silk_SMLAWB(-((int) ((16.0f) * ((long) 1 << (7)) + 0.5))/*Inlines.SILK_CONST(16.0f, 7)*/, SNR_adj_dB_Q7, ((int) ((0.16f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.16f, 16)*/)); + gain_add_Q16 = Inlines.silk_log2lin(Inlines.silk_SMLAWB(((int) ((16.0f) * ((long) 1 << (7)) + 0.5))/*Inlines.SILK_CONST(16.0f, 7)*/, ((int) ((SilkConstants.MIN_QGAIN_DB) * ((long) 1 << (7)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.MIN_QGAIN_DB, 7)*/, ((int) ((0.16f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.16f, 16)*/)); + Inlines.OpusAssert(gain_mult_Q16 > 0); + for (k = 0; k < psEnc.nb_subfr; k++) { + psEncCtrl.Gains_Q16[k] = Inlines.silk_SMULWW(psEncCtrl.Gains_Q16[k], gain_mult_Q16); + Inlines.OpusAssert(psEncCtrl.Gains_Q16[k] >= 0); + psEncCtrl.Gains_Q16[k] = Inlines.silk_ADD_POS_SAT32(psEncCtrl.Gains_Q16[k], gain_add_Q16); + } + + gain_mult_Q16 = ((int) ((1.0f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/ + Inlines.silk_RSHIFT_ROUND(Inlines.silk_MLA(((int) ((TuningParameters.INPUT_TILT) * ((long) 1 << (26)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.INPUT_TILT, 26)*/, + psEncCtrl.coding_quality_Q14, ((int) ((TuningParameters.HIGH_RATE_INPUT_TILT) * ((long) 1 << (12)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HIGH_RATE_INPUT_TILT, 12)*/), 10); + for (k = 0; k < psEnc.nb_subfr; k++) { + psEncCtrl.GainsPre_Q14[k] = Inlines.silk_SMULWB(gain_mult_Q16, psEncCtrl.GainsPre_Q14[k]); + } + + /** + * ********************************************* + */ + /* Control low-frequency shaping and noise tilt */ + /** + * ********************************************* + */ + /* Less low frequency shaping for noisy inputs */ + strength_Q16 = Inlines.silk_MUL(((int) ((TuningParameters.LOW_FREQ_SHAPING) * ((long) 1 << (4)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LOW_FREQ_SHAPING, 4)*/, Inlines.silk_SMLAWB(((int) ((1.0f) * ((long) 1 << (12)) + 0.5))/*Inlines.SILK_CONST(1.0f, 12)*/, + ((int) ((TuningParameters.LOW_QUALITY_LOW_FREQ_SHAPING_DECR) * ((long) 1 << (13)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LOW_QUALITY_LOW_FREQ_SHAPING_DECR, 13)*/, psEnc.input_quality_bands_Q15[0] - ((int) ((1.0f) * ((long) 1 << (15)) + 0.5))/*Inlines.SILK_CONST(1.0f, 15)*/)); + strength_Q16 = Inlines.silk_RSHIFT(Inlines.silk_MUL(strength_Q16, psEnc.speech_activity_Q8), 8); + if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED) { + /* Reduce low frequencies quantization noise for periodic signals, depending on pitch lag */ + /*f = 400; freqz([1, -0.98 + 2e-4 * f], [1, -0.97 + 7e-4 * f], 2^12, Fs); axis([0, 1000, -10, 1])*/ + int fs_kHz_inv = Inlines.silk_DIV32_16(((int) ((0.2f) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.2f, 14)*/, psEnc.fs_kHz); + for (k = 0; k < psEnc.nb_subfr; k++) { + b_Q14 = fs_kHz_inv + Inlines.silk_DIV32_16(((int) ((3.0f) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(3.0f, 14)*/, psEncCtrl.pitchL[k]); + /* Pack two coefficients in one int32 */ + psEncCtrl.LF_shp_Q14[k] = Inlines.silk_LSHIFT(((int) ((1.0f) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/ - b_Q14 - Inlines.silk_SMULWB(strength_Q16, b_Q14), 16); + psEncCtrl.LF_shp_Q14[k] |= (b_Q14 - ((int) ((1.0f) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/) & 0xFFFF; + } + Inlines.OpusAssert(((int) ((TuningParameters.HARM_HP_NOISE_COEF) * ((long) 1 << (24)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HARM_HP_NOISE_COEF, 24)*/ < ((int) ((0.5f) * ((long) 1 << (24)) + 0.5))/*Inlines.SILK_CONST(0.5f, 24)*/); + /* Guarantees that second argument to SMULWB() is within range of an short*/ + Tilt_Q16 = -((int) ((TuningParameters.HP_NOISE_COEF) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HP_NOISE_COEF, 16)*/ - Inlines.silk_SMULWB(((int) ((1.0f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/ - ((int) ((TuningParameters.HP_NOISE_COEF) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HP_NOISE_COEF, 16)*/, + Inlines.silk_SMULWB(((int) ((TuningParameters.HARM_HP_NOISE_COEF) * ((long) 1 << (24)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HARM_HP_NOISE_COEF, 24)*/, psEnc.speech_activity_Q8)); + } else { + b_Q14 = Inlines.silk_DIV32_16(21299, psEnc.fs_kHz); + /* 1.3_Q0 = 21299_Q14*/ + /* Pack two coefficients in one int32 */ + psEncCtrl.LF_shp_Q14[0] = Inlines.silk_LSHIFT(((int) ((1.0f) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/ - b_Q14 + - Inlines.silk_SMULWB(strength_Q16, Inlines.silk_SMULWB(((int) ((0.6f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.6f, 16)*/, b_Q14)), 16); + psEncCtrl.LF_shp_Q14[0] |= (b_Q14 - ((int) ((1.0f) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/) & 0xFFFF; + for (k = 1; k < psEnc.nb_subfr; k++) { + psEncCtrl.LF_shp_Q14[k] = psEncCtrl.LF_shp_Q14[0]; + } + Tilt_Q16 = -((int) ((TuningParameters.HP_NOISE_COEF) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HP_NOISE_COEF, 16)*/; + } + + /** + * ************************* + */ + /* HARMONIC SHAPING CONTROL */ + /** + * ************************* + */ + /* Control boosting of harmonic frequencies */ + HarmBoost_Q16 = Inlines.silk_SMULWB(Inlines.silk_SMULWB(((int) ((1.0f) * ((long) 1 << (17)) + 0.5))/*Inlines.SILK_CONST(1.0f, 17)*/ - Inlines.silk_LSHIFT(psEncCtrl.coding_quality_Q14, 3), + psEnc.LTPCorr_Q15), ((int) ((TuningParameters.LOW_RATE_HARMONIC_BOOST) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LOW_RATE_HARMONIC_BOOST, 16)*/); + + /* More harmonic boost for noisy input signals */ + HarmBoost_Q16 = Inlines.silk_SMLAWB(HarmBoost_Q16, + ((int) ((1.0f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/ - Inlines.silk_LSHIFT(psEncCtrl.input_quality_Q14, 2), ((int) ((TuningParameters.LOW_INPUT_QUALITY_HARMONIC_BOOST) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LOW_INPUT_QUALITY_HARMONIC_BOOST, 16)*/); + + if (SilkConstants.USE_HARM_SHAPING != 0 && psEnc.indices.signalType == SilkConstants.TYPE_VOICED) { + /* More harmonic noise shaping for high bitrates or noisy input */ + HarmShapeGain_Q16 = Inlines.silk_SMLAWB(((int) ((TuningParameters.HARMONIC_SHAPING) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HARMONIC_SHAPING, 16)*/, + ((int) ((1.0f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/ - Inlines.silk_SMULWB(((int) ((1.0f) * ((long) 1 << (18)) + 0.5))/*Inlines.SILK_CONST(1.0f, 18)*/ - Inlines.silk_LSHIFT(psEncCtrl.coding_quality_Q14, 4), + psEncCtrl.input_quality_Q14), ((int) ((TuningParameters.HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING, 16)*/); + + /* Less harmonic noise shaping for less periodic signals */ + HarmShapeGain_Q16 = Inlines.silk_SMULWB(Inlines.silk_LSHIFT(HarmShapeGain_Q16, 1), + Inlines.silk_SQRT_APPROX(Inlines.silk_LSHIFT(psEnc.LTPCorr_Q15, 15))); + } else { + HarmShapeGain_Q16 = 0; + } + + /** + * ********************** + */ + /* Smooth over subframes */ + /** + * ********************** + */ + for (k = 0; k < SilkConstants.MAX_NB_SUBFR; k++) { + psShapeSt.HarmBoost_smth_Q16 + = Inlines.silk_SMLAWB(psShapeSt.HarmBoost_smth_Q16, HarmBoost_Q16 - psShapeSt.HarmBoost_smth_Q16, ((int) ((TuningParameters.SUBFR_SMTH_COEF) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SUBFR_SMTH_COEF, 16)*/); + psShapeSt.HarmShapeGain_smth_Q16 + = Inlines.silk_SMLAWB(psShapeSt.HarmShapeGain_smth_Q16, HarmShapeGain_Q16 - psShapeSt.HarmShapeGain_smth_Q16, ((int) ((TuningParameters.SUBFR_SMTH_COEF) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SUBFR_SMTH_COEF, 16)*/); + psShapeSt.Tilt_smth_Q16 + = Inlines.silk_SMLAWB(psShapeSt.Tilt_smth_Q16, Tilt_Q16 - psShapeSt.Tilt_smth_Q16, ((int) ((TuningParameters.SUBFR_SMTH_COEF) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SUBFR_SMTH_COEF, 16)*/); + + psEncCtrl.HarmBoost_Q14[k] = (int) Inlines.silk_RSHIFT_ROUND(psShapeSt.HarmBoost_smth_Q16, 2); + psEncCtrl.HarmShapeGain_Q14[k] = (int) Inlines.silk_RSHIFT_ROUND(psShapeSt.HarmShapeGain_smth_Q16, 2); + psEncCtrl.Tilt_Q14[k] = (int) Inlines.silk_RSHIFT_ROUND(psShapeSt.Tilt_smth_Q16, 2); + } + } +} diff --git a/app/src/main/java/org/concentus/OpusApplication.java b/app/src/main/java/org/concentus/OpusApplication.java new file mode 100644 index 0000000000..c2448f96d3 --- /dev/null +++ b/app/src/main/java/org/concentus/OpusApplication.java @@ -0,0 +1,51 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +public enum OpusApplication { + OPUS_APPLICATION_UNIMPLEMENTED, + /// + /// Best for most VoIP/videoconference applications where listening quality and intelligibility matter most + /// + OPUS_APPLICATION_VOIP, + /// + /// Best for broadcast/high-fidelity application where the decoded audio should be as close as possible to the input + /// + OPUS_APPLICATION_AUDIO, + /// + /// Only use when lowest-achievable latency is what matters most. Voice-optimized modes cannot be used. + /// + OPUS_APPLICATION_RESTRICTED_LOWDELAY +} diff --git a/app/src/main/java/org/concentus/OpusBandwidth.java b/app/src/main/java/org/concentus/OpusBandwidth.java new file mode 100644 index 0000000000..a294f0cdff --- /dev/null +++ b/app/src/main/java/org/concentus/OpusBandwidth.java @@ -0,0 +1,101 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +public enum OpusBandwidth { + OPUS_BANDWIDTH_UNKNOWN, + OPUS_BANDWIDTH_AUTO, + OPUS_BANDWIDTH_NARROWBAND, + OPUS_BANDWIDTH_MEDIUMBAND, + OPUS_BANDWIDTH_WIDEBAND, + OPUS_BANDWIDTH_SUPERWIDEBAND, + OPUS_BANDWIDTH_FULLBAND +} + +// Helpers to port over uses of OpusBandwidth as an integer +class OpusBandwidthHelpers { + + static int GetOrdinal(OpusBandwidth bw) { + switch (bw) { + case OPUS_BANDWIDTH_NARROWBAND: + return 1; + case OPUS_BANDWIDTH_MEDIUMBAND: + return 2; + case OPUS_BANDWIDTH_WIDEBAND: + return 3; + case OPUS_BANDWIDTH_SUPERWIDEBAND: + return 4; + case OPUS_BANDWIDTH_FULLBAND: + return 5; + } + + return -1; + } + + static OpusBandwidth GetBandwidth(int ordinal) { + switch (ordinal) { + case 1: + return OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + case 2: + return OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND; + case 3: + return OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + case 4: + return OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND; + case 5: + return OpusBandwidth.OPUS_BANDWIDTH_FULLBAND; + } + + return OpusBandwidth.OPUS_BANDWIDTH_AUTO; + } + + static OpusBandwidth MIN(OpusBandwidth a, OpusBandwidth b) { + if (GetOrdinal(a) < GetOrdinal(b)) { + return a; + } + return b; + } + + static OpusBandwidth MAX(OpusBandwidth a, OpusBandwidth b) { + if (GetOrdinal(a) > GetOrdinal(b)) { + return a; + } + return b; + } + + static OpusBandwidth SUBTRACT(OpusBandwidth a, int b) { + return GetBandwidth(GetOrdinal(a) - b); + } +} diff --git a/app/src/main/java/org/concentus/OpusConstants.java b/app/src/main/java/org/concentus/OpusConstants.java new file mode 100644 index 0000000000..8154a473e3 --- /dev/null +++ b/app/src/main/java/org/concentus/OpusConstants.java @@ -0,0 +1,59 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +public class OpusConstants { + + /// + /// Auto/default setting + /// + public static final int OPUS_AUTO = -1000; + + /// + /// Maximum bitrate + /// + public static final int OPUS_BITRATE_MAX = -1; + + // from analysis.c + public static final int NB_FRAMES = 8; + public static final int NB_TBANDS = 18; + public static final int NB_TOT_BANDS = 21; + public static final int NB_TONAL_SKIP_BANDS = 9; + public static final int ANALYSIS_BUF_SIZE = 720; + /* 15 ms at 48 kHz */ + public static final int DETECT_SIZE = 200; + + public static final int MAX_ENCODER_BUFFER = 480; +} diff --git a/app/src/main/java/org/concentus/OpusDecoder.java b/app/src/main/java/org/concentus/OpusDecoder.java new file mode 100644 index 0000000000..d97258a5e7 --- /dev/null +++ b/app/src/main/java/org/concentus/OpusDecoder.java @@ -0,0 +1,784 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/** + * The Opus decoder structure. + * + * Opus is a stateful codec with overlapping blocks and as a result Opus + * packets are not coded independently of each other. Packets must be + * passed into the decoder serially and in the correct order for a correct + * decode. Lost packets can be replaced with loss concealment by calling + * the decoder with a null reference and zero length for the missing packet. + * + * A single codec state may only be accessed from a single thread at + * a time and any required locking must be performed by the caller. Separate + * streams must be decoded with separate decoder states and can be decoded + * in parallel. + */ +public class OpusDecoder { + + int channels; + int Fs; + /** + * Sampling rate (at the API level) + */ + final DecControlState DecControl = new DecControlState(); + int decode_gain; + + /* Everything beyond this point gets cleared on a reset */ + int stream_channels; + OpusBandwidth bandwidth; + OpusMode mode; + OpusMode prev_mode; + int frame_size; + int prev_redundancy; + int last_packet_duration; + int rangeFinal; + SilkDecoder SilkDecoder = new SilkDecoder(); + CeltDecoder Celt_Decoder = new CeltDecoder(); + + OpusDecoder() { + } // used internally + + void reset() { + channels = 0; + Fs = 0; + /** + * Sampling rate (at the API level) + */ + DecControl.Reset(); + decode_gain = 0; + partialReset(); + } + + /// + /// OPUS_DECODER_RESET_START + /// + void partialReset() { + stream_channels = 0; + bandwidth = OpusBandwidth.OPUS_BANDWIDTH_UNKNOWN; + mode = OpusMode.MODE_UNKNOWN; + prev_mode = OpusMode.MODE_UNKNOWN; + frame_size = 0; + prev_redundancy = 0; + last_packet_duration = 0; + rangeFinal = 0; + // fixme: do these get reset here? I don't think they do because init_celt and init_silk should both call RESET_STATE on their respective states + //SilkDecoder.Reset(); + //CeltDecoder.Reset(); + } + + int opus_decoder_init(int Fs, int channels) { + SilkDecoder silk_dec; + CeltDecoder celt_dec; + int ret; + + if ((Fs != 48000 && Fs != 24000 && Fs != 16000 && Fs != 12000 && Fs != 8000) + || (channels != 1 && channels != 2)) { + return OpusError.OPUS_BAD_ARG; + } + this.reset(); + + /* Initialize SILK encoder */ + silk_dec = this.SilkDecoder; + celt_dec = this.Celt_Decoder; + this.stream_channels = this.channels = channels; + + this.Fs = Fs; + this.DecControl.API_sampleRate = this.Fs; + this.DecControl.nChannelsAPI = this.channels; + + /* Reset decoder */ + ret = DecodeAPI.silk_InitDecoder(silk_dec); + if (ret != 0) { + return OpusError.OPUS_INTERNAL_ERROR; + } + + /* Initialize CELT decoder */ + ret = celt_dec.celt_decoder_init(Fs, channels); + if (ret != OpusError.OPUS_OK) { + return OpusError.OPUS_INTERNAL_ERROR; + } + + celt_dec.SetSignalling(0); + + this.prev_mode = OpusMode.MODE_UNKNOWN; + this.frame_size = Fs / 400; + return OpusError.OPUS_OK; + } + + /** + * Allocates and initializes a decoder state. Internally Opus stores data at + * 48000 Hz, so that should be the default value for Fs. However, the + * decoder can efficiently decode to buffers at 8, 12, 16, and 24 kHz so if + * for some reason the caller cannot use data at the full sample rate, or + * knows the compressed data doesn't use the full frequency range, it can + * request decoding at a reduced rate. Likewise, the decoder is capable of + * filling in either mono or interleaved stereo pcm buffers, at the caller's + * request. + * + * @param Fs Sample rate to decode at (Hz). This must be one of 8000, 12000, + * 16000, 24000, or 48000. + * @param channels Number of channels (1 or 2) to decode. + * @throws OpusException + */ + public OpusDecoder(int Fs, int channels) throws OpusException { + int ret; + if ((Fs != 48000 && Fs != 24000 && Fs != 16000 && Fs != 12000 && Fs != 8000)) { + throw new IllegalArgumentException("Sample rate is invalid (must be 8/12/16/24/48 Khz)"); + } + if (channels != 1 && channels != 2) { + throw new IllegalArgumentException("Number of channels must be 1 or 2"); + } + + ret = this.opus_decoder_init(Fs, channels); + if (ret != OpusError.OPUS_OK) { + if (ret == OpusError.OPUS_BAD_ARG) { + throw new IllegalArgumentException("OPUS_BAD_ARG when creating decoder"); + } + throw new OpusException("Error while initializing decoder", ret); + } + } + + private static final byte[] SILENCE = new byte[]{-1, -1}; + + int opus_decode_frame(byte[] data, int data_ptr, + int len, short[] pcm, int pcm_ptr, int frame_size, int decode_fec) { + SilkDecoder silk_dec; + CeltDecoder celt_dec; + int i, silk_ret = 0, celt_ret = 0; + EntropyCoder dec = new EntropyCoder(); // porting note: stack var + int silk_frame_size; + int pcm_silk_size; + short[] pcm_silk; + int pcm_transition_silk_size; + short[] pcm_transition_silk; + int pcm_transition_celt_size; + short[] pcm_transition_celt; + short[] pcm_transition = null; + int redundant_audio_size; + short[] redundant_audio; + + int audiosize; + OpusMode mode; + int transition = 0; + int start_band; + int redundancy = 0; + int redundancy_bytes = 0; + int celt_to_silk = 0; + int c; + int F2_5, F5, F10, F20; + int[] window; + int redundant_rng = 0; + int celt_accum; + + silk_dec = this.SilkDecoder; + celt_dec = this.Celt_Decoder; + F20 = this.Fs / 50; + F10 = F20 >> 1; + F5 = F10 >> 1; + F2_5 = F5 >> 1; + if (frame_size < F2_5) { + + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + /* Limit frame_size to avoid excessive stack allocations. */ + frame_size = Inlines.IMIN(frame_size, this.Fs / 25 * 3); + /* Payloads of 1 (2 including ToC) or 0 trigger the PLC/DTX */ + if (len <= 1) { + data = null; + /* In that case, don't conceal more than what the ToC says */ + frame_size = Inlines.IMIN(frame_size, this.frame_size); + } + if (data != null) { + audiosize = this.frame_size; + mode = this.mode; + dec.dec_init(data, data_ptr, len); + } else { + audiosize = frame_size; + mode = this.prev_mode; + + if (mode == OpusMode.MODE_UNKNOWN) { + /* If we haven't got any packet yet, all we can do is return zeros */ + for (i = pcm_ptr; i < pcm_ptr + (audiosize * this.channels); i++) { + pcm[i] = 0; + } + + return audiosize; + } + + /* Avoids trying to run the PLC on sizes other than 2.5 (CELT), 5 (CELT), + 10, or 20 (e.g. 12.5 or 30 ms). */ + if (audiosize > F20) { + do { + int ret = opus_decode_frame(null, 0, 0, pcm, pcm_ptr, Inlines.IMIN(audiosize, F20), 0); + if (ret < 0) { + + return ret; + } + pcm_ptr += ret * this.channels; + audiosize -= ret; + } while (audiosize > 0); + + return frame_size; + } else if (audiosize < F20) { + if (audiosize > F10) { + audiosize = F10; + } else if (mode != OpusMode.MODE_SILK_ONLY && audiosize > F5 && audiosize < F10) { + audiosize = F5; + } + } + } + + /* In fixed-point, we can tell CELT to do the accumulation on top of the + SILK PCM buffer. This saves some stack space. */ + celt_accum = ((mode != OpusMode.MODE_CELT_ONLY) && (frame_size >= F10)) ? 1 : 0; + + pcm_transition_silk_size = 0; + pcm_transition_celt_size = 0; + if (data != null && (this.prev_mode != OpusMode.MODE_UNKNOWN && this.prev_mode != OpusMode.MODE_AUTO) && ((mode == OpusMode.MODE_CELT_ONLY && this.prev_mode != OpusMode.MODE_CELT_ONLY && (this.prev_redundancy == 0)) + || (mode != OpusMode.MODE_CELT_ONLY && this.prev_mode == OpusMode.MODE_CELT_ONLY))) { + transition = 1; + /* Decide where to allocate the stack memory for pcm_transition */ + if (mode == OpusMode.MODE_CELT_ONLY) { + pcm_transition_celt_size = F5 * this.channels; + } else { + pcm_transition_silk_size = F5 * this.channels; + } + } + pcm_transition_celt = new short[pcm_transition_celt_size]; + if (transition != 0 && mode == OpusMode.MODE_CELT_ONLY) { + pcm_transition = pcm_transition_celt; + opus_decode_frame(null, 0, 0, pcm_transition, 0, Inlines.IMIN(F5, audiosize), 0); + } + if (audiosize > frame_size) { + /*fprintf(stderr, "PCM buffer too small: %d vs %d (mode = %d)\n", audiosize, frame_size, mode);*/ + + return OpusError.OPUS_BAD_ARG; + } else { + frame_size = audiosize; + } + + /* Don't allocate any memory when in CELT-only mode */ + pcm_silk_size = (mode != OpusMode.MODE_CELT_ONLY && (celt_accum == 0)) ? Inlines.IMAX(F10, frame_size) * this.channels : 0; + pcm_silk = new short[pcm_silk_size]; + + /* SILK processing */ + if (mode != OpusMode.MODE_CELT_ONLY) { + int lost_flag, decoded_samples; + short[] pcm_ptr2; + int pcm_ptr2_ptr = 0; + + if (celt_accum != 0) { + pcm_ptr2 = pcm; + pcm_ptr2_ptr = pcm_ptr; + } else { + pcm_ptr2 = pcm_silk; + pcm_ptr2_ptr = 0; + } + + if (this.prev_mode == OpusMode.MODE_CELT_ONLY) { + DecodeAPI.silk_InitDecoder(silk_dec); + } + + /* The SILK PLC cannot produce frames of less than 10 ms */ + this.DecControl.payloadSize_ms = Inlines.IMAX(10, 1000 * audiosize / this.Fs); + + if (data != null) { + this.DecControl.nChannelsInternal = this.stream_channels; + if (mode == OpusMode.MODE_SILK_ONLY) { + if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) { + this.DecControl.internalSampleRate = 8000; + } else if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) { + this.DecControl.internalSampleRate = 12000; + } else if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND) { + this.DecControl.internalSampleRate = 16000; + } else { + this.DecControl.internalSampleRate = 16000; + Inlines.OpusAssert(false); + } + } else { + /* Hybrid mode */ + this.DecControl.internalSampleRate = 16000; + } + } + + lost_flag = data == null ? 1 : 2 * decode_fec; + decoded_samples = 0; + do { + /* Call SILK decoder */ + int first_frame = (decoded_samples == 0) ? 1 : 0; + BoxedValueInt boxed_silk_frame_size = new BoxedValueInt(0); + silk_ret = DecodeAPI.silk_Decode(silk_dec, this.DecControl, + lost_flag, first_frame, dec, pcm_ptr2, pcm_ptr2_ptr, boxed_silk_frame_size); + silk_frame_size = boxed_silk_frame_size.Val; + + if (silk_ret != 0) { + if (lost_flag != 0) { + /* PLC failure should not be fatal */ + silk_frame_size = frame_size; + Arrays.MemSetWithOffset(pcm_ptr2, (short) 0, pcm_ptr2_ptr, frame_size * this.channels); + } else { + + return OpusError.OPUS_INTERNAL_ERROR; + } + } + pcm_ptr2_ptr += (silk_frame_size * this.channels); + decoded_samples += silk_frame_size; + } while (decoded_samples < frame_size); + } + + start_band = 0; + if (decode_fec == 0 && mode != OpusMode.MODE_CELT_ONLY && data != null + && dec.tell() + 17 + 20 * (this.mode == OpusMode.MODE_HYBRID ? 1 : 0) <= 8 * len) { + /* Check if we have a redundant 0-8 kHz band */ + if (mode == OpusMode.MODE_HYBRID) { + redundancy = dec.dec_bit_logp(12); + } else { + redundancy = 1; + } + if (redundancy != 0) { + celt_to_silk = dec.dec_bit_logp(1); + /* redundancy_bytes will be at least two, in the non-hybrid + case due to the ec_tell() check above */ + redundancy_bytes = mode == OpusMode.MODE_HYBRID + ? (int) dec.dec_uint(256) + 2 + : len - ((dec.tell() + 7) >> 3); + len -= redundancy_bytes; + /* This is a sanity check. It should never happen for a valid + packet, so the exact behaviour is not normative. */ + if (len * 8 < dec.tell()) { + len = 0; + redundancy_bytes = 0; + redundancy = 0; + } + /* Shrink decoder because of raw bits */ + dec.storage = (dec.storage - redundancy_bytes); + } + } + if (mode != OpusMode.MODE_CELT_ONLY) { + start_band = 17; + } + + { + int endband = 21; + + switch (this.bandwidth) { + case OPUS_BANDWIDTH_NARROWBAND: + endband = 13; + break; + case OPUS_BANDWIDTH_MEDIUMBAND: + case OPUS_BANDWIDTH_WIDEBAND: + endband = 17; + break; + case OPUS_BANDWIDTH_SUPERWIDEBAND: + endband = 19; + break; + case OPUS_BANDWIDTH_FULLBAND: + endband = 21; + break; + } + celt_dec.SetEndBand(endband); + celt_dec.SetChannels(this.stream_channels); + } + + if (redundancy != 0) { + transition = 0; + pcm_transition_silk_size = 0; + } + + pcm_transition_silk = new short[pcm_transition_silk_size]; + + if (transition != 0 && mode != OpusMode.MODE_CELT_ONLY) { + pcm_transition = pcm_transition_silk; + opus_decode_frame(null, 0, 0, pcm_transition, 0, Inlines.IMIN(F5, audiosize), 0); + } + + /* Only allocation memory for redundancy if/when needed */ + redundant_audio_size = redundancy != 0 ? F5 * this.channels : 0; + redundant_audio = new short[redundant_audio_size]; + + /* 5 ms redundant frame for CELT->SILK*/ + if (redundancy != 0 && celt_to_silk != 0) { + celt_dec.SetStartBand(0); + celt_dec.celt_decode_with_ec(data, (data_ptr + len), redundancy_bytes, + redundant_audio, 0, F5, null, 0); + redundant_rng = celt_dec.GetFinalRange(); + } + + /* MUST be after PLC */ + celt_dec.SetStartBand(start_band); + + if (mode != OpusMode.MODE_SILK_ONLY) { + int celt_frame_size = Inlines.IMIN(F20, frame_size); + /* Make sure to discard any previous CELT state */ + if (mode != this.prev_mode && (this.prev_mode != OpusMode.MODE_AUTO && this.prev_mode != OpusMode.MODE_UNKNOWN) && this.prev_redundancy == 0) { + celt_dec.ResetState(); + } + /* Decode CELT */ + celt_ret = celt_dec.celt_decode_with_ec(decode_fec != 0 ? null : data, data_ptr, + len, pcm, pcm_ptr, celt_frame_size, dec, celt_accum); + } else { + if (celt_accum == 0) { + for (i = pcm_ptr; i < (frame_size * this.channels) + pcm_ptr; i++) { + pcm[i] = 0; + } + } + /* For hybrid -> SILK transitions, we let the CELT MDCT + do a fade-out by decoding a silence frame */ + if (this.prev_mode == OpusMode.MODE_HYBRID && !(redundancy != 0 && celt_to_silk != 0 && this.prev_redundancy != 0)) { + celt_dec.SetStartBand(0); + celt_dec.celt_decode_with_ec(SILENCE, 0, 2, pcm, pcm_ptr, F2_5, null, celt_accum); + } + } + + if (mode != OpusMode.MODE_CELT_ONLY && celt_accum == 0) { + for (i = 0; i < frame_size * this.channels; i++) { + pcm[pcm_ptr + i] = Inlines.SAT16(Inlines.ADD32(pcm[pcm_ptr + i], pcm_silk[i])); + } + } + + window = celt_dec.GetMode().window; + + /* 5 ms redundant frame for SILK->CELT */ + if (redundancy != 0 && celt_to_silk == 0) { + celt_dec.ResetState(); + celt_dec.SetStartBand(0); + + celt_dec.celt_decode_with_ec(data, data_ptr + len, redundancy_bytes, redundant_audio, 0, F5, null, 0); + redundant_rng = celt_dec.GetFinalRange(); + CodecHelpers.smooth_fade(pcm, pcm_ptr + this.channels * (frame_size - F2_5), redundant_audio, this.channels * F2_5, + pcm, (pcm_ptr + this.channels * (frame_size - F2_5)), F2_5, this.channels, window, this.Fs); + } + if (redundancy != 0 && celt_to_silk != 0) { + for (c = 0; c < this.channels; c++) { + for (i = 0; i < F2_5; i++) { + pcm[this.channels * i + c + pcm_ptr] = redundant_audio[this.channels * i + c]; + } + } + CodecHelpers.smooth_fade(redundant_audio, (this.channels * F2_5), pcm, (pcm_ptr + (this.channels * F2_5)), + pcm, (pcm_ptr + (this.channels * F2_5)), F2_5, this.channels, window, this.Fs); + } + if (transition != 0) { + if (audiosize >= F5) { + for (i = 0; i < this.channels * F2_5; i++) { + pcm[i] = pcm_transition[i]; + } + CodecHelpers.smooth_fade(pcm_transition, (this.channels * F2_5), pcm, (pcm_ptr + (this.channels * F2_5)), + pcm, (pcm_ptr + (this.channels * F2_5)), F2_5, + this.channels, window, this.Fs); + } else { + /* Not enough time to do a clean transition, but we do it anyway + This will not preserve amplitude perfectly and may introduce + a bit of temporal aliasing, but it shouldn't be too bad and + that's pretty much the best we can do. In any case, generating this + transition is pretty silly in the first place */ + CodecHelpers.smooth_fade(pcm_transition, 0, pcm, pcm_ptr, + pcm, pcm_ptr, F2_5, + this.channels, window, this.Fs); + } + } + + if (this.decode_gain != 0) { + int gain; + gain = Inlines.celt_exp2(Inlines.MULT16_16_P15(((short) (0.5 + (6.48814081e-4f) * ((1) << (25))))/*Inlines.QCONST16(6.48814081e-4f, 25)*/, this.decode_gain)); + for (i = pcm_ptr; i < pcm_ptr + (frame_size * this.channels); i++) { + int x; + x = Inlines.MULT16_32_P16(pcm[i], gain); + pcm[i] = (short) Inlines.SATURATE(x, 32767); + } + } + + if (len <= 1) { + this.rangeFinal = 0; + } else { + this.rangeFinal = ((int) dec.rng) ^ redundant_rng; + } + + this.prev_mode = mode; + this.prev_redundancy = (redundancy != 0 && celt_to_silk == 0) ? 1 : 0; + + return celt_ret < 0 ? celt_ret : audiosize; + } + + int opus_decode_native(byte[] data, int data_ptr, + int len, short[] pcm_out, int pcm_out_ptr, int frame_size, int decode_fec, + int self_delimited, BoxedValueInt packet_offset, int soft_clip) { + int i, nb_samples; + int count, offset; + int packet_frame_size, packet_stream_channels; + packet_offset.Val = 0; + OpusBandwidth packet_bandwidth; + OpusMode packet_mode; + /* 48 x 2.5 ms = 120 ms */ + // fixme: make sure these values can fit in an int16 + short[] size = new short[48]; + if (decode_fec < 0 || decode_fec > 1) { + return OpusError.OPUS_BAD_ARG; + } + /* For FEC/PLC, frame_size has to be to have a multiple of 2.5 ms */ + if ((decode_fec != 0 || len == 0 || data == null) && frame_size % (this.Fs / 400) != 0) { + return OpusError.OPUS_BAD_ARG; + } + if (len == 0 || data == null) { + int pcm_count = 0; + do { + int ret; + ret = opus_decode_frame(null, 0, 0, pcm_out, pcm_out_ptr + (pcm_count * this.channels), frame_size - pcm_count, 0); + if (ret < 0) { + return ret; + } + pcm_count += ret; + } while (pcm_count < frame_size); + Inlines.OpusAssert(pcm_count == frame_size); + this.last_packet_duration = pcm_count; + return pcm_count; + } else if (len < 0) { + return OpusError.OPUS_BAD_ARG; + } + + packet_mode = OpusPacketInfo.getEncoderMode(data, data_ptr); + packet_bandwidth = OpusPacketInfo.getBandwidth(data, data_ptr); + packet_frame_size = OpusPacketInfo.getNumSamplesPerFrame(data, data_ptr, this.Fs); + packet_stream_channels = OpusPacketInfo.getNumEncodedChannels(data, data_ptr); + + BoxedValueByte boxed_toc = new BoxedValueByte((byte) 0); + BoxedValueInt boxed_offset = new BoxedValueInt(0); + count = OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, self_delimited, boxed_toc, null, 0, + size, 0, boxed_offset, packet_offset); + offset = boxed_offset.Val; + + if (count < 0) { + return count; + } + + data_ptr += offset; + + if (decode_fec != 0) { + BoxedValueInt dummy = new BoxedValueInt(0); + int duration_copy; + int ret; + /* If no FEC can be present, run the PLC (recursive call) */ + if (frame_size < packet_frame_size || packet_mode == OpusMode.MODE_CELT_ONLY || this.mode == OpusMode.MODE_CELT_ONLY) { + return opus_decode_native(null, 0, 0, pcm_out, pcm_out_ptr, frame_size, 0, 0, dummy, soft_clip); + } + /* Otherwise, run the PLC on everything except the size for which we might have FEC */ + duration_copy = this.last_packet_duration; + if (frame_size - packet_frame_size != 0) { + ret = opus_decode_native(null, 0, 0, pcm_out, pcm_out_ptr, frame_size - packet_frame_size, 0, 0, dummy, soft_clip); + if (ret < 0) { + this.last_packet_duration = duration_copy; + return ret; + } + Inlines.OpusAssert(ret == frame_size - packet_frame_size); + } + /* Complete with FEC */ + this.mode = packet_mode; + this.bandwidth = packet_bandwidth; + this.frame_size = packet_frame_size; + this.stream_channels = packet_stream_channels; + ret = opus_decode_frame(data, data_ptr, size[0], pcm_out, pcm_out_ptr + (this.channels * (frame_size - packet_frame_size)), + packet_frame_size, 1); + if (ret < 0) { + return ret; + } else { + this.last_packet_duration = frame_size; + return frame_size; + } + } + + if (count * packet_frame_size > frame_size) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + + /* Update the state as the last step to avoid updating it on an invalid packet */ + this.mode = packet_mode; + this.bandwidth = packet_bandwidth; + this.frame_size = packet_frame_size; + this.stream_channels = packet_stream_channels; + + nb_samples = 0; + for (i = 0; i < count; i++) { + int ret; + ret = opus_decode_frame(data, data_ptr, size[i], pcm_out, pcm_out_ptr + (nb_samples * this.channels), frame_size - nb_samples, 0); + if (ret < 0) { + return ret; + } + Inlines.OpusAssert(ret == packet_frame_size); + data_ptr += size[i]; + nb_samples += ret; + } + this.last_packet_duration = nb_samples; + + return nb_samples; + } + + /// + /// Decodes an Opus packet. + /// + /// + /// + /// + /// + /// + /// exact sizing. + /// + /// + /// Flag to request that any in-band forward error correction data be + /// decoded. If no such data is available, the frame is decoded as if it were lost. + /// The number of decoded samples + /** + * Decodes an Opus packet. + * @param in_data The input payload. This may be NULL if that previous packet was lost in transit (when PLC is enabled) + * @param in_data_offset The offset to use when reading the input payload. Usually 0 + * @param len The number of bytes in the payload (the packet size) + * @param out_pcm A buffer to put the output PCM, in a short array. The output size is (# of samples) * (# of channels). + * You can use the OpusPacketInfo helpers to get a hint of the frame size before you decode the packet if you need exact sizing. + * @param out_pcm_offset The offset to use when writing to the output buffer + * @param frame_size The number of samples (per channel) of available space in the output PCM buf. + * If this is less than the maximum packet duration (120ms; 5760 for 48khz), this function will + * not be capable of decoding some packets. In the case of PLC (data == NULL) or FEC (decode_fec == true), + * then frame_size needs to be exactly the duration of the audio that is missing, otherwise the decoder will + * not be in an optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size *must* + * be a multiple of 2.5 ms. + * @param decode_fec Indicates that we want to recreate the PREVIOUS (lost) packet using FEC data from THIS packet. Using this packet + * recovery scheme, you will actually decode this packet twice, first with decode_fec TRUE and then again with FALSE. If FEC data is not + * available in this packet, the decoder will simply generate a best-effort recreation of the lost packet. In that case, + * the length of frame_size must be EXACTLY the length of the audio that was lost, or else the decoder will be in an inconsistent state. + * @return The number of decoded samples (per channel) + * @throws OpusException + */ + public int decode(byte[] in_data, int in_data_offset, + int len, short[] out_pcm, int out_pcm_offset, int frame_size, boolean decode_fec) throws OpusException { + if (frame_size <= 0) { + throw new IllegalArgumentException("Frame size must be > 0"); + } + + try { + BoxedValueInt dummy = new BoxedValueInt(0); + int ret = opus_decode_native(in_data, in_data_offset, len, out_pcm, out_pcm_offset, frame_size, decode_fec ? 1 : 0, 0, dummy, 0); + + if (ret < 0) { + // An error happened; report it + if (ret == OpusError.OPUS_BAD_ARG) { + throw new IllegalArgumentException("OPUS_BAD_ARG while decoding"); + } + throw new OpusException("An error occurred during decoding", ret); + } + + return ret; + } catch (ArithmeticException e) { + throw new OpusException("Internal error during decoding: " + e.getMessage()); + } + } + + /** + * Decodes an Opus packet. + * @param in_data The input payload. This may be NULL if that previous packet was lost in transit (when PLC is enabled) + * @param in_data_offset The offset to use when reading the input payload. Usually 0 + * @param len The number of bytes in the payload (the packet size) + * @param out_pcm A buffer to put the output PCM, in a byte array. The output size is (# of samples) * (# of channels) * 2. + * You can use the OpusPacketInfo helpers to get a hint of the frame size before you decode the packet if you need exact sizing. + * @param out_pcm_offset The offset to use when writing to the output buffer + * @param frame_size The number of samples (per channel) of available space in the output PCM buf. + * If this is less than the maximum packet duration (120ms; 5760 for 48khz), this function will + * not be capable of decoding some packets. In the case of PLC (data == NULL) or FEC (decode_fec == true), + * then frame_size needs to be exactly the duration of the audio that is missing, otherwise the decoder will + * not be in an optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size *must* + * be a multiple of 2.5 ms. + * @param decode_fec Indicates that we want to recreate the PREVIOUS (lost) packet using FEC data from THIS packet. Using this packet + * recovery scheme, you will actually decode this packet twice, first with decode_fec TRUE and then again with FALSE. If FEC data is not + * available in this packet, the decoder will simply generate a best-effort recreation of the lost packet. In that case, the + * length of frame_size must be EXACTLY the length of the audio that was lost, or else the decoder will be in an inconsistent state. + * @return The number of decoded samples (per channel) + * @throws OpusException + */ + public int decode(byte[] in_data, int in_data_offset, int len, byte[] out_pcm, + int out_pcm_offset, int frame_size, boolean decode_fec) throws OpusException { + short[] spcm = new short[Math.min(frame_size, 5760) * channels]; + int decSamples = decode(in_data, in_data_offset, len, spcm, 0, frame_size, decode_fec); + //Convert short array to byte array + for (int c = 0, idx = out_pcm_offset; c < spcm.length; c++) { + out_pcm[idx++] = (byte) (spcm[c] & 0xff); + out_pcm[idx++] = (byte) ((spcm[c] >> 8) & 0xff); + } + return decSamples; + } + + public OpusBandwidth getBandwidth() { + return bandwidth; + } + + public int getFinalRange() { + return rangeFinal; + } + + public int getSampleRate() { + return Fs; + } + + public int getPitch() { + if (prev_mode == OpusMode.MODE_CELT_ONLY) { + return Celt_Decoder.GetPitch(); + } else { + return DecControl.prevPitchLag; + } + } + + public int getGain() { + return decode_gain; + } + + public void setGain(int value) { + if (value < -32768 || value > 32767) { + throw new IllegalArgumentException("Gain must be within the range of a signed int16"); + } + + decode_gain = value; + } + + public int getLastPacketDuration() { + return last_packet_duration; + } + + public void resetState() { + partialReset(); + Celt_Decoder.ResetState(); + DecodeAPI.silk_InitDecoder(SilkDecoder); + stream_channels = channels; + frame_size = Fs / 400; + } +} diff --git a/app/src/main/java/org/concentus/OpusEncoder.java b/app/src/main/java/org/concentus/OpusEncoder.java new file mode 100644 index 0000000000..6810e6220f --- /dev/null +++ b/app/src/main/java/org/concentus/OpusEncoder.java @@ -0,0 +1,1643 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// The Opus encoder structure +/// +public class OpusEncoder { + + final EncControlState silk_mode = new EncControlState(); + OpusApplication application; + int channels; + int delay_compensation; + int force_channels; + OpusSignal signal_type; + OpusBandwidth user_bandwidth; + OpusBandwidth max_bandwidth; + OpusMode user_forced_mode; + int voice_ratio; + int Fs; + int use_vbr; + int vbr_constraint; + OpusFramesize variable_duration; + int bitrate_bps; + int user_bitrate_bps; + int lsb_depth; + int encoder_buffer; + int lfe; + final TonalityAnalysisState analysis = new TonalityAnalysisState(); + + // partial reset happens below this line + int stream_channels; + short hybrid_stereo_width_Q14; + int variable_HP_smth2_Q15; + int prev_HB_gain; + final int[] hp_mem = new int[4]; + OpusMode mode; + OpusMode prev_mode; + int prev_channels; + int prev_framesize; + OpusBandwidth bandwidth; + int silk_bw_switch; + /* Sampling rate (at the API level) */ + int first; + int[] energy_masking; + final StereoWidthState width_mem = new StereoWidthState(); + final short[] delay_buffer = new short[OpusConstants.MAX_ENCODER_BUFFER * 2]; + OpusBandwidth detected_bandwidth; + int rangeFinal; + + // [Porting Note] There were originally "cabooses" that were tacked onto the end + // of the struct without being explicitly included (since they have a variable size). + // Here they are just included as an intrinsic variable. + final SilkEncoder SilkEncoder = new SilkEncoder(); + final CeltEncoder Celt_Encoder = new CeltEncoder(); + + OpusEncoder() { + } // used internally + + void reset() { + silk_mode.Reset(); + application = OpusApplication.OPUS_APPLICATION_UNIMPLEMENTED; + channels = 0; + delay_compensation = 0; + force_channels = 0; + signal_type = OpusSignal.OPUS_SIGNAL_UNKNOWN; + user_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_UNKNOWN; + max_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_UNKNOWN; + user_forced_mode = OpusMode.MODE_UNKNOWN; + voice_ratio = 0; + Fs = 0; + use_vbr = 0; + vbr_constraint = 0; + variable_duration = OpusFramesize.OPUS_FRAMESIZE_UNKNOWN; + bitrate_bps = 0; + user_bitrate_bps = 0; + lsb_depth = 0; + encoder_buffer = 0; + lfe = 0; + analysis.Reset(); + PartialReset(); + } + + /// + /// OPUS_ENCODER_RESET_START + /// + void PartialReset() { + stream_channels = 0; + hybrid_stereo_width_Q14 = 0; + variable_HP_smth2_Q15 = 0; + prev_HB_gain = 0; + Arrays.MemSet(hp_mem, 0, 4); + mode = OpusMode.MODE_UNKNOWN; + prev_mode = OpusMode.MODE_UNKNOWN; + prev_channels = 0; + prev_framesize = 0; + bandwidth = OpusBandwidth.OPUS_BANDWIDTH_UNKNOWN; + silk_bw_switch = 0; + first = 0; + energy_masking = null; + width_mem.Reset(); + Arrays.MemSet(delay_buffer, (short) 0, OpusConstants.MAX_ENCODER_BUFFER * 2); + detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_UNKNOWN; + rangeFinal = 0; + //SilkEncoder.Reset(); + //CeltEncoder.Reset(); + } + + public void resetState() { + EncControlState dummy = new EncControlState(); + analysis.Reset(); + PartialReset(); + + Celt_Encoder.ResetState(); + EncodeAPI.silk_InitEncoder(SilkEncoder, dummy); + stream_channels = channels; + hybrid_stereo_width_Q14 = 1 << 14; + prev_HB_gain = CeltConstants.Q15ONE; + first = 1; + mode = OpusMode.MODE_HYBRID; + bandwidth = OpusBandwidth.OPUS_BANDWIDTH_FULLBAND; + variable_HP_smth2_Q15 = Inlines.silk_LSHIFT(Inlines.silk_lin2log(TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ), 8); + } + + /** + * Allocates and initializes an encoder state. Note that regardless of the + * sampling rate and number channels selected, the Opus encoder can switch + * to a lower audio bandwidth or number of channels if the bitrate selected + * is too low. This also means that it is safe to always use 48 kHz stereo + * input and let the encoder optimize the encoding. The decoder will not be + * constrained later on by the mode that you select here for the encoder. + * + * @param Fs Sampling rate of input signal (Hz). This must be one of 8000, + * 12000, 16000, 24000, or 48000. + * @param channels Number of channels (1 or 2) in input signal + * @param application There are three coding modes: + * + * OPUS_APPLICATION_VOIP gives best quality at a given bitrate for voice + * signals. It enhances the input signal by high-pass filtering and + * emphasizing formants and harmonics.Optionally it includes in-band forward + * error correction to protect against packet loss. Use this mode for + * typical VoIP applications. Because of the enhancement, even at high + * bitrates the output may sound different from the input. + * + * OPUS_APPLICATION_AUDIO gives best quality at a given bitrate for most + * non-voice signals like music. Use this mode for music and mixed + * (music/voice) content, broadcast, and applications requiring less than 15 + * ms of coding delay. + * + * OPUS_APPLICATION_RESTRICTED_LOWDELAY configures low-delay mode that + * disables the speech-optimized mode in exchange for slightly reduced + * delay. This mode can only be set on an newly initialized or freshly reset + * encoder because it changes the codec delay. + * @throws OpusException + */ + public OpusEncoder(int Fs, int channels, OpusApplication application) throws OpusException { + int ret; + if ((Fs != 48000 && Fs != 24000 && Fs != 16000 && Fs != 12000 && Fs != 8000)) { + throw new IllegalArgumentException("Sample rate is invalid (must be 8/12/16/24/48 Khz)"); + } + if (channels != 1 && channels != 2) { + throw new IllegalArgumentException("Number of channels must be 1 or 2"); + } + + ret = this.opus_init_encoder(Fs, channels, application); + if (ret != OpusError.OPUS_OK) { + if (ret == OpusError.OPUS_BAD_ARG) { + throw new IllegalArgumentException("OPUS_BAD_ARG when creating encoder"); + } + throw new OpusException("Error while initializing encoder", ret); + } + } + + int opus_init_encoder(int Fs, int channels, OpusApplication application) { + SilkEncoder silk_enc; + CeltEncoder celt_enc; + int err; + int ret; + + if ((Fs != 48000 && Fs != 24000 && Fs != 16000 && Fs != 12000 && Fs != 8000) || (channels != 1 && channels != 2) + || application == OpusApplication.OPUS_APPLICATION_UNIMPLEMENTED) { + return OpusError.OPUS_BAD_ARG; + } + + this.reset(); + /* Create SILK encoder */ + silk_enc = this.SilkEncoder; + celt_enc = this.Celt_Encoder; + + this.stream_channels = this.channels = channels; + + this.Fs = Fs; + + ret = EncodeAPI.silk_InitEncoder(silk_enc, this.silk_mode); + if (ret != 0) { + return OpusError.OPUS_INTERNAL_ERROR; + } + + /* default SILK parameters */ + this.silk_mode.nChannelsAPI = channels; + this.silk_mode.nChannelsInternal = channels; + this.silk_mode.API_sampleRate = this.Fs; + this.silk_mode.maxInternalSampleRate = 16000; + this.silk_mode.minInternalSampleRate = 8000; + this.silk_mode.desiredInternalSampleRate = 16000; + this.silk_mode.payloadSize_ms = 20; + this.silk_mode.bitRate = 25000; + this.silk_mode.packetLossPercentage = 0; + this.silk_mode.complexity = 9; + this.silk_mode.useInBandFEC = 0; + this.silk_mode.useDTX = 0; + this.silk_mode.useCBR = 0; + this.silk_mode.reducedDependency = 0; + + /* Create CELT encoder */ + /* Initialize CELT encoder */ + err = celt_enc.celt_encoder_init(Fs, channels); + if (err != OpusError.OPUS_OK) { + return OpusError.OPUS_INTERNAL_ERROR; + } + + celt_enc.SetSignalling(0); + celt_enc.SetComplexity(this.silk_mode.complexity); + + this.use_vbr = 1; + /* Makes constrained VBR the default (safer for real-time use) */ + this.vbr_constraint = 1; + this.user_bitrate_bps = OpusConstants.OPUS_AUTO; + this.bitrate_bps = 3000 + Fs * channels; + this.application = application; + this.signal_type = OpusSignal.OPUS_SIGNAL_AUTO; + this.user_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_AUTO; + this.max_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_FULLBAND; + this.force_channels = OpusConstants.OPUS_AUTO; + this.user_forced_mode = OpusMode.MODE_AUTO; + this.voice_ratio = -1; + this.encoder_buffer = this.Fs / 100; + this.lsb_depth = 24; + this.variable_duration = OpusFramesize.OPUS_FRAMESIZE_ARG; + + /* Delay compensation of 4 ms (2.5 ms for SILK's extra look-ahead + + 1.5 ms for SILK resamplers and stereo prediction) */ + this.delay_compensation = this.Fs / 250; + + this.hybrid_stereo_width_Q14 = 1 << 14; + this.prev_HB_gain = CeltConstants.Q15ONE; + this.variable_HP_smth2_Q15 = Inlines.silk_LSHIFT(Inlines.silk_lin2log(TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ), 8); + this.first = 1; + this.mode = OpusMode.MODE_HYBRID; + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_FULLBAND; + + Analysis.tonality_analysis_init(this.analysis); + + return OpusError.OPUS_OK; + } + + int user_bitrate_to_bitrate(int frame_size, int max_data_bytes) { + if (frame_size == 0) { + frame_size = this.Fs / 400; + } + if (this.user_bitrate_bps == OpusConstants.OPUS_AUTO) { + return 60 * this.Fs / frame_size + this.Fs * this.channels; + } else if (this.user_bitrate_bps == OpusConstants.OPUS_BITRATE_MAX) { + return max_data_bytes * 8 * this.Fs / frame_size; + } else { + return this.user_bitrate_bps; + } + } + + /// + /// + /// + /// The storage type of analysis_pcm, either short or float + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + int opus_encode_native(short[] pcm, int pcm_ptr, int frame_size, + byte[] data, int data_ptr, int out_data_bytes, int lsb_depth, + short[] analysis_pcm, int analysis_pcm_ptr, int analysis_size, int c1, int c2, + int analysis_channels, int float_api) { + SilkEncoder silk_enc; + CeltEncoder celt_enc; + int i; + int ret = 0; + int nBytes; + EntropyCoder enc = new EntropyCoder(); // porting note: stack var + int bytes_target; + int prefill = 0; + int start_band = 0; + int redundancy = 0; + int redundancy_bytes = 0; + /* Number of bytes to use for redundancy frame */ + int celt_to_silk = 0; + short[] pcm_buf; + int nb_compr_bytes; + int to_celt = 0; + int redundant_rng = 0; + int cutoff_Hz, hp_freq_smth1; + int voice_est; + /* Probability of voice in Q7 */ + int equiv_rate; + int delay_compensation; + int frame_rate; + int max_rate; + /* Max bitrate we're allowed to use */ + OpusBandwidth curr_bandwidth; + int HB_gain; + int max_data_bytes; + /* Max number of bytes we're allowed to use */ + int total_buffer; + int stereo_width; + CeltMode celt_mode; + AnalysisInfo analysis_info = new AnalysisInfo(); // porting note: stack var + int analysis_read_pos_bak = -1; + int analysis_read_subframe_bak = -1; + short[] tmp_prefill; + + max_data_bytes = Inlines.IMIN(1276, out_data_bytes); + + this.rangeFinal = 0; + if ((this.variable_duration == OpusFramesize.OPUS_FRAMESIZE_UNKNOWN && 400 * frame_size != this.Fs && 200 * frame_size != this.Fs && 100 * frame_size != this.Fs + && 50 * frame_size != this.Fs && 25 * frame_size != this.Fs && 50 * frame_size != 3 * this.Fs) + || (400 * frame_size < this.Fs) + || max_data_bytes <= 0) { + return OpusError.OPUS_BAD_ARG; + } + + silk_enc = this.SilkEncoder; + celt_enc = this.Celt_Encoder; + if (this.application == OpusApplication.OPUS_APPLICATION_RESTRICTED_LOWDELAY) { + delay_compensation = 0; + } else { + delay_compensation = this.delay_compensation; + } + + lsb_depth = Inlines.IMIN(lsb_depth, this.lsb_depth); + celt_mode = celt_enc.GetMode(); + this.voice_ratio = -1; + + if (this.analysis.enabled) { + analysis_info.valid = 0; + if (this.silk_mode.complexity >= 7 && this.Fs == 48000) { + analysis_read_pos_bak = this.analysis.read_pos; + analysis_read_subframe_bak = this.analysis.read_subframe; + Analysis.run_analysis(this.analysis, + celt_mode, + analysis_pcm != null ? analysis_pcm : null, + analysis_pcm_ptr, + analysis_size, + frame_size, + c1, + c2, + analysis_channels, + this.Fs, + lsb_depth, + analysis_info); + } + + this.detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_UNKNOWN; + if (analysis_info.valid != 0) { + int analysis_bandwidth; + if (this.signal_type == OpusSignal.OPUS_SIGNAL_AUTO) { + this.voice_ratio = (int) Math.floor(.5f + 100 * (1 - analysis_info.music_prob)); + } + + analysis_bandwidth = analysis_info.bandwidth; + if (analysis_bandwidth <= 12) { + this.detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + } else if (analysis_bandwidth <= 14) { + this.detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND; + } else if (analysis_bandwidth <= 16) { + this.detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + } else if (analysis_bandwidth <= 18) { + this.detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND; + } else { + this.detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_FULLBAND; + } + } + } + + if (this.channels == 2 && this.force_channels != 1) { + stereo_width = CodecHelpers.compute_stereo_width(pcm, pcm_ptr, frame_size, this.Fs, this.width_mem); + } else { + stereo_width = 0; + } + total_buffer = delay_compensation; + this.bitrate_bps = user_bitrate_to_bitrate(frame_size, max_data_bytes); + + frame_rate = this.Fs / frame_size; + if (this.use_vbr == 0) { + int cbrBytes; + /* Multiply by 3 to make sure the division is exact. */ + int frame_rate3 = 3 * this.Fs / frame_size; + /* We need to make sure that "int" values always fit in 16 bits. */ + cbrBytes = Inlines.IMIN((3 * this.bitrate_bps / 8 + frame_rate3 / 2) / frame_rate3, max_data_bytes); + this.bitrate_bps = cbrBytes * frame_rate3 * 8 / 3; + max_data_bytes = cbrBytes; + } + if (max_data_bytes < 3 || this.bitrate_bps < 3 * frame_rate * 8 + || (frame_rate < 50 && (max_data_bytes * frame_rate < 300 || this.bitrate_bps < 2400))) { + /*If the space is too low to do something useful, emit 'PLC' frames.*/ + OpusMode tocmode = this.mode; + OpusBandwidth bw = this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_UNKNOWN ? OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND : this.bandwidth; + if (tocmode == OpusMode.MODE_UNKNOWN) { + tocmode = OpusMode.MODE_SILK_ONLY; + } + if (frame_rate > 100) { + tocmode = OpusMode.MODE_CELT_ONLY; + } + if (frame_rate < 50) { + tocmode = OpusMode.MODE_SILK_ONLY; + } + if (tocmode == OpusMode.MODE_SILK_ONLY && OpusBandwidthHelpers.GetOrdinal(bw) > OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND)) { + bw = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + } else if (tocmode == OpusMode.MODE_CELT_ONLY && OpusBandwidthHelpers.GetOrdinal(bw) == OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND)) { + bw = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + } else if (tocmode == OpusMode.MODE_HYBRID && OpusBandwidthHelpers.GetOrdinal(bw) <= OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND)) { + bw = OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND; + } + data[data_ptr] = CodecHelpers.gen_toc(tocmode, frame_rate, bw, this.stream_channels); + ret = 1; + if (this.use_vbr == 0) { + ret = OpusRepacketizer.padPacket(data, data_ptr, ret, max_data_bytes); + if (ret == OpusError.OPUS_OK) { + ret = max_data_bytes; + } + } + return ret; + } + max_rate = frame_rate * max_data_bytes * 8; + + /* Equivalent 20-ms rate for mode/channel/bandwidth decisions */ + equiv_rate = this.bitrate_bps - (40 * this.channels + 20) * (this.Fs / frame_size - 50); + + if (this.signal_type == OpusSignal.OPUS_SIGNAL_VOICE) { + voice_est = 127; + } else if (this.signal_type == OpusSignal.OPUS_SIGNAL_MUSIC) { + voice_est = 0; + } else if (this.voice_ratio >= 0) { + voice_est = this.voice_ratio * 327 >> 8; + /* For AUDIO, never be more than 90% confident of having speech */ + if (this.application == OpusApplication.OPUS_APPLICATION_AUDIO) { + voice_est = Inlines.IMIN(voice_est, 115); + } + } else if (this.application == OpusApplication.OPUS_APPLICATION_VOIP) { + voice_est = 115; + } else { + voice_est = 48; + } + + if (this.force_channels != OpusConstants.OPUS_AUTO && this.channels == 2) { + this.stream_channels = this.force_channels; + } else /* Rate-dependent mono-stereo decision */ if (this.channels == 2) { + int stereo_threshold; + stereo_threshold = OpusTables.stereo_music_threshold + ((voice_est * voice_est * (OpusTables.stereo_voice_threshold - OpusTables.stereo_music_threshold)) >> 14); + if (this.stream_channels == 2) { + stereo_threshold -= 1000; + } else { + stereo_threshold += 1000; + } + this.stream_channels = (equiv_rate > stereo_threshold) ? 2 : 1; + } else { + this.stream_channels = this.channels; + } + equiv_rate = this.bitrate_bps - (40 * this.stream_channels + 20) * (this.Fs / frame_size - 50); + + /* Mode selection depending on application and signal type */ + if (this.application == OpusApplication.OPUS_APPLICATION_RESTRICTED_LOWDELAY) { + this.mode = OpusMode.MODE_CELT_ONLY; + } else if (this.user_forced_mode == OpusMode.MODE_AUTO) { + int mode_voice, mode_music; + int threshold; + + /* Interpolate based on stereo width */ + mode_voice = Inlines.MULT16_32_Q15(CeltConstants.Q15ONE - stereo_width, OpusTables.mode_thresholds[0][0]) + + Inlines.MULT16_32_Q15(stereo_width, OpusTables.mode_thresholds[1][0]); + mode_music = Inlines.MULT16_32_Q15(CeltConstants.Q15ONE - stereo_width, OpusTables.mode_thresholds[1][1]) + + Inlines.MULT16_32_Q15(stereo_width, OpusTables.mode_thresholds[1][1]); + /* Interpolate based on speech/music probability */ + threshold = mode_music + ((voice_est * voice_est * (mode_voice - mode_music)) >> 14); + /* Bias towards SILK for VoIP because of some useful features */ + if (this.application == OpusApplication.OPUS_APPLICATION_VOIP) { + threshold += 8000; + } + + /*printf("%f %d\n", stereo_width/(float)1.0f, threshold);*/ + /* Hysteresis */ + if (this.prev_mode == OpusMode.MODE_CELT_ONLY) { + threshold -= 4000; + } else if (this.prev_mode != OpusMode.MODE_AUTO && this.prev_mode != OpusMode.MODE_UNKNOWN) { + threshold += 4000; + } + + this.mode = (equiv_rate >= threshold) ? OpusMode.MODE_CELT_ONLY : OpusMode.MODE_SILK_ONLY; + + /* When FEC is enabled and there's enough packet loss, use SILK */ + if (this.silk_mode.useInBandFEC != 0 && this.silk_mode.packetLossPercentage > (128 - voice_est) >> 4) { + this.mode = OpusMode.MODE_SILK_ONLY; + } + /* When encoding voice and DTX is enabled, set the encoder to SILK mode (at least for now) */ + if (this.silk_mode.useDTX != 0 && voice_est > 100) { + this.mode = OpusMode.MODE_SILK_ONLY; + } + } else { + this.mode = this.user_forced_mode; + } + + /* Override the chosen mode to make sure we meet the requested frame size */ + if (this.mode != OpusMode.MODE_CELT_ONLY && frame_size < this.Fs / 100) { + this.mode = OpusMode.MODE_CELT_ONLY; + } + if (this.lfe != 0) { + this.mode = OpusMode.MODE_CELT_ONLY; + } + /* If max_data_bytes represents less than 8 kb/s, switch to CELT-only mode */ + if (max_data_bytes < (frame_rate > 50 ? 12000 : 8000) * frame_size / (this.Fs * 8)) { + this.mode = OpusMode.MODE_CELT_ONLY; + } + + if (this.stream_channels == 1 && this.prev_channels == 2 && this.silk_mode.toMono == 0 + && this.mode != OpusMode.MODE_CELT_ONLY && this.prev_mode != OpusMode.MODE_CELT_ONLY) { + /* Delay stereo.mono transition by two frames so that SILK can do a smooth downmix */ + this.silk_mode.toMono = 1; + this.stream_channels = 2; + } else { + this.silk_mode.toMono = 0; + } + + if ((this.prev_mode != OpusMode.MODE_AUTO && this.prev_mode != OpusMode.MODE_UNKNOWN) + && ((this.mode != OpusMode.MODE_CELT_ONLY && this.prev_mode == OpusMode.MODE_CELT_ONLY) + || (this.mode == OpusMode.MODE_CELT_ONLY && this.prev_mode != OpusMode.MODE_CELT_ONLY))) { + redundancy = 1; + celt_to_silk = (this.mode != OpusMode.MODE_CELT_ONLY) ? 1 : 0; + if (celt_to_silk == 0) { + /* Switch to SILK/hybrid if frame size is 10 ms or more*/ + if (frame_size >= this.Fs / 100) { + this.mode = this.prev_mode; + to_celt = 1; + } else { + redundancy = 0; + } + } + } + /* For the first frame at a new SILK bandwidth */ + if (this.silk_bw_switch != 0) { + redundancy = 1; + celt_to_silk = 1; + this.silk_bw_switch = 0; + prefill = 1; + } + + if (redundancy != 0) { + /* Fair share of the max size allowed */ + redundancy_bytes = Inlines.IMIN(257, max_data_bytes * (this.Fs / 200) / (frame_size + this.Fs / 200)); + /* For VBR, target the actual bitrate (subject to the limit above) */ + if (this.use_vbr != 0) { + redundancy_bytes = Inlines.IMIN(redundancy_bytes, this.bitrate_bps / 1600); + } + } + + if (this.mode != OpusMode.MODE_CELT_ONLY && this.prev_mode == OpusMode.MODE_CELT_ONLY) { + EncControlState dummy = new EncControlState(); + EncodeAPI.silk_InitEncoder(silk_enc, dummy); + prefill = 1; + } + + /* Automatic (rate-dependent) bandwidth selection */ + if (this.mode == OpusMode.MODE_CELT_ONLY || this.first != 0 || this.silk_mode.allowBandwidthSwitch != 0) { + int[] voice_bandwidth_thresholds; + int[] music_bandwidth_thresholds; + int[] bandwidth_thresholds = new int[8]; + OpusBandwidth bandwidth = OpusBandwidth.OPUS_BANDWIDTH_FULLBAND; + int equiv_rate2; + + equiv_rate2 = equiv_rate; + if (this.mode != OpusMode.MODE_CELT_ONLY) { + /* Adjust the threshold +/- 10% depending on complexity */ + equiv_rate2 = equiv_rate2 * (45 + this.silk_mode.complexity) / 50; + /* CBR is less efficient by ~1 kb/s */ + if (this.use_vbr == 0) { + equiv_rate2 -= 1000; + } + } + if (this.channels == 2 && this.force_channels != 1) { + voice_bandwidth_thresholds = OpusTables.stereo_voice_bandwidth_thresholds; + music_bandwidth_thresholds = OpusTables.stereo_music_bandwidth_thresholds; + } else { + voice_bandwidth_thresholds = OpusTables.mono_voice_bandwidth_thresholds; + music_bandwidth_thresholds = OpusTables.mono_music_bandwidth_thresholds; + } + /* Interpolate bandwidth thresholds depending on voice estimation */ + for (i = 0; i < 8; i++) { + bandwidth_thresholds[i] = music_bandwidth_thresholds[i] + + ((voice_est * voice_est * (voice_bandwidth_thresholds[i] - music_bandwidth_thresholds[i])) >> 14); + } + do { + int threshold, hysteresis; + threshold = bandwidth_thresholds[2 * (OpusBandwidthHelpers.GetOrdinal(bandwidth) - OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND))]; + hysteresis = bandwidth_thresholds[2 * (OpusBandwidthHelpers.GetOrdinal(bandwidth) - OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND)) + 1]; + if (this.first == 0) { + if (OpusBandwidthHelpers.GetOrdinal(this.bandwidth) >= OpusBandwidthHelpers.GetOrdinal(bandwidth)) { + threshold -= hysteresis; + } else { + threshold += hysteresis; + } + } + if (equiv_rate2 >= threshold) { + break; + } + + bandwidth = OpusBandwidthHelpers.SUBTRACT(bandwidth, 1); + } while (OpusBandwidthHelpers.GetOrdinal(bandwidth) > OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND)); + this.bandwidth = bandwidth; + /* Prevents any transition to SWB/FB until the SILK layer has fully + switched to WB mode and turned the variable LP filter off */ + if (this.first == 0 && this.mode != OpusMode.MODE_CELT_ONLY + && this.silk_mode.inWBmodeWithoutVariableLP == 0 + && OpusBandwidthHelpers.GetOrdinal(this.bandwidth) > OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND)) { + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + } + } + + if (OpusBandwidthHelpers.GetOrdinal(this.bandwidth) > OpusBandwidthHelpers.GetOrdinal(this.max_bandwidth)) { + this.bandwidth = this.max_bandwidth; + } + + if (this.user_bandwidth != OpusBandwidth.OPUS_BANDWIDTH_AUTO) { + this.bandwidth = this.user_bandwidth; + } + + /* This prevents us from using hybrid at unsafe CBR/max rates */ + if (this.mode != OpusMode.MODE_CELT_ONLY && max_rate < 15000) { + this.bandwidth = OpusBandwidthHelpers.MIN(this.bandwidth, OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND); + } + + /* Prevents Opus from wasting bits on frequencies that are above + the Nyquist rate of the input signal */ + if (this.Fs <= 24000 && OpusBandwidthHelpers.GetOrdinal(this.bandwidth) > OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND)) { + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND; + } + if (this.Fs <= 16000 && OpusBandwidthHelpers.GetOrdinal(this.bandwidth) > OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND)) { + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + } + if (this.Fs <= 12000 && OpusBandwidthHelpers.GetOrdinal(this.bandwidth) > OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND)) { + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND; + } + if (this.Fs <= 8000 && OpusBandwidthHelpers.GetOrdinal(this.bandwidth) > OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND)) { + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + } + /* Use detected bandwidth to reduce the encoded bandwidth. */ + if (this.detected_bandwidth != OpusBandwidth.OPUS_BANDWIDTH_UNKNOWN && this.user_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_AUTO) { + OpusBandwidth min_detected_bandwidth; + /* Makes bandwidth detection more conservative just in case the detector + gets it wrong when we could have coded a high bandwidth transparently. + When operating in SILK/hybrid mode, we don't go below wideband to avoid + more complicated switches that require redundancy. */ + if (equiv_rate <= 18000 * this.stream_channels && this.mode == OpusMode.MODE_CELT_ONLY) { + min_detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + } else if (equiv_rate <= 24000 * this.stream_channels && this.mode == OpusMode.MODE_CELT_ONLY) { + min_detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND; + } else if (equiv_rate <= 30000 * this.stream_channels) { + min_detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + } else if (equiv_rate <= 44000 * this.stream_channels) { + min_detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND; + } else { + min_detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_FULLBAND; + } + + this.detected_bandwidth = OpusBandwidthHelpers.MAX(this.detected_bandwidth, min_detected_bandwidth); + this.bandwidth = OpusBandwidthHelpers.MIN(this.bandwidth, this.detected_bandwidth); + } + celt_enc.SetLSBDepth(lsb_depth); + + /* CELT mode doesn't support mediumband, use wideband instead */ + if (this.mode == OpusMode.MODE_CELT_ONLY && this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) { + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + } + if (this.lfe != 0) { + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + } + + /* Can't support higher than wideband for >20 ms frames */ + if (frame_size > this.Fs / 50 && (this.mode == OpusMode.MODE_CELT_ONLY || OpusBandwidthHelpers.GetOrdinal(this.bandwidth) > OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND))) { + byte[] tmp_data; + int nb_frames; + OpusBandwidth bak_bandwidth; + int bak_channels, bak_to_mono; + OpusMode bak_mode; + OpusRepacketizer rp; + int bytes_per_frame; + int repacketize_len; + + if (this.analysis.enabled && analysis_read_pos_bak != -1) { + this.analysis.read_pos = analysis_read_pos_bak; + this.analysis.read_subframe = analysis_read_subframe_bak; + } + + nb_frames = frame_size > this.Fs / 25 ? 3 : 2; + bytes_per_frame = Inlines.IMIN(1276, (out_data_bytes - 3) / nb_frames); + + tmp_data = new byte[nb_frames * bytes_per_frame]; + + rp = new OpusRepacketizer(); + + bak_mode = this.user_forced_mode; + bak_bandwidth = this.user_bandwidth; + bak_channels = this.force_channels; + + this.user_forced_mode = this.mode; + this.user_bandwidth = this.bandwidth; + this.force_channels = this.stream_channels; + bak_to_mono = this.silk_mode.toMono; + + if (bak_to_mono != 0) { + this.force_channels = 1; + } else { + this.prev_channels = this.stream_channels; + } + for (i = 0; i < nb_frames; i++) { + int tmp_len; + this.silk_mode.toMono = 0; + /* When switching from SILK/Hybrid to CELT, only ask for a switch at the last frame */ + if (to_celt != 0 && i == nb_frames - 1) { + this.user_forced_mode = OpusMode.MODE_CELT_ONLY; + } + tmp_len = opus_encode_native(pcm, pcm_ptr + (i * (this.channels * this.Fs / 50)), this.Fs / 50, + tmp_data, i * bytes_per_frame, bytes_per_frame, lsb_depth, + null, 0, 0, c1, c2, analysis_channels, float_api); + if (tmp_len < 0) { + + return OpusError.OPUS_INTERNAL_ERROR; + } + ret = rp.addPacket(tmp_data, i * bytes_per_frame, tmp_len); + if (ret < 0) { + + return OpusError.OPUS_INTERNAL_ERROR; + } + } + if (this.use_vbr != 0) { + repacketize_len = out_data_bytes; + } else { + repacketize_len = Inlines.IMIN(3 * this.bitrate_bps / (3 * 8 * 50 / nb_frames), out_data_bytes); + } + ret = rp.opus_repacketizer_out_range_impl(0, nb_frames, data, data_ptr, repacketize_len, 0, (this.use_vbr == 0) ? 1 : 0); + if (ret < 0) { + return OpusError.OPUS_INTERNAL_ERROR; + } + this.user_forced_mode = bak_mode; + this.user_bandwidth = bak_bandwidth; + this.force_channels = bak_channels; + this.silk_mode.toMono = bak_to_mono; + + return ret; + } + curr_bandwidth = this.bandwidth; + + /* Chooses the appropriate mode for speech + *NEVER* switch to/from CELT-only mode here as this will invalidate some assumptions */ + if (this.mode == OpusMode.MODE_SILK_ONLY && OpusBandwidthHelpers.GetOrdinal(curr_bandwidth) > OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND)) { + this.mode = OpusMode.MODE_HYBRID; + } + if (this.mode == OpusMode.MODE_HYBRID && OpusBandwidthHelpers.GetOrdinal(curr_bandwidth) <= OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND)) { + this.mode = OpusMode.MODE_SILK_ONLY; + } + + /* printf("%d %d %d %d\n", st.bitrate_bps, st.stream_channels, st.mode, curr_bandwidth); */ + bytes_target = Inlines.IMIN(max_data_bytes - redundancy_bytes, this.bitrate_bps * frame_size / (this.Fs * 8)) - 1; + + data_ptr += 1; + + enc.enc_init(data, data_ptr, (max_data_bytes - 1)); + + pcm_buf = new short[(total_buffer + frame_size) * this.channels]; + System.arraycopy(this.delay_buffer, ((this.encoder_buffer - total_buffer) * this.channels), pcm_buf, 0, total_buffer * this.channels); + + if (this.mode == OpusMode.MODE_CELT_ONLY) { + hp_freq_smth1 = Inlines.silk_LSHIFT(Inlines.silk_lin2log(TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ), 8); + } else { + hp_freq_smth1 = silk_enc.state_Fxx[0].variable_HP_smth1_Q15; + } + + this.variable_HP_smth2_Q15 = Inlines.silk_SMLAWB(this.variable_HP_smth2_Q15, + hp_freq_smth1 - this.variable_HP_smth2_Q15, ((int) ((TuningParameters.VARIABLE_HP_SMTH_COEF2) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_SMTH_COEF2, 16)*/); + + /* convert from log scale to Hertz */ + cutoff_Hz = Inlines.silk_log2lin(Inlines.silk_RSHIFT(this.variable_HP_smth2_Q15, 8)); + + if (this.application == OpusApplication.OPUS_APPLICATION_VOIP) { + CodecHelpers.hp_cutoff(pcm, pcm_ptr, cutoff_Hz, pcm_buf, (total_buffer * this.channels), this.hp_mem, frame_size, this.channels, this.Fs); + } else { + CodecHelpers.dc_reject(pcm, pcm_ptr, 3, pcm_buf, total_buffer * this.channels, this.hp_mem, frame_size, this.channels, this.Fs); + } + + /* SILK processing */ + HB_gain = CeltConstants.Q15ONE; + if (this.mode != OpusMode.MODE_CELT_ONLY) { + int total_bitRate, celt_rate; + short[] pcm_silk = new short[this.channels * frame_size]; + + /* Distribute bits between SILK and CELT */ + total_bitRate = 8 * bytes_target * frame_rate; + if (this.mode == OpusMode.MODE_HYBRID) { + int HB_gain_ref; + /* Base rate for SILK */ + this.silk_mode.bitRate = this.stream_channels * (5000 + ((this.Fs == 100 * frame_size) ? 1000 : 0)); + if (curr_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND) { + /* SILK gets 2/3 of the remaining bits */ + this.silk_mode.bitRate += (total_bitRate - this.silk_mode.bitRate) * 2 / 3; + } else { + /* FULLBAND */ + /* SILK gets 3/5 of the remaining bits */ + this.silk_mode.bitRate += (total_bitRate - this.silk_mode.bitRate) * 3 / 5; + } + /* Don't let SILK use more than 80% */ + if (this.silk_mode.bitRate > total_bitRate * 4 / 5) { + this.silk_mode.bitRate = total_bitRate * 4 / 5; + } + if (this.energy_masking == null) { + /* Increasingly attenuate high band when it gets allocated fewer bits */ + celt_rate = total_bitRate - this.silk_mode.bitRate; + HB_gain_ref = (curr_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND) ? 3000 : 3600; + HB_gain = Inlines.SHL32(celt_rate, 9) / Inlines.SHR32(celt_rate + this.stream_channels * HB_gain_ref, 6); + HB_gain = HB_gain < CeltConstants.Q15ONE * 6 / 7 ? HB_gain + CeltConstants.Q15ONE / 7 : CeltConstants.Q15ONE; + } + } else { + /* SILK gets all bits */ + this.silk_mode.bitRate = total_bitRate; + } + + /* Surround masking for SILK */ + if (this.energy_masking != null && this.use_vbr != 0 && this.lfe == 0) { + int mask_sum = 0; + int masking_depth; + int rate_offset; + int c; + int end = 17; + short srate = 16000; + if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) { + end = 13; + srate = 8000; + } else if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) { + end = 15; + srate = 12000; + } + for (c = 0; c < this.channels; c++) { + for (i = 0; i < end; i++) { + int mask; + mask = Inlines.MAX16(Inlines.MIN16(this.energy_masking[21 * c + i], + ((short) (0.5 + (.5f) * ((1) << (10))))/*Inlines.QCONST16(.5f, 10)*/), -((short) (0.5 + (2.0f) * ((1) << (10))))/*Inlines.QCONST16(2.0f, 10)*/); + if (mask > 0) { + mask = Inlines.HALF16(mask); + } + mask_sum += mask; + } + } + /* Conservative rate reduction, we cut the masking in half */ + masking_depth = mask_sum / end * this.channels; + masking_depth += ((short) (0.5 + (.2f) * ((1) << (10))))/*Inlines.QCONST16(.2f, 10)*/; + rate_offset = Inlines.PSHR32(Inlines.MULT16_16(srate, masking_depth), 10); + rate_offset = Inlines.MAX32(rate_offset, -2 * this.silk_mode.bitRate / 3); + /* Split the rate change between the SILK and CELT part for hybrid. */ + if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND || this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_FULLBAND) { + this.silk_mode.bitRate += 3 * rate_offset / 5; + } else { + this.silk_mode.bitRate += rate_offset; + } + bytes_target += rate_offset * frame_size / (8 * this.Fs); + } + + this.silk_mode.payloadSize_ms = 1000 * frame_size / this.Fs; + this.silk_mode.nChannelsAPI = this.channels; + this.silk_mode.nChannelsInternal = this.stream_channels; + if (curr_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) { + this.silk_mode.desiredInternalSampleRate = 8000; + } else if (curr_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) { + this.silk_mode.desiredInternalSampleRate = 12000; + } else { + Inlines.OpusAssert(this.mode == OpusMode.MODE_HYBRID || curr_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND); + this.silk_mode.desiredInternalSampleRate = 16000; + } + if (this.mode == OpusMode.MODE_HYBRID) { + /* Don't allow bandwidth reduction at lowest bitrates in hybrid mode */ + this.silk_mode.minInternalSampleRate = 16000; + } else { + this.silk_mode.minInternalSampleRate = 8000; + } + + if (this.mode == OpusMode.MODE_SILK_ONLY) { + int effective_max_rate = max_rate; + this.silk_mode.maxInternalSampleRate = 16000; + if (frame_rate > 50) { + effective_max_rate = effective_max_rate * 2 / 3; + } + if (effective_max_rate < 13000) { + this.silk_mode.maxInternalSampleRate = 12000; + this.silk_mode.desiredInternalSampleRate = Inlines.IMIN(12000, this.silk_mode.desiredInternalSampleRate); + } + if (effective_max_rate < 9600) { + this.silk_mode.maxInternalSampleRate = 8000; + this.silk_mode.desiredInternalSampleRate = Inlines.IMIN(8000, this.silk_mode.desiredInternalSampleRate); + } + } else { + this.silk_mode.maxInternalSampleRate = 16000; + } + + this.silk_mode.useCBR = this.use_vbr == 0 ? 1 : 0; + + /* Call SILK encoder for the low band */ + nBytes = Inlines.IMIN(1275, max_data_bytes - 1 - redundancy_bytes); + + this.silk_mode.maxBits = nBytes * 8; + /* Only allow up to 90% of the bits for hybrid mode*/ + if (this.mode == OpusMode.MODE_HYBRID) { + this.silk_mode.maxBits = this.silk_mode.maxBits * 9 / 10; + } + if (this.silk_mode.useCBR != 0) { + this.silk_mode.maxBits = (this.silk_mode.bitRate * frame_size / (this.Fs * 8)) * 8; + /* Reduce the initial target to make it easier to reach the CBR rate */ + this.silk_mode.bitRate = Inlines.IMAX(1, this.silk_mode.bitRate - 2000); + } + + if (prefill != 0) { + BoxedValueInt zero = new BoxedValueInt(0); + int prefill_offset; + + /* Use a smooth onset for the SILK prefill to avoid the encoder trying to encode + a discontinuity. The exact location is what we need to avoid leaving any "gap" + in the audio when mixing with the redundant CELT frame. Here we can afford to + overwrite st.delay_buffer because the only thing that uses it before it gets + rewritten is tmp_prefill[] and even then only the part after the ramp really + gets used (rather than sent to the encoder and discarded) */ + prefill_offset = this.channels * (this.encoder_buffer - this.delay_compensation - this.Fs / 400); + CodecHelpers.gain_fade(this.delay_buffer, prefill_offset, + 0, CeltConstants.Q15ONE, celt_mode.overlap, this.Fs / 400, this.channels, celt_mode.window, this.Fs); + Arrays.MemSet(this.delay_buffer, (short) 0, prefill_offset); + System.arraycopy(this.delay_buffer, 0, pcm_silk, 0, this.encoder_buffer * this.channels); + + EncodeAPI.silk_Encode(silk_enc, this.silk_mode, pcm_silk, this.encoder_buffer, null, zero, 1); + } + + System.arraycopy(pcm_buf, total_buffer * this.channels, pcm_silk, 0, frame_size * this.channels); + + BoxedValueInt boxed_silkBytes = new BoxedValueInt(nBytes); + ret = EncodeAPI.silk_Encode(silk_enc, this.silk_mode, pcm_silk, frame_size, enc, boxed_silkBytes, 0); + nBytes = boxed_silkBytes.Val; + + if (ret != 0) { + /*fprintf (stderr, "SILK encode error: %d\n", ret);*/ + /* Handle error */ + + return OpusError.OPUS_INTERNAL_ERROR; + } + if (nBytes == 0) { + this.rangeFinal = 0; + data[data_ptr - 1] = CodecHelpers.gen_toc(this.mode, this.Fs / frame_size, curr_bandwidth, this.stream_channels); + + return 1; + } + /* Extract SILK public bandwidth for signaling in first byte */ + if (this.mode == OpusMode.MODE_SILK_ONLY) { + if (this.silk_mode.internalSampleRate == 8000) { + curr_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + } else if (this.silk_mode.internalSampleRate == 12000) { + curr_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND; + } else if (this.silk_mode.internalSampleRate == 16000) { + curr_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + } + } else { + Inlines.OpusAssert(this.silk_mode.internalSampleRate == 16000); + } + + this.silk_mode.opusCanSwitch = this.silk_mode.switchReady; + if (this.silk_mode.opusCanSwitch != 0) { + redundancy = 1; + celt_to_silk = 0; + this.silk_bw_switch = 1; + } + } + + /* CELT processing */ + { + int endband = 21; + + switch (curr_bandwidth) { + case OPUS_BANDWIDTH_NARROWBAND: + endband = 13; + break; + case OPUS_BANDWIDTH_MEDIUMBAND: + case OPUS_BANDWIDTH_WIDEBAND: + endband = 17; + break; + case OPUS_BANDWIDTH_SUPERWIDEBAND: + endband = 19; + break; + case OPUS_BANDWIDTH_FULLBAND: + endband = 21; + break; + } + celt_enc.SetEndBand(endband); + celt_enc.SetChannels(this.stream_channels); + } + celt_enc.SetBitrate(OpusConstants.OPUS_BITRATE_MAX); + if (this.mode != OpusMode.MODE_SILK_ONLY) { + int celt_pred = 2; + celt_enc.SetVBR(false); + /* We may still decide to disable prediction later */ + if (this.silk_mode.reducedDependency != 0) { + celt_pred = 0; + } + celt_enc.SetPrediction(celt_pred); + + if (this.mode == OpusMode.MODE_HYBRID) { + int len; + + len = (enc.tell() + 7) >> 3; + if (redundancy != 0) { + len += this.mode == OpusMode.MODE_HYBRID ? 3 : 1; + } + if (this.use_vbr != 0) { + nb_compr_bytes = len + bytes_target - (this.silk_mode.bitRate * frame_size) / (8 * this.Fs); + } else { + /* check if SILK used up too much */ + nb_compr_bytes = len > bytes_target ? len : bytes_target; + } + } else if (this.use_vbr != 0) { + int bonus = 0; + if (this.analysis.enabled && this.variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE && frame_size != this.Fs / 50) { + bonus = (60 * this.stream_channels + 40) * (this.Fs / frame_size - 50); + if (analysis_info.valid != 0) { + bonus = (int) (bonus * (1.0f + .5f * analysis_info.tonality)); + } + } + celt_enc.SetVBR(true); + celt_enc.SetVBRConstraint(this.vbr_constraint != 0); + celt_enc.SetBitrate(this.bitrate_bps + bonus); + nb_compr_bytes = max_data_bytes - 1 - redundancy_bytes; + } else { + nb_compr_bytes = bytes_target; + } + + } else { + nb_compr_bytes = 0; + } + + tmp_prefill = new short[this.channels * this.Fs / 400]; + if (this.mode != OpusMode.MODE_SILK_ONLY && this.mode != this.prev_mode && (this.prev_mode != OpusMode.MODE_AUTO && this.prev_mode != OpusMode.MODE_UNKNOWN)) { + System.arraycopy(this.delay_buffer, ((this.encoder_buffer - total_buffer - this.Fs / 400) * this.channels), tmp_prefill, 0, this.channels * this.Fs / 400); + } + + if (this.channels * (this.encoder_buffer - (frame_size + total_buffer)) > 0) { + Arrays.MemMove(this.delay_buffer, this.channels * frame_size, 0, this.channels * (this.encoder_buffer - frame_size - total_buffer)); + System.arraycopy(pcm_buf, 0, this.delay_buffer, (this.channels * (this.encoder_buffer - frame_size - total_buffer)), (frame_size + total_buffer) * this.channels); + } else { + System.arraycopy(pcm_buf, (frame_size + total_buffer - this.encoder_buffer) * this.channels, this.delay_buffer, 0, this.encoder_buffer * this.channels); + } + + /* gain_fade() and stereo_fade() need to be after the buffer copying + because we don't want any of this to affect the SILK part */ + if (this.prev_HB_gain < CeltConstants.Q15ONE || HB_gain < CeltConstants.Q15ONE) { + CodecHelpers.gain_fade(pcm_buf, 0, + this.prev_HB_gain, HB_gain, celt_mode.overlap, frame_size, this.channels, celt_mode.window, this.Fs); + } + + this.prev_HB_gain = HB_gain; + if (this.mode != OpusMode.MODE_HYBRID || this.stream_channels == 1) { + this.silk_mode.stereoWidth_Q14 = Inlines.IMIN((1 << 14), 2 * Inlines.IMAX(0, equiv_rate - 30000)); + } + if (this.energy_masking == null && this.channels == 2) { + /* Apply stereo width reduction (at low bitrates) */ + if (this.hybrid_stereo_width_Q14 < (1 << 14) || this.silk_mode.stereoWidth_Q14 < (1 << 14)) { + int g1, g2; + g1 = this.hybrid_stereo_width_Q14; + g2 = (this.silk_mode.stereoWidth_Q14); + g1 = g1 == 16384 ? CeltConstants.Q15ONE : Inlines.SHL16(g1, 1); + g2 = g2 == 16384 ? CeltConstants.Q15ONE : Inlines.SHL16(g2, 1); + CodecHelpers.stereo_fade(pcm_buf, g1, g2, celt_mode.overlap, + frame_size, this.channels, celt_mode.window, this.Fs); + this.hybrid_stereo_width_Q14 = (short) (this.silk_mode.stereoWidth_Q14); + } + } + + if (this.mode != OpusMode.MODE_CELT_ONLY && enc.tell() + 17 + 20 * ((this.mode == OpusMode.MODE_HYBRID) ? 1 : 0) <= 8 * (max_data_bytes - 1)) { + /* For SILK mode, the redundancy is inferred from the length */ + if (this.mode == OpusMode.MODE_HYBRID && (redundancy != 0 || enc.tell() + 37 <= 8 * nb_compr_bytes)) { + enc.enc_bit_logp(redundancy, 12); + } + if (redundancy != 0) { + int max_redundancy; + enc.enc_bit_logp(celt_to_silk, 1); + if (this.mode == OpusMode.MODE_HYBRID) { + max_redundancy = (max_data_bytes - 1) - nb_compr_bytes; + } else { + max_redundancy = (max_data_bytes - 1) - ((enc.tell() + 7) >> 3); + } + /* Target the same bit-rate for redundancy as for the rest, + up to a max of 257 bytes */ + redundancy_bytes = Inlines.IMIN(max_redundancy, this.bitrate_bps / 1600); + redundancy_bytes = Inlines.IMIN(257, Inlines.IMAX(2, redundancy_bytes)); + if (this.mode == OpusMode.MODE_HYBRID) { + enc.enc_uint(redundancy_bytes - 2, 256); + } + } + } else { + redundancy = 0; + } + + if (redundancy == 0) { + this.silk_bw_switch = 0; + redundancy_bytes = 0; + } + if (this.mode != OpusMode.MODE_CELT_ONLY) { + start_band = 17; + } + + if (this.mode == OpusMode.MODE_SILK_ONLY) { + ret = (enc.tell() + 7) >> 3; + enc.enc_done(); + nb_compr_bytes = ret; + } else { + nb_compr_bytes = Inlines.IMIN((max_data_bytes - 1) - redundancy_bytes, nb_compr_bytes); + enc.enc_shrink(nb_compr_bytes); + } + + if (this.analysis.enabled && redundancy != 0 || this.mode != OpusMode.MODE_SILK_ONLY) { + analysis_info.enabled = this.analysis.enabled; + celt_enc.SetAnalysis(analysis_info); + } + /* 5 ms redundant frame for CELT->SILK */ + if (redundancy != 0 && celt_to_silk != 0) { + int err; + celt_enc.SetStartBand(0); + celt_enc.SetVBR(false); + err = celt_enc.celt_encode_with_ec(pcm_buf, 0, this.Fs / 200, data, data_ptr + nb_compr_bytes, redundancy_bytes, null); + if (err < 0) { + return OpusError.OPUS_INTERNAL_ERROR; + } + redundant_rng = celt_enc.GetFinalRange(); + celt_enc.ResetState(); + } + + celt_enc.SetStartBand(start_band); + + if (this.mode != OpusMode.MODE_SILK_ONLY) { + if (this.mode != this.prev_mode && (this.prev_mode != OpusMode.MODE_AUTO && this.prev_mode != OpusMode.MODE_UNKNOWN)) { + byte[] dummy = new byte[2]; + celt_enc.ResetState(); + + /* Prefilling */ + celt_enc.celt_encode_with_ec(tmp_prefill, 0, this.Fs / 400, dummy, 0, 2, null); + celt_enc.SetPrediction(0); + } + /* If false, we already busted the budget and we'll end up with a "PLC packet" */ + if (enc.tell() <= 8 * nb_compr_bytes) { + ret = celt_enc.celt_encode_with_ec(pcm_buf, 0, frame_size, null, 0, nb_compr_bytes, enc); + if (ret < 0) { + return OpusError.OPUS_INTERNAL_ERROR; + } + } + } + + /* 5 ms redundant frame for SILK->CELT */ + if (redundancy != 0 && celt_to_silk == 0) { + int err; + byte[] dummy = new byte[2]; + int N2, N4; + N2 = this.Fs / 200; + N4 = this.Fs / 400; + + celt_enc.ResetState(); + celt_enc.SetStartBand(0); + celt_enc.SetPrediction(0); + + /* NOTE: We could speed this up slightly (at the expense of code size) by just adding a function that prefills the buffer */ + celt_enc.celt_encode_with_ec(pcm_buf, (this.channels * (frame_size - N2 - N4)), N4, dummy, 0, 2, null); + + err = celt_enc.celt_encode_with_ec(pcm_buf, (this.channels * (frame_size - N2)), N2, data, data_ptr + nb_compr_bytes, redundancy_bytes, null); + if (err < 0) { + return OpusError.OPUS_INTERNAL_ERROR; + } + redundant_rng = celt_enc.GetFinalRange(); + } + + /* Signalling the mode in the first byte */ + data_ptr -= 1; + data[data_ptr] = CodecHelpers.gen_toc(this.mode, this.Fs / frame_size, curr_bandwidth, this.stream_channels); + + this.rangeFinal = ((int) enc.rng) ^ redundant_rng; + + if (to_celt != 0) { + this.prev_mode = OpusMode.MODE_CELT_ONLY; + } else { + this.prev_mode = this.mode; + } + this.prev_channels = this.stream_channels; + this.prev_framesize = frame_size; + + this.first = 0; + + /* In the unlikely case that the SILK encoder busted its target, tell + the decoder to call the PLC */ + if (enc.tell() > (max_data_bytes - 1) * 8) { + if (max_data_bytes < 2) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + data[data_ptr + 1] = 0; + ret = 1; + this.rangeFinal = 0; + } else if (this.mode == OpusMode.MODE_SILK_ONLY && redundancy == 0) { + /*When in LPC only mode it's perfectly + reasonable to strip off trailing zero bytes as + the required range decoder behavior is to + fill these in. This can't be done when the MDCT + modes are used because the decoder needs to know + the actual length for allocation purposes.*/ + while (ret > 2 && data[data_ptr + ret] == 0) { + ret--; + } + } + /* Count ToC and redundancy */ + ret += 1 + redundancy_bytes; + if (this.use_vbr == 0) { + if (OpusRepacketizer.padPacket(data, data_ptr, ret, max_data_bytes) != OpusError.OPUS_OK) { + return OpusError.OPUS_INTERNAL_ERROR; + } + ret = max_data_bytes; + } + + return ret; + } + + /** + * Encodes an Opus frame, putting the output into a specified data buffer + * @param in_pcm 16-bit input signal (Interleaved if stereo), in a short array. Length should be at least frame_size * channels + * @param pcm_offset Offset to use when reading the in_pcm buffer + * @param frame_size The number of samples _per channel_ in the inpus signal. The frame size must be a valid Opus framesize for the given sample rate. + * For example, at 48Khz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10ms + * (480 samples at 48Khz) will prevent the encoder from using FEC, DTX, or hybrid modes. + * @param out_data Destination buffer for the output payload. This must contain at least max_data_bytes + * @param out_data_offset The offset to use when writing to the output data buffer + * @param max_data_bytes The maximum amount of space allocated for the output payload. This may be used to impose + * an upper limit on the instant bitrate, but should not be used as the only bitrate control (use setBitrate for that) + * @return The length of the encoded packet, in bytes + * @throws OpusException + */ + public int encode(short[] in_pcm, int pcm_offset, int frame_size, + byte[] out_data, int out_data_offset, int max_data_bytes) throws OpusException { + // Check that the caller is telling the truth about its input buffers + if (out_data_offset + max_data_bytes > out_data.length) { + throw new IllegalArgumentException("Output buffer is too small: Stated size is " + max_data_bytes + " bytes, actual size is " + (out_data.length - out_data_offset) + " bytes"); + } + + int delay_compensation; + if (this.application == OpusApplication.OPUS_APPLICATION_RESTRICTED_LOWDELAY) { + delay_compensation = 0; + } else { + delay_compensation = this.delay_compensation; + } + + int internal_frame_size = CodecHelpers.compute_frame_size(in_pcm, pcm_offset, frame_size, + this.variable_duration, this.channels, this.Fs, this.bitrate_bps, + delay_compensation, this.analysis.subframe_mem, this.analysis.enabled); + + // Check that input pcm length is >= frame_size + if (pcm_offset + internal_frame_size > in_pcm.length) { + throw new IllegalArgumentException("Not enough samples provided in input signal: Expected " + internal_frame_size + " samples, found " + (in_pcm.length - pcm_offset)); + } + + try { + int ret = opus_encode_native(in_pcm, pcm_offset, internal_frame_size, out_data, out_data_offset, max_data_bytes, 16, + in_pcm, pcm_offset, frame_size, 0, -2, this.channels, 0); + + if (ret < 0) { + // An error happened; report it + if (ret == OpusError.OPUS_BAD_ARG) { + throw new IllegalArgumentException("OPUS_BAD_ARG while encoding"); + } + throw new OpusException("An error occurred during encoding", ret); + } + + return ret; + } catch (ArithmeticException e) { + throw new OpusException("Internal error during encoding: " + e.getMessage()); + } + } + + /** + * Encodes an Opus frame, putting the output into a specified data buffer + * @param in_pcm 16-bit input signal (Interleaved if stereo), in a little endian byte array. Length should be at least frame_size * channels * 2 + * @param pcm_offset Offset to use when reading the in_pcm buffer + * @param frame_size The number of samples _per channel_ in the inpus signal. The frame size must be a valid Opus framesize for the given sample rate. + * For example, at 48Khz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10ms + * (480 samples at 48Khz) will prevent the encoder from using FEC, DTX, or hybrid modes. + * @param out_data Destination buffer for the output payload. This must contain at least max_data_bytes + * @param out_data_offset The offset to use when writing to the output data buffer + * @param max_data_bytes The maximum amount of space allocated for the output payload. This may be used to impose + * an upper limit on the instant bitrate, but should not be used as the only bitrate control (use setBitrate for that) + * @return The length of the encoded packet, in bytes + * @throws OpusException + */ + public int encode(byte[] in_pcm, int pcm_offset, int frame_size, + byte[] out_data, int out_data_offset, int max_data_bytes) throws OpusException { + //Convert byte array to short array + short[] spcm = new short[frame_size * channels]; + for (int c = 0, idx = pcm_offset; c < spcm.length; idx += 2, c++) { + spcm[c] = (short) (((in_pcm[idx] & 0xff) | (in_pcm[idx + 1] << 8)) & 0xffff); + } + return encode(spcm, 0, frame_size, out_data, out_data_offset, max_data_bytes); + } + + /// + /// Gets or sets the application (or signal type) of the input signal. This hints + /// to the encoder what type of details we want to preserve in the encoding. + /// This cannot be changed after the encoder has started + /// + public OpusApplication getApplication() { + return application; + } + + public void setApplication(OpusApplication value) { + if (first == 0 && application != value) { + throw new IllegalArgumentException("Application cannot be changed after encoding has started"); + } + application = value; + } + + /// + /// Gets or sets the bitrate for encoder, in bits per second. Valid bitrates are be between 6K (6144) and 510K (522240) + /// + public int getBitrate() { + return user_bitrate_to_bitrate(prev_framesize, 1276); + } + + public void setBitrate(int value) { + if (value != OpusConstants.OPUS_AUTO && value != OpusConstants.OPUS_BITRATE_MAX) { + if (value <= 0) { + throw new IllegalArgumentException("Bitrate must be positive"); + } else if (value <= 500) { + value = 500; + } else if (value > 300000 * channels) { + value = 300000 * channels; + } + } + + user_bitrate_bps = value; + } + + /// + /// Gets or sets the maximum number of channels to be encoded. This can be used to force a downmix from stereo to mono if stereo + /// separation is not important + /// + public int getForceChannels() { + return force_channels; + } + + public void setForceChannels(int value) { + if ((value < 1 || value > channels) && value != OpusConstants.OPUS_AUTO) { + throw new IllegalArgumentException("Force channels must be <= num. of channels"); + } + + force_channels = value; + } + + /// + /// Gets or sets the maximum bandwidth to be used by the encoder. + /// + public OpusBandwidth getMaxBandwidth() { + return max_bandwidth; + } + + public void setMaxBandwidth(OpusBandwidth value) { + max_bandwidth = value; + if (max_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) { + silk_mode.maxInternalSampleRate = 8000; + } else if (max_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) { + silk_mode.maxInternalSampleRate = 12000; + } else { + silk_mode.maxInternalSampleRate = 16000; + } + } + + /// + /// Gets or sets the "preferred" encoded bandwidth + /// + public OpusBandwidth getBandwidth() { + return bandwidth; + } + + public void setBandwidth(OpusBandwidth value) { + user_bandwidth = value; + if (user_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) { + silk_mode.maxInternalSampleRate = 8000; + } else if (user_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) { + silk_mode.maxInternalSampleRate = 12000; + } else { + silk_mode.maxInternalSampleRate = 16000; + } + } + + /// + /// Gets or sets a flag to enable Discontinuous Transmission mode. This mode is only available in the SILK encoder + /// (Bitrate < 40Kbit/s and/or ForceMode == SILK). When enabled, the encoder detects silence and background noise + /// and reduces the number of output packets, with up to 600ms in between separate packet transmissions. + /// + public boolean getUseDTX() { + return silk_mode.useDTX != 0; + } + + public void setUseDTX(boolean value) { + silk_mode.useDTX = value ? 1 : 0; + } + + /// + /// Gets or sets the encoder complexity, between 0 and 10 + /// + public int getComplexity() { + return silk_mode.complexity; + } + + public void setComplexity(int value) { + if (value < 0 || value > 10) { + throw new IllegalArgumentException("Complexity must be between 0 and 10"); + } + silk_mode.complexity = value; + Celt_Encoder.SetComplexity(value); + } + + /// + /// Gets or sets a flag to enable Forward Error Correction. This mode is only available in the SILK encoder + /// (Bitrate < 40Kbit/s and/or ForceMode == SILK). When enabled, lost packets can be partially recovered + /// by decoding data stored in the following packet. + /// + public boolean getUseInbandFEC() { + return silk_mode.useInBandFEC != 0; + } + + public void setUseInbandFEC(boolean value) { + silk_mode.useInBandFEC = value ? 1 : 0; + } + + /// + /// Gets or sets the expected amount of packet loss in the transmission medium, from 0 to 100. + /// Only applies if UseInbandFEC is also enabled, and the encoder is in SILK mode. + /// + public int getPacketLossPercent() { + return silk_mode.packetLossPercentage; + } + + public void setPacketLossPercent(int value) { + if (value < 0 || value > 100) { + throw new IllegalArgumentException("Packet loss must be between 0 and 100"); + } + silk_mode.packetLossPercentage = value; + Celt_Encoder.SetPacketLossPercent(value); + } + + /// + /// Gets or sets a flag to enable Variable Bitrate encoding. This is recommended as it generally improves audio quality + /// with little impact on average bitrate + /// + public boolean getUseVBR() { + return use_vbr != 0; + } + + public void setUseVBR(boolean value) { + use_vbr = value ? 1 : 0; + silk_mode.useCBR = value ? 0 : 1; + } + + /// + /// Gets or sets a flag to enable constrained VBR. This only applies when the encoder is in CELT mode (i.e. high bitrates) + /// + public boolean getUseConstrainedVBR() { + return vbr_constraint != 0; + } + + public void setUseConstrainedVBR(boolean value) { + vbr_constraint = value ? 1 : 0; + } + + /// + /// Gets or sets a hint to the encoder for what type of audio is being processed, voice or music + /// + public OpusSignal getSignalType() { + return signal_type; + } + + public void setSignalType(OpusSignal value) { + signal_type = value; + } + + /// + /// Gets the number of samples of audio that are being stored in a buffer and are therefore contributing to latency. + /// + public int getLookahead() { + int returnVal = Fs / 400; + if (application != OpusApplication.OPUS_APPLICATION_RESTRICTED_LOWDELAY) { + returnVal += delay_compensation; + } + + return returnVal; + } + + /// + /// Gets the encoder's input sample rate. + /// + public int getSampleRate() { + return Fs; + } + + public int getFinalRange() { + return rangeFinal; + } + + /// + /// Gets or sets the bit resolution of the input audio signal. Though the encoder always uses 16-bit internally, this can help + /// it make better decisions about bandwidth and cutoff values + /// + public int getLSBDepth() { + return lsb_depth; + } + + public void setLSBDepth(int value) { + if (value < 8 || value > 24) { + throw new IllegalArgumentException("LSB depth must be between 8 and 24"); + } + + lsb_depth = value; + } + + /// + /// Gets or sets a fixed length for each encoded frame. Typically, the encoder just chooses a frame duration based on the input length + /// and the current mode. This can be used to enforce an exact length if it is required by your application (e.g. monotonous transmission) + /// + public OpusFramesize getExpertFrameDuration() { + return variable_duration; + } + + public void setExpertFrameDuration(OpusFramesize value) { + variable_duration = value; + Celt_Encoder.SetExpertFrameDuration(value); + } + + /// + /// Gets or sets a user-forced mode for the encoder. There are three modes, SILK, HYBRID, and CELT. Silk can only encode below 40Kbit/s and is best suited + /// for speech. Silk also has modes such as FEC which may be desirable. Celt sounds better at higher bandwidth and is comparable to AAC. + /// Hybrid is used to create a smooth transition between the two modes. Note that this value may not always be honored due to other factors such + /// as frame size and bitrate. + /// + public OpusMode getForceMode() { + return user_forced_mode; + } + + public void setForceMode(OpusMode value) { + user_forced_mode = value; + } + + /// + /// Gets or sets a value indicating that this stream is a low-frequency channel. This is used when encoding 5.1 surround audio. + /// + public boolean getIsLFE() { + return lfe != 0; + } + + public void setIsLFE(boolean value) { + lfe = value ? 1 : 0; + Celt_Encoder.SetLFE(value ? 1 : 0); + } + + /// + /// Gets or sets a flag to enable prediction, which does something. + /// + public boolean getPredictionDisabled() { + return silk_mode.reducedDependency != 0; + } + + public void setPredictionDisabled(boolean value) { + silk_mode.reducedDependency = value ? 1 : 0; + } + + /// + /// Gets or sets a value indicating whether neural net analysis functions should be enabled, increasing encode quality + /// at the expense of speed. + /// + public boolean getEnableAnalysis() { + return analysis.enabled; + } + + public void setEnableAnalysis(boolean value) { + analysis.enabled = value; + } + + void SetEnergyMask(int[] value) { + energy_masking = value; + Celt_Encoder.SetEnergyMask(value); + } + + CeltMode GetCeltMode() { + return Celt_Encoder.GetMode(); + } +} diff --git a/app/src/main/java/org/concentus/OpusError.java b/app/src/main/java/org/concentus/OpusError.java new file mode 100644 index 0000000000..0ebc230aaa --- /dev/null +++ b/app/src/main/java/org/concentus/OpusError.java @@ -0,0 +1,82 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Note that since most API-level errors are detected and thrown as +/// OpusExceptions, direct use of this class is not usually needed +/// +public class OpusError { + + /** + * No error + */ + public static int OPUS_OK = 0; + + /** + * One or more invalid/out of range arguments + */ + public static int OPUS_BAD_ARG = -1; + + /** + * Not enough bytes allocated in the buffer + */ + public static int OPUS_BUFFER_TOO_SMALL = -2; + + /** + * An public error was detected + */ + public static int OPUS_INTERNAL_ERROR = -3; + + /** + * The compressed data passed is corrupted + */ + public static int OPUS_INVALID_PACKET = -4; + + /** + * Invalid/unsupported request number + */ + public static int OPUS_UNIMPLEMENTED = -5; + + /** + * An encoder or decoder structure is invalid or already freed + */ + public static int OPUS_INVALID_STATE = -6; + + /** + * Memory allocation has failed + */ + public static int OPUS_ALLOC_FAIL = -7; +} diff --git a/app/src/main/java/org/concentus/OpusException.java b/app/src/main/java/org/concentus/OpusException.java new file mode 100644 index 0000000000..3b6d1f98a5 --- /dev/null +++ b/app/src/main/java/org/concentus/OpusException.java @@ -0,0 +1,55 @@ +/* Copyright (c) 2016 Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +public class OpusException extends Exception { + + private String _message; + private int _opus_error_code; + + public OpusException() { + this("", 0); + } + + public OpusException(String message) { + this(message, 1); + } + + public OpusException(String message, int opus_error_code) { + _message = message + ": " + CodecHelpers.opus_strerror(opus_error_code); + _opus_error_code = opus_error_code; + } + + @Override + public String getMessage() { + return _message; + } +} diff --git a/app/src/main/java/org/concentus/OpusFramesize.java b/app/src/main/java/org/concentus/OpusFramesize.java new file mode 100644 index 0000000000..e3b70774e1 --- /dev/null +++ b/app/src/main/java/org/concentus/OpusFramesize.java @@ -0,0 +1,100 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +public enum OpusFramesize { + /** + * Error state + */ + OPUS_FRAMESIZE_UNKNOWN, + /// + /// Select frame size from the argument (default) + /// + OPUS_FRAMESIZE_ARG, + /// + /// Use 2.5 ms frames + /// + OPUS_FRAMESIZE_2_5_MS, + /// + /// Use 5 ms frames + /// + OPUS_FRAMESIZE_5_MS, + /// + /// Use 10 ms frames + /// + OPUS_FRAMESIZE_10_MS, + /// + /// Use 20 ms frames + /// + OPUS_FRAMESIZE_20_MS, + /// + /// Use 40 ms frames + /// + OPUS_FRAMESIZE_40_MS, + /// + /// Use 60 ms frames + /// + OPUS_FRAMESIZE_60_MS, + /// + /// Do not use - not fully implemented. Optimize the frame size dynamically. + /// + OPUS_FRAMESIZE_VARIABLE +} + +class OpusFramesizeHelpers { + + static int GetOrdinal(OpusFramesize size) { + switch (size) { + case OPUS_FRAMESIZE_ARG: + return 1; + case OPUS_FRAMESIZE_2_5_MS: + return 2; + case OPUS_FRAMESIZE_5_MS: + return 3; + case OPUS_FRAMESIZE_10_MS: + return 4; + case OPUS_FRAMESIZE_20_MS: + return 5; + case OPUS_FRAMESIZE_40_MS: + return 6; + case OPUS_FRAMESIZE_60_MS: + return 7; + case OPUS_FRAMESIZE_VARIABLE: + return 8; + } + + return -1; + } +} diff --git a/app/src/main/java/org/concentus/OpusMSDecoder.java b/app/src/main/java/org/concentus/OpusMSDecoder.java new file mode 100644 index 0000000000..f670fbbbc9 --- /dev/null +++ b/app/src/main/java/org/concentus/OpusMSDecoder.java @@ -0,0 +1,340 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +public class OpusMSDecoder { + + ChannelLayout layout = new ChannelLayout(); + OpusDecoder[] decoders = null; + + private OpusMSDecoder(int nb_streams, int nb_coupled_streams) { + decoders = new OpusDecoder[nb_streams]; + for (int c = 0; c < nb_streams; c++) { + decoders[c] = new OpusDecoder(); + } + } + + int opus_multistream_decoder_init( + int Fs, + int channels, + int streams, + int coupled_streams, + short[] mapping + ) { + int i, ret; + int decoder_ptr = 0; + + if ((channels > 255) || (channels < 1) || (coupled_streams > streams) + || (streams < 1) || (coupled_streams < 0) || (streams > 255 - coupled_streams)) { + throw new IllegalArgumentException("Invalid channel or coupled stream count"); + } + + this.layout.nb_channels = channels; + this.layout.nb_streams = streams; + this.layout.nb_coupled_streams = coupled_streams; + + for (i = 0; i < this.layout.nb_channels; i++) { + this.layout.mapping[i] = mapping[i]; + } + if (OpusMultistream.validate_layout(this.layout) == 0) { + throw new IllegalArgumentException("Invalid surround channel layout"); + } + + for (i = 0; i < this.layout.nb_coupled_streams; i++) { + ret = this.decoders[decoder_ptr].opus_decoder_init(Fs, 2); + if (ret != OpusError.OPUS_OK) { + return ret; + } + decoder_ptr++; + } + for (; i < this.layout.nb_streams; i++) { + ret = this.decoders[decoder_ptr].opus_decoder_init(Fs, 1); + if (ret != OpusError.OPUS_OK) { + return ret; + } + decoder_ptr++; + } + return OpusError.OPUS_OK; + } + + /// + /// Creates a new MS decoder + /// + /// + /// + /// + /// + /// A mapping family (just use { 0, 1, 255 }) + /// + public static OpusMSDecoder create( + int Fs, + int channels, + int streams, + int coupled_streams, + short[] mapping) throws OpusException { + int ret; + OpusMSDecoder st; + if ((channels > 255) || (channels < 1) || (coupled_streams > streams) + || (streams < 1) || (coupled_streams < 0) || (streams > 255 - coupled_streams)) { + throw new IllegalArgumentException("Invalid channel / stream configuration"); + } + st = new OpusMSDecoder(streams, coupled_streams); + ret = st.opus_multistream_decoder_init(Fs, channels, streams, coupled_streams, mapping); + if (ret != OpusError.OPUS_OK) { + if (ret == OpusError.OPUS_BAD_ARG) { + throw new IllegalArgumentException("Bad argument while creating MS decoder"); + } + throw new OpusException("Could not create MS decoder", ret); + } + return st; + } + + static int opus_multistream_packet_validate(byte[] data, int data_ptr, + int len, int nb_streams, int Fs) { + int s; + int count; + BoxedValueByte toc = new BoxedValueByte((byte) 0); + short[] size = new short[48]; + int samples = 0; + BoxedValueInt packet_offset = new BoxedValueInt(0); + BoxedValueInt dummy = new BoxedValueInt(0); + + for (s = 0; s < nb_streams; s++) { + int tmp_samples; + if (len <= 0) { + return OpusError.OPUS_INVALID_PACKET; + } + + count = OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, (s != nb_streams - 1) ? 1 : 0, toc, null, 0, + size, 0, dummy, packet_offset); + if (count < 0) { + return count; + } + + tmp_samples = OpusPacketInfo.getNumSamples(data, data_ptr, packet_offset.Val, Fs); + if (s != 0 && samples != tmp_samples) { + return OpusError.OPUS_INVALID_PACKET; + } + samples = tmp_samples; + data_ptr += packet_offset.Val; + len -= packet_offset.Val; + } + + return samples; + } + + int opus_multistream_decode_native( + byte[] data, + int data_ptr, + int len, + short[] pcm, + int pcm_ptr, + int frame_size, + int decode_fec, + int soft_clip + ) { + int Fs; + int s, c; + int decoder_ptr; + int do_plc = 0; + short[] buf; + + /* Limit frame_size to avoid excessive stack allocations. */ + Fs = this.getSampleRate(); + frame_size = Inlines.IMIN(frame_size, Fs / 25 * 3); + buf = new short[2 * frame_size]; + decoder_ptr = 0; + + if (len == 0) { + do_plc = 1; + } + if (len < 0) { + return OpusError.OPUS_BAD_ARG; + } + if (do_plc == 0 && len < 2 * this.layout.nb_streams - 1) { + return OpusError.OPUS_INVALID_PACKET; + } + if (do_plc == 0) { + int ret = opus_multistream_packet_validate(data, data_ptr, len, this.layout.nb_streams, Fs); + if (ret < 0) { + return ret; + } else if (ret > frame_size) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + } + for (s = 0; s < this.layout.nb_streams; s++) { + OpusDecoder dec; + int ret; + + dec = this.decoders[decoder_ptr++]; + + if (do_plc == 0 && len <= 0) { + return OpusError.OPUS_INTERNAL_ERROR; + } + BoxedValueInt packet_offset = new BoxedValueInt(0); + ret = dec.opus_decode_native( + data, data_ptr, len, buf, 0, frame_size, decode_fec, + (s != this.layout.nb_streams - 1) ? 1 : 0, packet_offset, soft_clip); + data_ptr += packet_offset.Val; + len -= packet_offset.Val; + if (ret <= 0) { + return ret; + } + frame_size = ret; + if (s < this.layout.nb_coupled_streams) { + int chan, prev; + prev = -1; + /* Copy "left" audio to the channel(s) where it belongs */ + while ((chan = OpusMultistream.get_left_channel(this.layout, s, prev)) != -1) { + opus_copy_channel_out_short(pcm, pcm_ptr, this.layout.nb_channels, chan, + buf, 0, 2, frame_size); + prev = chan; + } + prev = -1; + /* Copy "right" audio to the channel(s) where it belongs */ + while ((chan = OpusMultistream.get_right_channel(this.layout, s, prev)) != -1) { + opus_copy_channel_out_short(pcm, pcm_ptr, this.layout.nb_channels, chan, + buf, 1, 2, frame_size); + prev = chan; + } + } else { + int chan, prev; + prev = -1; + /* Copy audio to the channel(s) where it belongs */ + while ((chan = OpusMultistream.get_mono_channel(this.layout, s, prev)) != -1) { + opus_copy_channel_out_short(pcm, pcm_ptr, this.layout.nb_channels, chan, + buf, 0, 1, frame_size); + prev = chan; + } + } + } + /* Handle muted channels */ + for (c = 0; c < this.layout.nb_channels; c++) { + if (this.layout.mapping[c] == 255) { + opus_copy_channel_out_short(pcm, pcm_ptr, this.layout.nb_channels, c, + null, 0, 0, frame_size); + } + } + + return frame_size; + } + + static void opus_copy_channel_out_short( + short[] dst, + int dst_ptr, + int dst_stride, + int dst_channel, + short[] src, + int src_ptr, + int src_stride, + int frame_size + ) { + int i; + if (src != null) { + for (i = 0; i < frame_size; i++) { + dst[i * dst_stride + dst_channel + dst_ptr] = src[i * src_stride + src_ptr]; + } + } else { + for (i = 0; i < frame_size; i++) { + dst[i * dst_stride + dst_channel + dst_ptr] = 0; + } + } + } + + public int decodeMultistream( + byte[] data, + int data_offset, + int len, + short[] out_pcm, + int out_pcm_offset, + int frame_size, + int decode_fec + ) { + return opus_multistream_decode_native(data, data_offset, len, + out_pcm, out_pcm_offset, frame_size, decode_fec, 0); + } + + public OpusBandwidth getBandwidth() { + if (decoders == null || decoders.length == 0) { + throw new IllegalStateException("Decoder not initialized"); + } + return decoders[0].getBandwidth(); + } + + public int getSampleRate() { + if (decoders == null || decoders.length == 0) { + throw new IllegalStateException("Decoder not initialized"); + } + return decoders[0].getSampleRate(); + } + + public int getGain() { + if (decoders == null || decoders.length == 0) { + throw new IllegalStateException("Decoder not initialized"); + } + return decoders[0].getGain(); + } + + public void setGain(int value) { + for (int s = 0; s < layout.nb_streams; s++) { + decoders[s].setGain(value); + } + } + + public int getLastPacketDuration() { + if (decoders == null || decoders.length == 0) { + return OpusError.OPUS_INVALID_STATE; + } + return decoders[0].getLastPacketDuration(); + } + + public int getFinalRange() { + int value = 0; + for (int s = 0; s < layout.nb_streams; s++) { + value ^= decoders[s].getFinalRange(); + } + return value; + } + + public void ResetState() { + for (int s = 0; s < layout.nb_streams; s++) { + decoders[s].resetState(); + } + } + + public OpusDecoder GetMultistreamDecoderState(int streamId) { + return decoders[streamId]; + } +} diff --git a/app/src/main/java/org/concentus/OpusMSEncoder.java b/app/src/main/java/org/concentus/OpusMSEncoder.java new file mode 100644 index 0000000000..48a8511256 --- /dev/null +++ b/app/src/main/java/org/concentus/OpusMSEncoder.java @@ -0,0 +1,947 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +public class OpusMSEncoder { + + final ChannelLayout layout = new ChannelLayout(); + int lfe_stream = 0; + OpusApplication application = OpusApplication.OPUS_APPLICATION_AUDIO; + OpusFramesize variable_duration = OpusFramesize.OPUS_FRAMESIZE_UNKNOWN; + int surround = 0; + int bitrate_bps = 0; + final float[] subframe_mem = new float[3]; + OpusEncoder[] encoders = null; + int[] window_mem = null; + int[] preemph_mem = null; + + private OpusMSEncoder(int nb_streams, int nb_coupled_streams) { + if (nb_streams < 1 || nb_coupled_streams > nb_streams || nb_coupled_streams < 0) { + throw new IllegalArgumentException("Invalid channel count in MS encoder"); + } + + encoders = new OpusEncoder[nb_streams]; + for (int c = 0; c < nb_streams; c++) { + encoders[c] = new OpusEncoder(); + } + + int nb_channels = (nb_coupled_streams * 2) /*stereo channels*/ + (nb_streams - nb_coupled_streams) /*mono channels*/; + window_mem = new int[nb_channels * 120]; + preemph_mem = new int[nb_channels]; + } + + public void resetState() { + int s; + subframe_mem[0] = subframe_mem[1] = subframe_mem[2] = 0; + if (surround != 0) { + Arrays.MemSet(preemph_mem, 0, layout.nb_channels); + Arrays.MemSet(window_mem, 0, layout.nb_channels * 120); + } + int encoder_ptr = 0; + for (s = 0; s < layout.nb_streams; s++) { + OpusEncoder enc = encoders[encoder_ptr++]; + enc.resetState(); + } + } + + static int validate_encoder_layout(ChannelLayout layout) { + int s; + for (s = 0; s < layout.nb_streams; s++) { + if (s < layout.nb_coupled_streams) { + if (OpusMultistream.get_left_channel(layout, s, -1) == -1) { + return 0; + } + if (OpusMultistream.get_right_channel(layout, s, -1) == -1) { + return 0; + } + } else if (OpusMultistream.get_mono_channel(layout, s, -1) == -1) { + return 0; + } + } + return 1; + } + + static void channel_pos(int channels, int[] pos/*[8]*/) { + /* Position in the mix: 0 don't mix, 1: left, 2: center, 3:right */ + if (channels == 4) { + pos[0] = 1; + pos[1] = 3; + pos[2] = 1; + pos[3] = 3; + } else if (channels == 3 || channels == 5 || channels == 6) { + pos[0] = 1; + pos[1] = 2; + pos[2] = 3; + pos[3] = 1; + pos[4] = 3; + pos[5] = 0; + } else if (channels == 7) { + pos[0] = 1; + pos[1] = 2; + pos[2] = 3; + pos[3] = 1; + pos[4] = 3; + pos[5] = 2; + pos[6] = 0; + } else if (channels == 8) { + pos[0] = 1; + pos[1] = 2; + pos[2] = 3; + pos[3] = 1; + pos[4] = 3; + pos[5] = 1; + pos[6] = 3; + pos[7] = 0; + } + } + + private static final int[] diff_table/*[17]*/ = { + ((short) (0.5 + (0.5000000f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.5000000f, CeltConstants.DB_SHIFT)*/, ((short) (0.5 + (0.2924813f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.2924813f, CeltConstants.DB_SHIFT)*/, ((short) (0.5 + (0.1609640f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.1609640f, CeltConstants.DB_SHIFT)*/, ((short) (0.5 + (0.0849625f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.0849625f, CeltConstants.DB_SHIFT)*/, + ((short) (0.5 + (0.0437314f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.0437314f, CeltConstants.DB_SHIFT)*/, ((short) (0.5 + (0.0221971f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.0221971f, CeltConstants.DB_SHIFT)*/, ((short) (0.5 + (0.0111839f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.0111839f, CeltConstants.DB_SHIFT)*/, ((short) (0.5 + (0.0056136f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.0056136f, CeltConstants.DB_SHIFT)*/, + ((short) (0.5 + (0.0028123f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(0.0028123f, CeltConstants.DB_SHIFT)*/, + 0, 0, 0, 0, 0, 0, 0, 0}; + + /* Computes a rough approximation of log2(2^a + 2^b) */ + static int logSum(int a, int b) { + int max; + int diff; + int frac; + + int low; + if (a > b) { + max = a; + diff = Inlines.SUB32(Inlines.EXTEND32(a), Inlines.EXTEND32(b)); + } else { + max = b; + diff = Inlines.SUB32(Inlines.EXTEND32(b), Inlines.EXTEND32(a)); + } + if (!(diff < ((short) (0.5 + (8.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(8.0f, CeltConstants.DB_SHIFT)*/)) /* inverted to catch NaNs */ { + return max; + } + low = Inlines.SHR32(diff, CeltConstants.DB_SHIFT - 1); + frac = Inlines.SHL16(diff - Inlines.SHL16(low, CeltConstants.DB_SHIFT - 1), 16 - CeltConstants.DB_SHIFT); + return max + diff_table[low] + Inlines.MULT16_16_Q15(frac, Inlines.SUB16(diff_table[low + 1], diff_table[low])); + } + + // fixme: test the perf of this alternate implementation + //int logSum(int a, int b) + //{ + // return log2(pow(4, a) + pow(4, b)) / 2; + //} + static void surround_analysis(CeltMode celt_mode, short[] pcm, int pcm_ptr, + int[] bandLogE, int[] mem, int[] preemph_mem, + int len, int overlap, int channels, int rate + ) { + int c; + int i; + int LM; + int[] pos = {0, 0, 0, 0, 0, 0, 0, 0}; + int upsample; + int frame_size; + int channel_offset; + int[][] bandE = Arrays.InitTwoDimensionalArrayInt(1, 21); + int[][] maskLogE = Arrays.InitTwoDimensionalArrayInt(3, 21); + int[] input; + short[] x; + int[][] freq; + + upsample = CeltCommon.resampling_factor(rate); + frame_size = len * upsample; + + for (LM = 0; LM < celt_mode.maxLM; LM++) { + if (celt_mode.shortMdctSize << LM == frame_size) { + break; + } + } + + input = new int[frame_size + overlap]; + x = new short[len]; + freq = Arrays.InitTwoDimensionalArrayInt(1, frame_size); + + channel_pos(channels, pos); + + for (c = 0; c < 3; c++) { + for (i = 0; i < 21; i++) { + maskLogE[c][i] = -((short) (0.5 + (28.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; + } + } + + for (c = 0; c < channels; c++) { + System.arraycopy(mem, c * overlap, input, 0, overlap); + opus_copy_channel_in_short(x, 0, 1, pcm, pcm_ptr, channels, c, len); + BoxedValueInt boxed_preemph = new BoxedValueInt(preemph_mem[c]); + CeltCommon.celt_preemphasis(x, input, overlap, frame_size, 1, upsample, celt_mode.preemph, boxed_preemph, 0); + preemph_mem[c] = boxed_preemph.Val; + + MDCT.clt_mdct_forward( + celt_mode.mdct, + input, + 0, + freq[0], + 0, + celt_mode.window, + overlap, + celt_mode.maxLM - LM, + 1); + if (upsample != 1) { + int bound = len; + for (i = 0; i < bound; i++) { + freq[0][i] *= upsample; + } + for (; i < frame_size; i++) { + freq[0][i] = 0; + } + } + + Bands.compute_band_energies(celt_mode, freq, bandE, 21, 1, LM); + QuantizeBands.amp2Log2(celt_mode, 21, 21, bandE[0], bandLogE, 21 * c, 1); + /* Apply spreading function with -6 dB/band going up and -12 dB/band going down. */ + for (i = 1; i < 21; i++) { + bandLogE[21 * c + i] = Inlines.MAX16(bandLogE[21 * c + i], bandLogE[21 * c + i - 1] - ((short) (0.5 + (1.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/); + } + for (i = 19; i >= 0; i--) { + bandLogE[21 * c + i] = Inlines.MAX16(bandLogE[21 * c + i], bandLogE[21 * c + i + 1] - ((short) (0.5 + (2.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(2.0f, CeltConstants.DB_SHIFT)*/); + } + if (pos[c] == 1) { + for (i = 0; i < 21; i++) { + maskLogE[0][i] = logSum(maskLogE[0][i], bandLogE[21 * c + i]); + } + } else if (pos[c] == 3) { + for (i = 0; i < 21; i++) { + maskLogE[2][i] = logSum(maskLogE[2][i], bandLogE[21 * c + i]); + } + } else if (pos[c] == 2) { + for (i = 0; i < 21; i++) { + maskLogE[0][i] = logSum(maskLogE[0][i], bandLogE[21 * c + i] - ((short) (0.5 + (.5f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/); + maskLogE[2][i] = logSum(maskLogE[2][i], bandLogE[21 * c + i] - ((short) (0.5 + (.5f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/); + } + } + System.arraycopy(input, frame_size, mem, c * overlap, overlap); + } + for (i = 0; i < 21; i++) { + maskLogE[1][i] = Inlines.MIN32(maskLogE[0][i], maskLogE[2][i]); + } + channel_offset = Inlines.HALF16(Inlines.celt_log2(((int) (0.5 + (2.0f) * (((int) 1) << (14))))/*Inlines.QCONST32(2.0f, 14)*/ / (channels - 1))); + for (c = 0; c < 3; c++) { + for (i = 0; i < 21; i++) { + maskLogE[c][i] += channel_offset; + } + } + + for (c = 0; c < channels; c++) { + int[] mask; + if (pos[c] != 0) { + mask = maskLogE[pos[c] - 1]; + for (i = 0; i < 21; i++) { + bandLogE[21 * c + i] = bandLogE[21 * c + i] - mask[i]; + } + } else { + for (i = 0; i < 21; i++) { + bandLogE[21 * c + i] = 0; + } + } + } + } + + int opus_multistream_encoder_init( + int Fs, + int channels, + int streams, + int coupled_streams, + short[] mapping, + OpusApplication application, + int surround + ) { + int i, ret; + int encoder_ptr; + + if ((channels > 255) || (channels < 1) || (coupled_streams > streams) + || (streams < 1) || (coupled_streams < 0) || (streams > 255 - coupled_streams)) { + return OpusError.OPUS_BAD_ARG; + } + + this.layout.nb_channels = channels; + this.layout.nb_streams = streams; + this.layout.nb_coupled_streams = coupled_streams; + this.subframe_mem[0] = this.subframe_mem[1] = this.subframe_mem[2] = 0; + if (surround == 0) { + this.lfe_stream = -1; + } + this.bitrate_bps = OpusConstants.OPUS_AUTO; + this.application = application; + this.variable_duration = OpusFramesize.OPUS_FRAMESIZE_ARG; + for (i = 0; i < this.layout.nb_channels; i++) { + this.layout.mapping[i] = mapping[i]; + } + if (OpusMultistream.validate_layout(this.layout) == 0 || validate_encoder_layout(this.layout) == 0) { + return OpusError.OPUS_BAD_ARG; + } + + encoder_ptr = 0; + + for (i = 0; i < this.layout.nb_coupled_streams; i++) { + ret = this.encoders[encoder_ptr].opus_init_encoder(Fs, 2, application); + if (ret != OpusError.OPUS_OK) { + return ret; + } + if (i == this.lfe_stream) { + this.encoders[encoder_ptr].setIsLFE(true); + } + encoder_ptr += 1; + } + for (; i < this.layout.nb_streams; i++) { + ret = this.encoders[encoder_ptr].opus_init_encoder(Fs, 1, application); + if (i == this.lfe_stream) { + this.encoders[encoder_ptr].setIsLFE(true); + } + if (ret != OpusError.OPUS_OK) { + return ret; + } + encoder_ptr += 1; + } + if (surround != 0) { + Arrays.MemSet(this.preemph_mem, 0, channels); + Arrays.MemSet(this.window_mem, 0, channels * 120); + } + this.surround = surround; + return OpusError.OPUS_OK; + } + + int opus_multistream_surround_encoder_init( + int Fs, + int channels, + int mapping_family, + BoxedValueInt streams, + BoxedValueInt coupled_streams, + short[] mapping, + OpusApplication application + ) { + streams.Val = 0; + coupled_streams.Val = 0; + if ((channels > 255) || (channels < 1)) { + return OpusError.OPUS_BAD_ARG; + } + this.lfe_stream = -1; + if (mapping_family == 0) { + if (channels == 1) { + streams.Val = 1; + coupled_streams.Val = 0; + mapping[0] = 0; + } else if (channels == 2) { + streams.Val = 1; + coupled_streams.Val = 1; + mapping[0] = 0; + mapping[1] = 1; + } else { + return OpusError.OPUS_UNIMPLEMENTED; + } + } else if (mapping_family == 1 && channels <= 8 && channels >= 1) { + int i; + streams.Val = VorbisLayout.vorbis_mappings[channels - 1].nb_streams; + coupled_streams.Val = VorbisLayout.vorbis_mappings[channels - 1].nb_coupled_streams; + for (i = 0; i < channels; i++) { + mapping[i] = VorbisLayout.vorbis_mappings[channels - 1].mapping[i]; + } + if (channels >= 6) { + this.lfe_stream = streams.Val - 1; + } + } else if (mapping_family == 255) { + byte i; + streams.Val = channels; + coupled_streams.Val = 0; + for (i = 0; i < channels; i++) { + mapping[i] = i; + } + } else { + return OpusError.OPUS_UNIMPLEMENTED; + } + return opus_multistream_encoder_init(Fs, channels, streams.Val, coupled_streams.Val, + mapping, application, (channels > 2 && mapping_family == 1) ? 1 : 0); + } + + public static OpusMSEncoder Create( + int Fs, + int channels, + int streams, + int coupled_streams, + short[] mapping, + OpusApplication application + ) throws OpusException { + int ret; + OpusMSEncoder st; + if ((channels > 255) || (channels < 1) || (coupled_streams > streams) + || (streams < 1) || (coupled_streams < 0) || (streams > 255 - coupled_streams)) { + throw new IllegalArgumentException("Invalid channel / stream configuration"); + } + st = new OpusMSEncoder(streams, coupled_streams); + ret = st.opus_multistream_encoder_init(Fs, channels, streams, coupled_streams, mapping, application, 0); + if (ret != OpusError.OPUS_OK) { + if (ret == OpusError.OPUS_BAD_ARG) { + throw new IllegalArgumentException("OPUS_BAD_ARG when creating MS encoder"); + } + throw new OpusException("Could not create MS encoder", ret); + } + return st; + } + + static void GetStreamCount(int channels, int mapping_family, BoxedValueInt nb_streams, BoxedValueInt nb_coupled_streams) { + if (mapping_family == 0) { + if (channels == 1) { + nb_streams.Val = 1; + nb_coupled_streams.Val = 0; + } else if (channels == 2) { + nb_streams.Val = 1; + nb_coupled_streams.Val = 1; + } else { + throw new IllegalArgumentException("More than 2 channels requires custom mappings"); + } + } else if (mapping_family == 1 && channels <= 8 && channels >= 1) { + nb_streams.Val = VorbisLayout.vorbis_mappings[channels - 1].nb_streams; + nb_coupled_streams.Val = VorbisLayout.vorbis_mappings[channels - 1].nb_coupled_streams; + } else if (mapping_family == 255) { + nb_streams.Val = channels; + nb_coupled_streams.Val = 0; + } else { + throw new IllegalArgumentException("Invalid mapping family"); + } + } + + public static OpusMSEncoder CreateSurround( + int Fs, + int channels, + int mapping_family, + BoxedValueInt streams, + BoxedValueInt coupled_streams, + short[] mapping, + OpusApplication application + ) throws OpusException { + int ret; + OpusMSEncoder st; + if ((channels > 255) || (channels < 1) || application == OpusApplication.OPUS_APPLICATION_UNIMPLEMENTED) { + throw new IllegalArgumentException("Invalid channel count or application"); + } + BoxedValueInt nb_streams = new BoxedValueInt(0); + BoxedValueInt nb_coupled_streams = new BoxedValueInt(0); + GetStreamCount(channels, mapping_family, nb_streams, nb_coupled_streams); + + st = new OpusMSEncoder(nb_streams.Val, nb_coupled_streams.Val); + ret = st.opus_multistream_surround_encoder_init(Fs, channels, mapping_family, streams, coupled_streams, mapping, application); + if (ret != OpusError.OPUS_OK) { + if (ret == OpusError.OPUS_BAD_ARG) { + throw new IllegalArgumentException("Bad argument passed to CreateSurround"); + } + throw new OpusException("Could not create multistream encoder", ret); + } + return st; + } + + int surround_rate_allocation( + int[] out_rates, + int frame_size + ) { + int i; + int channel_rate; + int Fs; + OpusEncoder ptr; + int stream_offset; + int lfe_offset; + int coupled_ratio; + /* Q8 */ + int lfe_ratio; + /* Q8 */ + int rate_sum = 0; + + ptr = this.encoders[0]; + Fs = ptr.getSampleRate(); + + if (this.bitrate_bps > this.layout.nb_channels * 40000) { + stream_offset = 20000; + } else { + stream_offset = this.bitrate_bps / this.layout.nb_channels / 2; + } + stream_offset += 60 * (Fs / frame_size - 50); + /* We start by giving each stream (coupled or uncoupled) the same bitrate. + This models the main saving of coupled channels over uncoupled. */ + /* The LFE stream is an exception to the above and gets fewer bits. */ + lfe_offset = 3500 + 60 * (Fs / frame_size - 50); + /* Coupled streams get twice the mono rate after the first 20 kb/s. */ + coupled_ratio = 512; + /* Should depend on the bitrate, for now we assume LFE gets 1/8 the bits of mono */ + lfe_ratio = 32; + + /* Compute bitrate allocation between streams */ + if (this.bitrate_bps == OpusConstants.OPUS_AUTO) { + channel_rate = Fs + 60 * Fs / frame_size; + } else if (this.bitrate_bps == OpusConstants.OPUS_BITRATE_MAX) { + channel_rate = 300000; + } else { + int nb_lfe; + int nb_uncoupled; + int nb_coupled; + int total; + nb_lfe = (this.lfe_stream != -1) ? 1 : 0; + nb_coupled = this.layout.nb_coupled_streams; + nb_uncoupled = this.layout.nb_streams - nb_coupled - nb_lfe; + total = (nb_uncoupled << 8) /* mono */ + + coupled_ratio * nb_coupled /* stereo */ + + nb_lfe * lfe_ratio; + channel_rate = 256 * (this.bitrate_bps - lfe_offset * nb_lfe - stream_offset * (nb_coupled + nb_uncoupled)) / total; + } + + for (i = 0; i < this.layout.nb_streams; i++) { + if (i < this.layout.nb_coupled_streams) { + out_rates[i] = stream_offset + (channel_rate * coupled_ratio >> 8); + } else if (i != this.lfe_stream) { + out_rates[i] = stream_offset + channel_rate; + } else { + out_rates[i] = lfe_offset + (channel_rate * lfe_ratio >> 8); + } + out_rates[i] = Inlines.IMAX(out_rates[i], 500); + rate_sum += out_rates[i]; + } + return rate_sum; + } + + /* Max size in case the encoder decides to return three frames */ + private final int MS_FRAME_TMP = (3 * 1275 + 7); + + int opus_multistream_encode_native( + short[] pcm, + int pcm_ptr, + int analysis_frame_size, + byte[] data, + int data_ptr, + int max_data_bytes, + int lsb_depth, + int float_api + ) { + int Fs; + int s; + int encoder_ptr; + int tot_size; + short[] buf; + int[] bandSMR; + byte[] tmp_data = new byte[MS_FRAME_TMP]; + OpusRepacketizer rp = new OpusRepacketizer(); + int vbr; + CeltMode celt_mode; + int[] bitrates = new int[256]; + int[] bandLogE = new int[42]; + int[] mem = null; + int[] preemph_mem = null; + int frame_size; + int rate_sum; + int smallest_packet; + + if (this.surround != 0) { + preemph_mem = this.preemph_mem; + mem = this.window_mem; + } + + encoder_ptr = 0; + Fs = this.encoders[encoder_ptr].getSampleRate(); + vbr = this.encoders[encoder_ptr].getUseVBR() ? 1 : 0; + celt_mode = this.encoders[encoder_ptr].GetCeltMode(); + + { + int delay_compensation; + int channels; + + channels = this.layout.nb_streams + this.layout.nb_coupled_streams; + delay_compensation = this.encoders[encoder_ptr].getLookahead(); + delay_compensation -= Fs / 400; + frame_size = CodecHelpers.compute_frame_size(pcm, pcm_ptr, analysis_frame_size, + this.variable_duration, channels, Fs, this.bitrate_bps, + delay_compensation, this.subframe_mem, this.encoders[encoder_ptr].analysis.enabled); + } + + if (400 * frame_size < Fs) { + return OpusError.OPUS_BAD_ARG; + } + /* Validate frame_size before using it to allocate stack space. + This mirrors the checks in opus_encode[_float](). */ + if (400 * frame_size != Fs && 200 * frame_size != Fs + && 100 * frame_size != Fs && 50 * frame_size != Fs + && 25 * frame_size != Fs && 50 * frame_size != 3 * Fs) { + return OpusError.OPUS_BAD_ARG; + } + + /* Smallest packet the encoder can produce. */ + smallest_packet = this.layout.nb_streams * 2 - 1; + if (max_data_bytes < smallest_packet) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + buf = new short[2 * frame_size]; + + bandSMR = new int[21 * this.layout.nb_channels]; + if (this.surround != 0) { + surround_analysis(celt_mode, pcm, pcm_ptr, bandSMR, mem, preemph_mem, frame_size, 120, this.layout.nb_channels, Fs); + } + + /* Compute bitrate allocation between streams (this could be a lot better) */ + rate_sum = surround_rate_allocation(bitrates, frame_size); + + if (vbr == 0) { + if (this.bitrate_bps == OpusConstants.OPUS_AUTO) { + max_data_bytes = Inlines.IMIN(max_data_bytes, 3 * rate_sum / (3 * 8 * Fs / frame_size)); + } else if (this.bitrate_bps != OpusConstants.OPUS_BITRATE_MAX) { + max_data_bytes = Inlines.IMIN(max_data_bytes, Inlines.IMAX(smallest_packet, + 3 * this.bitrate_bps / (3 * 8 * Fs / frame_size))); + } + } + + for (s = 0; s < this.layout.nb_streams; s++) { + OpusEncoder enc = this.encoders[encoder_ptr]; + encoder_ptr += 1; + enc.setBitrate(bitrates[s]); + if (this.surround != 0) { + int equiv_rate; + equiv_rate = this.bitrate_bps; + if (frame_size * 50 < Fs) { + equiv_rate -= 60 * (Fs / frame_size - 50) * this.layout.nb_channels; + } + if (equiv_rate > 10000 * this.layout.nb_channels) { + enc.setBandwidth(OpusBandwidth.OPUS_BANDWIDTH_FULLBAND); + } else if (equiv_rate > 7000 * this.layout.nb_channels) { + enc.setBandwidth(OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND); + } else if (equiv_rate > 5000 * this.layout.nb_channels) { + enc.setBandwidth(OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND); + } else { + enc.setBandwidth(OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND); + } + if (s < this.layout.nb_coupled_streams) { + /* To preserve the spatial image, force stereo CELT on coupled streams */ + enc.setForceMode(OpusMode.MODE_CELT_ONLY); + enc.setForceChannels(2); + } + } + } + + encoder_ptr = 0; + /* Counting ToC */ + tot_size = 0; + for (s = 0; s < this.layout.nb_streams; s++) { + OpusEncoder enc; + int len; + int curr_max; + int c1, c2; + + rp.Reset(); + enc = this.encoders[encoder_ptr]; + if (s < this.layout.nb_coupled_streams) { + int i; + int left, right; + left = OpusMultistream.get_left_channel(this.layout, s, -1); + right = OpusMultistream.get_right_channel(this.layout, s, -1); + opus_copy_channel_in_short(buf, 0, 2, + pcm, pcm_ptr, this.layout.nb_channels, left, frame_size); + opus_copy_channel_in_short(buf, 1, 2, + pcm, pcm_ptr, this.layout.nb_channels, right, frame_size); + encoder_ptr += 1; + if (this.surround != 0) { + for (i = 0; i < 21; i++) { + bandLogE[i] = bandSMR[21 * left + i]; + bandLogE[21 + i] = bandSMR[21 * right + i]; + } + } + c1 = left; + c2 = right; + } else { + int i; + int chan = OpusMultistream.get_mono_channel(this.layout, s, -1); + opus_copy_channel_in_short(buf, 0, 1, + pcm, pcm_ptr, this.layout.nb_channels, chan, frame_size); + encoder_ptr += 1; + if (this.surround != 0) { + for (i = 0; i < 21; i++) { + bandLogE[i] = bandSMR[21 * chan + i]; + } + } + c1 = chan; + c2 = -1; + } + if (this.surround != 0) { + enc.SetEnergyMask(bandLogE); + } + + /* number of bytes left (+Toc) */ + curr_max = max_data_bytes - tot_size; + /* Reserve one byte for the last stream and two for the others */ + curr_max -= Inlines.IMAX(0, 2 * (this.layout.nb_streams - s - 1) - 1); + curr_max = Inlines.IMIN(curr_max, MS_FRAME_TMP); + /* Repacketizer will add one or two bytes for self-delimited frames */ + if (s != this.layout.nb_streams - 1) { + curr_max -= curr_max > 253 ? 2 : 1; + } + if (vbr == 0 && s == this.layout.nb_streams - 1) { + enc.setBitrate(curr_max * (8 * Fs / frame_size)); + } + len = enc.opus_encode_native(buf, 0, frame_size, tmp_data, 0, curr_max, lsb_depth, + pcm, pcm_ptr, analysis_frame_size, c1, c2, this.layout.nb_channels, float_api); + if (len < 0) { + return len; + } + /* We need to use the repacketizer to add the self-delimiting lengths + while taking into account the fact that the encoder can now return + more than one frame at a time (e.g. 60 ms CELT-only) */ + rp.addPacket(tmp_data, 0, len); + len = rp.opus_repacketizer_out_range_impl(0, rp.getNumFrames(), + data, data_ptr, max_data_bytes - tot_size, (s != this.layout.nb_streams - 1) ? 1 : 0, (vbr == 0 && s == this.layout.nb_streams - 1) ? 1 : 0); + data_ptr += len; + tot_size += len; + } + + return tot_size; + } + + static void opus_copy_channel_in_short( + short[] dst, + int dst_ptr, + int dst_stride, + short[] src, + int src_ptr, + int src_stride, + int src_channel, + int frame_size + ) { + int i; + for (i = 0; i < frame_size; i++) { + dst[dst_ptr + i * dst_stride] = src[i * src_stride + src_channel + src_ptr]; + } + } + + public int encodeMultistream( + short[] pcm, + int pcm_offset, + int frame_size, + byte[] outputBuffer, + int outputBuffer_offset, + int max_data_bytes + ) { + // todo: catch error codes here + return opus_multistream_encode_native(pcm, pcm_offset, frame_size, outputBuffer, outputBuffer_offset, max_data_bytes, 16, 0); + } + + public int getBitrate() { + int s; + int value = 0; + int encoder_ptr = 0; + for (s = 0; s < layout.nb_streams; s++) { + OpusEncoder enc = encoders[encoder_ptr++]; + value += enc.getBitrate(); + } + return value; + } + + public void setBitrate(int value) { + if (value < 0 && value != OpusConstants.OPUS_AUTO && value != OpusConstants.OPUS_BITRATE_MAX) { + throw new IllegalArgumentException("Invalid bitrate"); + } + bitrate_bps = value; + } + + public OpusApplication getApplication() { + return encoders[0].getApplication(); + } + + public void setApplication(OpusApplication value) { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) { + encoders[encoder_ptr].setApplication(value); + } + } + + public int getForceChannels() { + return encoders[0].getForceChannels(); + } + + public void setForceChannels(int value) { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) { + encoders[encoder_ptr].setForceChannels(value); + } + } + + public OpusBandwidth getMaxBandwidth() { + return encoders[0].getMaxBandwidth(); + } + + public void setMaxBandwidth(OpusBandwidth value) { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) { + encoders[encoder_ptr].setMaxBandwidth(value); + } + } + + public OpusBandwidth getBandwidth() { + return encoders[0].getBandwidth(); + } + + public void setBandwidth(OpusBandwidth value) { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) { + encoders[encoder_ptr].setBandwidth(value); + } + } + + public boolean getUseDTX() { + return encoders[0].getUseDTX(); + } + + public void setUseDTX(boolean value) { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) { + encoders[encoder_ptr].setUseDTX(value); + } + } + + public int getComplexity() { + return encoders[0].getComplexity(); + } + + public void setComplexity(int value) { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) { + encoders[encoder_ptr].setComplexity(value); + } + } + + public OpusMode getForceMode() { + return encoders[0].getForceMode(); + } + + public void setForceMode(OpusMode value) { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) { + encoders[encoder_ptr].setForceMode(value); + } + } + + public boolean getUseInbandFEC() { + return encoders[0].getUseInbandFEC(); + } + + public void setUseInbandFEC(boolean value) { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) { + encoders[encoder_ptr].setUseInbandFEC(value); + } + } + + public int getPacketLossPercent() { + return encoders[0].getPacketLossPercent(); + } + + public void setPacketLossPercent(int value) { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) { + encoders[encoder_ptr].setPacketLossPercent(value); + } + } + + public boolean getUseVBR() { + return encoders[0].getUseVBR(); + } + + public void setUseVBR(boolean value) { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) { + encoders[encoder_ptr].setUseVBR(value); + } + } + + public boolean getUseConstrainedVBR() { + return encoders[0].getUseConstrainedVBR(); + } + + public void setUseConstrainedVBR(boolean value) { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) { + encoders[encoder_ptr].setUseConstrainedVBR(value); + } + } + + public OpusSignal getSignalType() { + return encoders[0].getSignalType(); + } + + public void setSignalType(OpusSignal value) { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) { + encoders[encoder_ptr].setSignalType(value); + } + } + + public int getLookahead() { + return encoders[0].getLookahead(); + } + + public int getSampleRate() { + return encoders[0].getSampleRate(); + } + + public int getFinalRange() { + int s; + int value = 0; + int encoder_ptr = 0; + for (s = 0; s < layout.nb_streams; s++) { + value ^= encoders[encoder_ptr++].getFinalRange(); + } + return value; + } + + public int getLSBDepth() { + return encoders[0].getLSBDepth(); + } + + public void setLSBDepth(int value) { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) { + encoders[encoder_ptr].setLSBDepth(value); + } + } + + public boolean getPredictionDisabled() { + return encoders[0].getPredictionDisabled(); + } + + public void setPredictionDisabled(boolean value) { + for (int encoder_ptr = 0; encoder_ptr < layout.nb_streams; encoder_ptr++) { + encoders[encoder_ptr].setPredictionDisabled(value); + } + } + + public OpusFramesize getExpertFrameDuration() { + return variable_duration; + } + + public void setExpertFrameDuration(OpusFramesize value) { + variable_duration = value; + } + + public OpusEncoder getMultistreamEncoderState(int streamId) { + if (streamId >= layout.nb_streams) { + throw new IllegalArgumentException("Requested stream doesn't exist"); + } + return encoders[streamId]; + } +} diff --git a/app/src/main/java/org/concentus/OpusMode.java b/app/src/main/java/org/concentus/OpusMode.java new file mode 100644 index 0000000000..c163bb3f1a --- /dev/null +++ b/app/src/main/java/org/concentus/OpusMode.java @@ -0,0 +1,43 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +public enum OpusMode { + MODE_UNKNOWN, + MODE_AUTO, + MODE_SILK_ONLY, + MODE_HYBRID, + MODE_CELT_ONLY +} diff --git a/app/src/main/java/org/concentus/OpusMultistream.java b/app/src/main/java/org/concentus/OpusMultistream.java new file mode 100644 index 0000000000..a8c79227cb --- /dev/null +++ b/app/src/main/java/org/concentus/OpusMultistream.java @@ -0,0 +1,86 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class OpusMultistream { + + static int validate_layout(ChannelLayout layout) { + int i, max_channel; + + max_channel = layout.nb_streams + layout.nb_coupled_streams; + if (max_channel > 255) { + return 0; + } + for (i = 0; i < layout.nb_channels; i++) { + if (layout.mapping[i] >= max_channel && layout.mapping[i] != 255) { + return 0; + } + } + return 1; + } + + static int get_left_channel(ChannelLayout layout, int stream_id, int prev) { + int i; + i = (prev < 0) ? 0 : prev + 1; + for (; i < layout.nb_channels; i++) { + if (layout.mapping[i] == stream_id * 2) { + return i; + } + } + return -1; + } + + static int get_right_channel(ChannelLayout layout, int stream_id, int prev) { + int i; + i = (prev < 0) ? 0 : prev + 1; + for (; i < layout.nb_channels; i++) { + if (layout.mapping[i] == stream_id * 2 + 1) { + return i; + } + } + return -1; + } + + static int get_mono_channel(ChannelLayout layout, int stream_id, int prev) { + int i; + i = (prev < 0) ? 0 : prev + 1; + for (; i < layout.nb_channels; i++) { + if (layout.mapping[i] == stream_id + layout.nb_coupled_streams) { + return i; + } + } + return -1; + } +} diff --git a/app/src/main/java/org/concentus/OpusPacketInfo.java b/app/src/main/java/org/concentus/OpusPacketInfo.java new file mode 100644 index 0000000000..8a9c2f2677 --- /dev/null +++ b/app/src/main/java/org/concentus/OpusPacketInfo.java @@ -0,0 +1,411 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +import java.util.ArrayList; +import java.util.List; + +public class OpusPacketInfo { + + /** + * The Table of Contents byte for this packet. Contains info about modes, frame length, etc. + */ + public byte TOCByte; + + /** + * The list of subframes in this packet + */ + public List Frames; + + /** + * The index of the start of the payload within the packet + */ + public int PayloadOffset; + + private OpusPacketInfo(byte toc, List frames, int payloadOffset) { + TOCByte = toc; + Frames = frames; + PayloadOffset = payloadOffset; + } + + /** + * Parse an opus packet into a packetinfo object containing one or more frames. + * Opus_decode will perform this operation internally so most applications do + * not need to use this function. + * @param packet The packet data to be parsed + * @param packet_offset The index of the beginning of the packet in the data array (usually 0) + * @param len The packet's length + * @return A parsed packet info struct + * @throws OpusException + */ + public static OpusPacketInfo parseOpusPacket(byte[] packet, int packet_offset, int len) throws OpusException { + // Find the number of frames first + int numFrames = getNumFrames(packet, packet_offset, len); + + BoxedValueInt payload_offset = new BoxedValueInt(0); + BoxedValueByte out_toc = new BoxedValueByte((byte) 0); + byte[][] frames = new byte[numFrames][]; + short[] size = new short[numFrames]; + BoxedValueInt packetOffset = new BoxedValueInt(0); + int error = opus_packet_parse_impl(packet, packet_offset, len, 0, out_toc, frames, 0, size, 0, payload_offset, packetOffset); + if (error < 0) { + throw new OpusException("An error occurred while parsing the packet", error); + } + + // Since packet_parse_impl has created deep copies of each frame, we can return them safely from this function without + // worrying about variable scoping or side effects + ArrayList copiedFrames = new ArrayList(); + for (int c = 0; c < frames.length; c++) { + // Java does not let us unbox an array (?) so we have to copy the bytes _again + Byte[] convertedFrame = new Byte[frames[c].length]; + for (int d = 0; d < convertedFrame.length; d++) { + convertedFrame[d] = frames[c][d]; + } + copiedFrames.add(convertedFrame); + } + + return new OpusPacketInfo(out_toc.Val, copiedFrames, payload_offset.Val); + } + + public static int getNumSamplesPerFrame(byte[] packet, int packet_offset, int Fs) { + int audiosize; + if ((packet[packet_offset] & 0x80) != 0) { + audiosize = ((packet[packet_offset] >> 3) & 0x3); + audiosize = (Fs << audiosize) / 400; + } else if ((packet[packet_offset] & 0x60) == 0x60) { + audiosize = ((packet[packet_offset] & 0x08) != 0) ? Fs / 50 : Fs / 100; + } else { + audiosize = ((packet[packet_offset] >> 3) & 0x3); + if (audiosize == 3) { + audiosize = Fs * 60 / 1000; + } else { + audiosize = (Fs << audiosize) / 100; + } + } + return audiosize; + } + + /// + /// Gets the encoded bandwidth of an Opus packet. Note that you are not forced to decode at this bandwidth + /// + /// An Opus packet (must be at least 1 byte) + /// An OpusBandwidth value + public static OpusBandwidth getBandwidth(byte[] packet, int packet_offset) { + OpusBandwidth bandwidth; + if ((packet[packet_offset] & 0x80) != 0) { + bandwidth = OpusBandwidthHelpers.GetBandwidth(OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) + ((packet[packet_offset] >> 5) & 0x3)); + if (bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) { + bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + } + } else if ((packet[packet_offset] & 0x60) == 0x60) { + bandwidth = ((packet[packet_offset] & 0x10) != 0) ? OpusBandwidth.OPUS_BANDWIDTH_FULLBAND + : OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND; + } else { + bandwidth = OpusBandwidthHelpers.GetBandwidth(OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) + ((packet[packet_offset] >> 5) & 0x3)); + } + return bandwidth; + } + + public static int getNumEncodedChannels(byte[] packet, int packet_offset) { + return ((packet[packet_offset] & 0x4) != 0) ? 2 : 1; + } + + /// + /// Gets the number of frames in an Opus packet. + /// + /// An Opus packet + /// The packet's length (must be at least 1) + /// The number of frames in the packet + public static int getNumFrames(byte[] packet, int packet_offset, int len) { + int count; + if (len < 1) { + return OpusError.OPUS_BAD_ARG; + } + count = packet[packet_offset] & 0x3; + if (count == 0) { + return 1; + } else if (count != 3) { + return 2; + } else if (len < 2) { + return OpusError.OPUS_INVALID_PACKET; + } else { + return packet[packet_offset + 1] & 0x3F; + } + } + + public static int getNumSamples(byte[] packet, int packet_offset, int len, + int Fs) { + int samples; + int count = getNumFrames(packet, packet_offset, len); + + if (count < 0) { + return count; + } + + samples = count * getNumSamplesPerFrame(packet, packet_offset, Fs); + /* Can't have more than 120 ms */ + if (samples * 25 > Fs * 3) { + return OpusError.OPUS_INVALID_PACKET; + } else { + return samples; + } + } + + /// + /// Gets the number of samples of an Opus packet. + /// + /// Your current decoder state + /// An Opus packet + /// The packet's length + /// The size of the PCM samples that this packet will be decoded to by the specified decoder + public static int getNumSamples(OpusDecoder dec, + byte[] packet, int packet_offset, int len) { + return getNumSamples(packet, packet_offset, len, dec.Fs); + } + + public static OpusMode getEncoderMode(byte[] packet, int packet_offset) { + OpusMode mode; + if ((packet[packet_offset] & 0x80) != 0) { + mode = OpusMode.MODE_CELT_ONLY; + } else if ((packet[packet_offset] & 0x60) == 0x60) { + mode = OpusMode.MODE_HYBRID; + } else { + mode = OpusMode.MODE_SILK_ONLY; + } + return mode; + } + + static int encode_size(int size, byte[] data, int data_ptr) { + if (size < 252) { + data[data_ptr] = (byte) (size & 0xFF); + return 1; + } else { + int dp1 = 252 + (size & 0x3); + data[data_ptr] = (byte) (dp1 & 0xFF); + data[data_ptr + 1] = (byte) ((size - dp1) >> 2); + return 2; + } + } + + static int parse_size(byte[] data, int data_ptr, int len, BoxedValueShort size) { + if (len < 1) { + size.Val = -1; + return -1; + } else if (Inlines.SignedByteToUnsignedInt(data[data_ptr]) < 252) { + size.Val = (short) Inlines.SignedByteToUnsignedInt(data[data_ptr]); + return 1; + } else if (len < 2) { + size.Val = -1; + return -1; + } else { + size.Val = (short) (4 * Inlines.SignedByteToUnsignedInt(data[data_ptr + 1]) + Inlines.SignedByteToUnsignedInt(data[data_ptr])); + return 2; + } + } + + static int opus_packet_parse_impl(byte[] data, int data_ptr, int len, + int self_delimited, BoxedValueByte out_toc, + byte[][] frames, int frames_ptr, short[] sizes, int sizes_ptr, + BoxedValueInt payload_offset, BoxedValueInt packet_offset) { + int i, bytes; + int count; + int cbr; + byte toc; + int ch; + int framesize; + int last_size; + int pad = 0; + int data0 = data_ptr; + out_toc.Val = 0; + payload_offset.Val = 0; + packet_offset.Val = 0; + + if (sizes == null || len < 0) { + return OpusError.OPUS_BAD_ARG; + } + if (len == 0) { + return OpusError.OPUS_INVALID_PACKET; + } + + framesize = getNumSamplesPerFrame(data, data_ptr, 48000); + + cbr = 0; + toc = data[data_ptr++]; + len--; + last_size = len; + switch (toc & 0x3) { + /* One frame */ + case 0: + count = 1; + break; + /* Two CBR frames */ + case 1: + count = 2; + cbr = 1; + if (self_delimited == 0) { + if ((len & 0x1) != 0) { + return OpusError.OPUS_INVALID_PACKET; + } + last_size = len / 2; + /* If last_size doesn't fit in size[0], we'll catch it later */ + sizes[sizes_ptr] = (short) last_size; + } + break; + /* Two VBR frames */ + case 2: + count = 2; + BoxedValueShort boxed_size = new BoxedValueShort(sizes[sizes_ptr]); + bytes = parse_size(data, data_ptr, len, boxed_size); + sizes[sizes_ptr] = boxed_size.Val; + len -= bytes; + if (sizes[sizes_ptr] < 0 || sizes[sizes_ptr] > len) { + return OpusError.OPUS_INVALID_PACKET; + } + data_ptr += bytes; + last_size = len - sizes[sizes_ptr]; + break; + /* Multiple CBR/VBR frames (from 0 to 120 ms) */ + default: + /*case 3:*/ + if (len < 1) { + return OpusError.OPUS_INVALID_PACKET; + } + /* Number of frames encoded in bits 0 to 5 */ + ch = Inlines.SignedByteToUnsignedInt(data[data_ptr++]); + count = ch & 0x3F; + if (count <= 0 || framesize * count > 5760) { + return OpusError.OPUS_INVALID_PACKET; + } + len--; + /* Padding flag is bit 6 */ + if ((ch & 0x40) != 0) { + int p; + do { + int tmp; + if (len <= 0) { + return OpusError.OPUS_INVALID_PACKET; + } + p = Inlines.SignedByteToUnsignedInt(data[data_ptr++]); + len--; + tmp = p == 255 ? 254 : p; + len -= tmp; + pad += tmp; + } while (p == 255); + } + if (len < 0) { + return OpusError.OPUS_INVALID_PACKET; + } + /* VBR flag is bit 7 */ + cbr = (ch & 0x80) != 0 ? 0 : 1; + if (cbr == 0) { + /* VBR case */ + last_size = len; + for (i = 0; i < count - 1; i++) { + boxed_size = new BoxedValueShort(sizes[sizes_ptr + i]); + bytes = parse_size(data, data_ptr, len, boxed_size); + sizes[sizes_ptr + i] = boxed_size.Val; + len -= bytes; + if (sizes[sizes_ptr + i] < 0 || sizes[sizes_ptr + i] > len) { + return OpusError.OPUS_INVALID_PACKET; + } + data_ptr += bytes; + last_size -= bytes + sizes[sizes_ptr + i]; + } + if (last_size < 0) { + return OpusError.OPUS_INVALID_PACKET; + } + } else if (self_delimited == 0) { + /* CBR case */ + last_size = len / count; + if (last_size * count != len) { + return OpusError.OPUS_INVALID_PACKET; + } + for (i = 0; i < count - 1; i++) { + sizes[sizes_ptr + i] = (short) last_size; + } + } + break; + } + + /* Self-delimited framing has an extra size for the last frame. */ + if (self_delimited != 0) { + BoxedValueShort boxed_size = new BoxedValueShort(sizes[sizes_ptr + count - 1]); + bytes = parse_size(data, data_ptr, len, boxed_size); + sizes[sizes_ptr + count - 1] = boxed_size.Val; + len -= bytes; + if (sizes[sizes_ptr + count - 1] < 0 || sizes[sizes_ptr + count - 1] > len) { + return OpusError.OPUS_INVALID_PACKET; + } + data_ptr += bytes; + /* For CBR packets, apply the size to all the frames. */ + if (cbr != 0) { + if (sizes[sizes_ptr + count - 1] * count > len) { + return OpusError.OPUS_INVALID_PACKET; + } + for (i = 0; i < count - 1; i++) { + sizes[sizes_ptr + i] = sizes[sizes_ptr + count - 1]; + } + } else if (bytes + sizes[sizes_ptr + count - 1] > last_size) { + return OpusError.OPUS_INVALID_PACKET; + } + } else { + /* Because it's not encoded explicitly, it's possible the size of the + last packet (or all the packets, for the CBR case) is larger than + 1275. Reject them here.*/ + if (last_size > 1275) { + return OpusError.OPUS_INVALID_PACKET; + } + sizes[sizes_ptr + count - 1] = (short) last_size; + } + + payload_offset.Val = (int) (data_ptr - data0); + + for (i = 0; i < count; i++) { + if (frames != null) { + // The old code returned pointers to the single data array, but that can cause unwanted side effects. + // So I have replaced it with this code that creates a new copy of each frame. Slower, but more robust + frames[frames_ptr + i] = new byte[data.length - data_ptr]; + System.arraycopy(data, data_ptr, frames[frames_ptr + i], 0, data.length - data_ptr); + } + data_ptr += sizes[sizes_ptr + i]; + } + + packet_offset.Val = pad + (int) (data_ptr - data0); + + out_toc.Val = toc; + + return count; + } +} diff --git a/app/src/main/java/org/concentus/OpusRepacketizer.java b/app/src/main/java/org/concentus/OpusRepacketizer.java new file mode 100644 index 0000000000..4eb65deec1 --- /dev/null +++ b/app/src/main/java/org/concentus/OpusRepacketizer.java @@ -0,0 +1,547 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +public class OpusRepacketizer { + + byte toc = 0; + int nb_frames = 0; + final byte[][] frames = new byte[48][]; + final short[] len = new short[48]; + int framesize = 0; + + /** + * (Re)initializes a previously allocated repacketizer state. The state must + * be at least the size returned by opus_repacketizer_get_size(). This can + * be used for applications which use their own allocator instead of + * malloc(). It must also be called to reset the queue of packets waiting to + * be repacketized, which is necessary if the maximum packet duration of 120 + * ms is reached or if you wish to submit packets with a different Opus + * configuration (coding mode, audio bandwidth, frame size, or channel + * count). Failure to do so will prevent a new packet from being added with + * opus_repacketizer_cat(). + * + * @see opus_repacketizer_create + * @see opus_repacketizer_get_size + * @see opus_repacketizer_cat + * @param rp OpusRepacketizer*: The repacketizer state to + * (re)initialize. + */ + public void Reset() { + this.nb_frames = 0; + } + + /** + * Allocates memory and initializes the new repacketizer with + * opus_repacketizer_init(). + */ + public OpusRepacketizer() { + this.Reset(); + } + + int opus_repacketizer_cat_impl(byte[] data, int data_ptr, int len, int self_delimited) { + BoxedValueByte dummy_toc = new BoxedValueByte((byte) 0); + BoxedValueInt dummy_offset = new BoxedValueInt(0); + int curr_nb_frames, ret; + /* Set of check ToC */ + if (len < 1) { + return OpusError.OPUS_INVALID_PACKET; + } + + if (this.nb_frames == 0) { + this.toc = data[data_ptr]; + this.framesize = OpusPacketInfo.getNumSamplesPerFrame(data, data_ptr, 8000); + } else if ((this.toc & 0xFC) != (data[data_ptr] & 0xFC)) { + /*fprintf(stderr, "toc mismatch: 0x%x vs 0x%x\n", rp.toc, data[0]);*/ + return OpusError.OPUS_INVALID_PACKET; + } + curr_nb_frames = OpusPacketInfo.getNumFrames(data, data_ptr, len); + if (curr_nb_frames < 1) { + return OpusError.OPUS_INVALID_PACKET; + } + + /* Check the 120 ms maximum packet size */ + if ((curr_nb_frames + this.nb_frames) * this.framesize > 960) { + return OpusError.OPUS_INVALID_PACKET; + } + + ret = OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, self_delimited, dummy_toc, this.frames, this.nb_frames, this.len, this.nb_frames, dummy_offset, dummy_offset); + if (ret < 1) { + return ret; + } + + this.nb_frames += curr_nb_frames; + return OpusError.OPUS_OK; + } + + /** + * opus_repacketizer_cat. Add a packet to the current repacketizer state. + * This packet must match the configuration of any packets already submitted + * for repacketization since the last call to opus_repacketizer_init(). This + * means that it must have the same coding mode, audio bandwidth, frame + * size, and channel count. This can be checked in advance by examining the + * top 6 bits of the first byte of the packet, and ensuring they match the + * top 6 bits of the first byte of any previously submitted packet. The + * total duration of audio in the repacketizer state also must not exceed + * 120 ms, the maximum duration of a single packet, after adding this + * packet. + * + * The contents of the current repacketizer state can be extracted into new + * packets using opus_repacketizer_out() or opus_repacketizer_out_range(). + * + * In order to add a packet with a different configuration or to add more + * audio beyond 120 ms, you must clear the repacketizer state by calling + * opus_repacketizer_init(). If a packet is too large to add to the current + * repacketizer state, no part of it is added, even if it contains multiple + * frames, some of which might fit. If you wish to be able to add parts of + * such packets, you should first use another repacketizer to split the + * packet into pieces and add them individually. + * + * @see opus_repacketizer_out_range + * @see opus_repacketizer_out + * @see opus_repacketizer_init + * @param data : The packet data. The application must ensure this pointer + * remains valid until the next call to opus_repacketizer_init() or + * opus_repacketizer_destroy(). + * @param len: The number of bytes in the packet data. + * @returns An error code indicating whether or not the operation succeeded. + * @retval #OPUS_OK The packet's contents have been added to the + * repacketizer state. + * @retval #OPUS_INVALID_PACKET The packet did not have a valid TOC + * sequence, the packet's TOC sequence was not compatible with previously + * submitted packets (because the coding mode, audio bandwidth, frame size, + * or channel count did not match), or adding this packet would increase the + * total amount of audio stored in the repacketizer state to more than 120 + * ms. + */ + public int addPacket(byte[] data, int data_offset, int len) { + return opus_repacketizer_cat_impl(data, data_offset, len, 0); + } + + /** + * Return the total number of frames contained in packet data submitted to + * the repacketizer state so far via opus_repacketizer_cat() since the last + * call to opus_repacketizer_init() or opus_repacketizer_create(). This + * defines the valid range of packets that can be extracted with + * opus_repacketizer_out_range() or opus_repacketizer_out(). + * + * @param rp OpusRepacketizer*: The repacketizer state containing + * the frames. + * @returns The total number of frames contained in the packet data + * submitted to the repacketizer state. + */ + public int getNumFrames() { + return this.nb_frames; + } + + int opus_repacketizer_out_range_impl(int begin, int end, + byte[] data, int data_ptr, int maxlen, int self_delimited, int pad) { + int i, count; + int tot_size; + int ptr; + + if (begin < 0 || begin >= end || end > this.nb_frames) { + /*fprintf(stderr, "%d %d %d\n", begin, end, rp.nb_frames);*/ + return OpusError.OPUS_BAD_ARG; + } + count = end - begin; + + if (self_delimited != 0) { + tot_size = 1 + (this.len[count - 1] >= 252 ? 1 : 0); + } else { + tot_size = 0; + } + + ptr = data_ptr; + if (count == 1) { + /* Code 0 */ + tot_size += this.len[0] + 1; + if (tot_size > maxlen) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + data[ptr++] = (byte) (this.toc & 0xFC); + } else if (count == 2) { + if (this.len[1] == this.len[0]) { + /* Code 1 */ + tot_size += 2 * this.len[0] + 1; + if (tot_size > maxlen) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + data[ptr++] = (byte) ((this.toc & 0xFC) | 0x1); + } else { + /* Code 2 */ + tot_size += this.len[0] + this.len[1] + 2 + (this.len[0] >= 252 ? 1 : 0); + if (tot_size > maxlen) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + data[ptr++] = (byte) ((this.toc & 0xFC) | 0x2); + ptr += OpusPacketInfo.encode_size(this.len[0], data, ptr); + } + } + if (count > 2 || (pad != 0 && tot_size < maxlen)) { + /* Code 3 */ + int vbr; + int pad_amount = 0; + + /* Restart the process for the padding case */ + ptr = data_ptr; + if (self_delimited != 0) { + tot_size = 1 + (this.len[count - 1] >= 252 ? 1 : 0); + } else { + tot_size = 0; + } + vbr = 0; + for (i = 1; i < count; i++) { + if (this.len[i] != this.len[0]) { + vbr = 1; + break; + } + } + if (vbr != 0) { + tot_size += 2; + for (i = 0; i < count - 1; i++) { + tot_size += 1 + (this.len[i] >= 252 ? 1 : 0) + this.len[i]; + } + tot_size += this.len[count - 1]; + + if (tot_size > maxlen) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + data[ptr++] = (byte) ((this.toc & 0xFC) | 0x3); + data[ptr++] = (byte) (count | 0x80); + } else { + tot_size += count * this.len[0] + 2; + if (tot_size > maxlen) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + data[ptr++] = (byte) ((this.toc & 0xFC) | 0x3); + data[ptr++] = (byte) (count); + } + + pad_amount = pad != 0 ? (maxlen - tot_size) : 0; + + if (pad_amount != 0) { + int nb_255s; + data[data_ptr + 1] = (byte) (data[data_ptr + 1] | 0x40); + nb_255s = (pad_amount - 1) / 255; + for (i = 0; i < nb_255s; i++) { + data[ptr++] = -1; + } + + data[ptr++] = (byte) (pad_amount - 255 * nb_255s - 1); + tot_size += pad_amount; + } + + if (vbr != 0) { + for (i = 0; i < count - 1; i++) { + ptr += (OpusPacketInfo.encode_size(this.len[i], data, ptr)); + } + } + } + + if (self_delimited != 0) { + int sdlen = OpusPacketInfo.encode_size(this.len[count - 1], data, ptr); + ptr += (sdlen); + } + + /* Copy the actual data */ + for (i = begin; i < count + begin; i++) { + + if (data == this.frames[i]) { + /* Using OPUS_MOVE() instead of OPUS_COPY() in case we're doing in-place + padding from opus_packet_pad or opus_packet_unpad(). */ + Arrays.MemMove(data, 0, ptr, this.len[i]); + } else { + System.arraycopy(this.frames[i], 0, data, ptr, this.len[i]); + } + ptr += this.len[i]; + } + + if (pad != 0) { + /* Fill padding with zeros. */ + Arrays.MemSetWithOffset(data, (byte) 0, ptr, data_ptr + maxlen - ptr); + } + + return tot_size; + } + + /** + * Construct a new packet from data previously submitted to the repacketizer + * state via opus_repacketizer_cat(). + * + * @param rp OpusRepacketizer*: The repacketizer state from which + * to construct the new packet. + * @param begin int: The index of the first frame in the current + * repacketizer state to include in the output. + * @param end int: One past the index of the last frame in the + * current repacketizer state to include in the output. + * @param[out] data final unsigned char*: The buffer in which to + * store the output packet. + * @param maxlen opus_int32: The maximum number of bytes to store + * in the output buffer. In order to guarantee success, this should be at + * least 1276 for a single frame, or for multiple frames, + * 1277*(end-begin). However, 1*(end-begin) plus + * the size of all packet data submitted to the repacketizer since the last + * call to opus_repacketizer_init() or opus_repacketizer_create() is also + * sufficient, and possibly much smaller. + * @returns The total size of the output packet on success, or an error code + * on failure. + * @retval #OPUS_BAD_ARG [begin,end) was an invalid range of + * frames (begin < 0, begin >= end, or end > + * opus_repacketizer_get_nb_frames()). + * @retval #OPUS_BUFFER_TOO_SMALL \a maxlen was insufficient to contain the + * complete output packet. + */ + public int createPacket(int begin, int end, byte[] data, int data_offset, int maxlen) { + return opus_repacketizer_out_range_impl(begin, end, data, data_offset, maxlen, 0, 0); + } + + /** + * Construct a new packet from data previously submitted to the repacketizer + * state via opus_repacketizer_cat(). This is a convenience routine that + * returns all the data submitted so far in a single packet. It is + * equivalent to calling + * @code + * opus_repacketizer_out_range(rp, 0, opus_repacketizer_get_nb_frames(rp), + * data, maxlen) + * @endcode + * @param rp OpusRepacketizer*: The repacketizer state from which to + * construct the new packet. + * @param[out] data final unsigned char*: The buffer in which to + * store the output packet. + * @param maxlen opus_int32: The maximum number of bytes to store in + * the output buffer. In order to guarantee + * success, this should be at least + * 1277*opus_repacketizer_get_nb_frames(rp). + * However, + * 1*opus_repacketizer_get_nb_frames(rp) + * plus the size of all packet data + * submitted to the repacketizer since the + * last call to opus_repacketizer_init() or + * opus_repacketizer_create() is also + * sufficient, and possibly much smaller. + * @returns The total size of the output packet on success, or an error code + * on failure. + * @retval #OPUS_BUFFER_TOO_SMALL \a maxlen was insufficient to contain the + * complete output packet. + */ + public int createPacket(byte[] data, int data_offset, int maxlen) { + return opus_repacketizer_out_range_impl(0, this.nb_frames, data, data_offset, maxlen, 0, 0); + } + + /** + * Pads a given Opus packet to a larger size (possibly changing the TOC + * sequence). + * + * @param[in,out] data final unsigned char*: The buffer containing + * the packet to pad. + * @param len opus_int32: The size of the packet. This must be at + * least 1. + * @param new_len opus_int32: The desired size of the packet after + * padding. This must be at least as large as len. + * @returns an error code + * @retval #OPUS_OK \a on success. + * @retval #OPUS_BAD_ARG \a len was less than 1 or new_len was less than + * len. + * @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet. + */ + public static int padPacket(byte[] data, int data_offset, int len, int new_len) { + OpusRepacketizer rp = new OpusRepacketizer(); + int ret; + if (len < 1) { + return OpusError.OPUS_BAD_ARG; + } + if (len == new_len) { + return OpusError.OPUS_OK; + } else if (len > new_len) { + return OpusError.OPUS_BAD_ARG; + } + rp.Reset(); + /* Moving payload to the end of the packet so we can do in-place padding */ + Arrays.MemMove(data, data_offset, data_offset + new_len - len, len); + //data.MemMoveTo(data.Point(new_len - len), len); + rp.addPacket(data, data_offset + new_len - len, len); + ret = rp.opus_repacketizer_out_range_impl(0, rp.nb_frames, data, data_offset, new_len, 0, 1); + if (ret > 0) { + return OpusError.OPUS_OK; + } else { + return ret; + } + } + + /** + * Remove all padding from a given Opus packet and rewrite the TOC sequence + * to minimize space usage. + * + * @param[in,out] data final unsigned char*: The buffer containing + * the packet to strip. + * @param len opus_int32: The size of the packet. This must be at + * least 1. + * @returns The new size of the output packet on success, or an error code + * on failure. + * @retval #OPUS_BAD_ARG \a len was less than 1. + * @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet. + */ + public static int unpadPacket(byte[] data, int data_offset, int len) { + int ret; + if (len < 1) { + return OpusError.OPUS_BAD_ARG; + } + + OpusRepacketizer rp = new OpusRepacketizer(); + rp.Reset(); + ret = rp.addPacket(data, data_offset, len); + if (ret < 0) { + return ret; + } + ret = rp.opus_repacketizer_out_range_impl(0, rp.nb_frames, data, data_offset, len, 0, 0); + Inlines.OpusAssert(ret > 0 && ret <= len); + + return ret; + } + + /** + * Pads a given Opus multi-stream packet to a larger size (possibly changing + * the TOC sequence). + * + * @param[in,out] data final unsigned char*: The buffer containing + * the packet to pad. + * @param len opus_int32: The size of the packet. This must be at + * least 1. + * @param new_len opus_int32: The desired size of the packet after + * padding. This must be at least 1. + * @param nb_streams opus_int32: The number of streams (not + * channels) in the packet. This must be at least as large as len. + * @returns an error code + * @retval #OPUS_OK \a on success. + * @retval #OPUS_BAD_ARG \a len was less than 1. + * @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet. + */ + public static int padMultistreamPacket(byte[] data, int data_offset, int len, int new_len, int nb_streams) { + int s; + int count; + BoxedValueByte dummy_toc = new BoxedValueByte((byte) 0); + short[] size = new short[48]; + BoxedValueInt packet_offset = new BoxedValueInt(0); + BoxedValueInt dummy_offset = new BoxedValueInt(0); + int amount; + + if (len < 1) { + return OpusError.OPUS_BAD_ARG; + } + if (len == new_len) { + return OpusError.OPUS_OK; + } else if (len > new_len) { + return OpusError.OPUS_BAD_ARG; + } + amount = new_len - len; + /* Seek to last stream */ + for (s = 0; s < nb_streams - 1; s++) { + if (len <= 0) { + return OpusError.OPUS_INVALID_PACKET; + } + count = OpusPacketInfo.opus_packet_parse_impl(data, data_offset, len, 1, dummy_toc, null, 0, + size, 0, dummy_offset, packet_offset); + if (count < 0) { + return count; + } + data_offset += packet_offset.Val; + len -= packet_offset.Val; + } + return padPacket(data, data_offset, len, len + amount); + } + + // FIXME THIS METHOD FAILS IN TEST_OPUS_ENCODE + /** + * Remove all padding from a given Opus multi-stream packet and rewrite the + * TOC sequence to minimize space usage. + * + * @param[in,out] data final unsigned char*: The buffer containing + * the packet to strip. + * @param len opus_int32: The size of the packet. This must be at + * least 1. + * @param nb_streams opus_int32: The number of streams (not + * channels) in the packet. This must be at least 1. + * @returns The new size of the output packet on success, or an error code + * on failure. + * @retval #OPUS_BAD_ARG \a len was less than 1 or new_len was less than + * len. + * @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet. + */ + public static int unpadMultistreamPacket(byte[] data, int data_offset, int len, int nb_streams) { + int s; + BoxedValueByte dummy_toc = new BoxedValueByte((byte) 0); + short[] size = new short[48]; + BoxedValueInt packet_offset = new BoxedValueInt(0); + BoxedValueInt dummy_offset = new BoxedValueInt(0); + OpusRepacketizer rp = new OpusRepacketizer(); + int dst; + int dst_len; + + if (len < 1) { + return OpusError.OPUS_BAD_ARG; + } + dst = data_offset; + dst_len = 0; + /* Unpad all frames */ + for (s = 0; s < nb_streams; s++) { + int ret; + int self_delimited = ((s != nb_streams) ? 1 : 0) - 1; + if (len <= 0) { + return OpusError.OPUS_INVALID_PACKET; + } + rp.Reset(); + ret = OpusPacketInfo.opus_packet_parse_impl(data, data_offset, len, self_delimited, dummy_toc, null, 0, + size, 0, dummy_offset, packet_offset); + if (ret < 0) { + return ret; + } + ret = rp.opus_repacketizer_cat_impl(data, data_offset, packet_offset.Val, self_delimited); + if (ret < 0) { + return ret; + } + ret = rp.opus_repacketizer_out_range_impl(0, rp.nb_frames, data, dst, len, self_delimited, 0); + if (ret < 0) { + return ret; + } else { + dst_len += ret; + } + dst += ret; + data_offset += packet_offset.Val; + len -= packet_offset.Val; + } + return dst_len; + } +} diff --git a/app/src/main/java/org/concentus/OpusSignal.java b/app/src/main/java/org/concentus/OpusSignal.java new file mode 100644 index 0000000000..7319cb6721 --- /dev/null +++ b/app/src/main/java/org/concentus/OpusSignal.java @@ -0,0 +1,48 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +public enum OpusSignal { + OPUS_SIGNAL_UNKNOWN, + OPUS_SIGNAL_AUTO, + /// + /// Signal being encoded is voice + /// + OPUS_SIGNAL_VOICE, + /// + /// Signal being encoded is music + /// + OPUS_SIGNAL_MUSIC +} diff --git a/app/src/main/java/org/concentus/OpusTables.java b/app/src/main/java/org/concentus/OpusTables.java new file mode 100644 index 0000000000..a8191b7006 --- /dev/null +++ b/app/src/main/java/org/concentus/OpusTables.java @@ -0,0 +1,280 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class OpusTables { + + static final float[] dct_table = { + 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, + 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, + 0.351851f, 0.338330f, 0.311806f, 0.273300f, 0.224292f, 0.166664f, 0.102631f, 0.034654f, + -0.034654f, -0.102631f, -0.166664f, -0.224292f, -0.273300f, -0.311806f, -0.338330f, -0.351851f, + 0.346760f, 0.293969f, 0.196424f, 0.068975f, -0.068975f, -0.196424f, -0.293969f, -0.346760f, + -0.346760f, -0.293969f, -0.196424f, -0.068975f, 0.068975f, 0.196424f, 0.293969f, 0.346760f, + 0.338330f, 0.224292f, 0.034654f, -0.166664f, -0.311806f, -0.351851f, -0.273300f, -0.102631f, + 0.102631f, 0.273300f, 0.351851f, 0.311806f, 0.166664f, -0.034654f, -0.224292f, -0.338330f, + 0.326641f, 0.135299f, -0.135299f, -0.326641f, -0.326641f, -0.135299f, 0.135299f, 0.326641f, + 0.326641f, 0.135299f, -0.135299f, -0.326641f, -0.326641f, -0.135299f, 0.135299f, 0.326641f, + 0.311806f, 0.034654f, -0.273300f, -0.338330f, -0.102631f, 0.224292f, 0.351851f, 0.166664f, + -0.166664f, -0.351851f, -0.224292f, 0.102631f, 0.338330f, 0.273300f, -0.034654f, -0.311806f, + 0.293969f, -0.068975f, -0.346760f, -0.196424f, 0.196424f, 0.346760f, 0.068975f, -0.293969f, + -0.293969f, 0.068975f, 0.346760f, 0.196424f, -0.196424f, -0.346760f, -0.068975f, 0.293969f, + 0.273300f, -0.166664f, -0.338330f, 0.034654f, 0.351851f, 0.102631f, -0.311806f, -0.224292f, + 0.224292f, 0.311806f, -0.102631f, -0.351851f, -0.034654f, 0.338330f, 0.166664f, -0.273300f,}; + + static final float[] analysis_window = { + 0.000043f, 0.000171f, 0.000385f, 0.000685f, 0.001071f, 0.001541f, 0.002098f, 0.002739f, + 0.003466f, 0.004278f, 0.005174f, 0.006156f, 0.007222f, 0.008373f, 0.009607f, 0.010926f, + 0.012329f, 0.013815f, 0.015385f, 0.017037f, 0.018772f, 0.020590f, 0.022490f, 0.024472f, + 0.026535f, 0.028679f, 0.030904f, 0.033210f, 0.035595f, 0.038060f, 0.040604f, 0.043227f, + 0.045928f, 0.048707f, 0.051564f, 0.054497f, 0.057506f, 0.060591f, 0.063752f, 0.066987f, + 0.070297f, 0.073680f, 0.077136f, 0.080665f, 0.084265f, 0.087937f, 0.091679f, 0.095492f, + 0.099373f, 0.103323f, 0.107342f, 0.111427f, 0.115579f, 0.119797f, 0.124080f, 0.128428f, + 0.132839f, 0.137313f, 0.141849f, 0.146447f, 0.151105f, 0.155823f, 0.160600f, 0.165435f, + 0.170327f, 0.175276f, 0.180280f, 0.185340f, 0.190453f, 0.195619f, 0.200838f, 0.206107f, + 0.211427f, 0.216797f, 0.222215f, 0.227680f, 0.233193f, 0.238751f, 0.244353f, 0.250000f, + 0.255689f, 0.261421f, 0.267193f, 0.273005f, 0.278856f, 0.284744f, 0.290670f, 0.296632f, + 0.302628f, 0.308658f, 0.314721f, 0.320816f, 0.326941f, 0.333097f, 0.339280f, 0.345492f, + 0.351729f, 0.357992f, 0.364280f, 0.370590f, 0.376923f, 0.383277f, 0.389651f, 0.396044f, + 0.402455f, 0.408882f, 0.415325f, 0.421783f, 0.428254f, 0.434737f, 0.441231f, 0.447736f, + 0.454249f, 0.460770f, 0.467298f, 0.473832f, 0.480370f, 0.486912f, 0.493455f, 0.500000f, + 0.506545f, 0.513088f, 0.519630f, 0.526168f, 0.532702f, 0.539230f, 0.545751f, 0.552264f, + 0.558769f, 0.565263f, 0.571746f, 0.578217f, 0.584675f, 0.591118f, 0.597545f, 0.603956f, + 0.610349f, 0.616723f, 0.623077f, 0.629410f, 0.635720f, 0.642008f, 0.648271f, 0.654508f, + 0.660720f, 0.666903f, 0.673059f, 0.679184f, 0.685279f, 0.691342f, 0.697372f, 0.703368f, + 0.709330f, 0.715256f, 0.721144f, 0.726995f, 0.732807f, 0.738579f, 0.744311f, 0.750000f, + 0.755647f, 0.761249f, 0.766807f, 0.772320f, 0.777785f, 0.783203f, 0.788573f, 0.793893f, + 0.799162f, 0.804381f, 0.809547f, 0.814660f, 0.819720f, 0.824724f, 0.829673f, 0.834565f, + 0.839400f, 0.844177f, 0.848895f, 0.853553f, 0.858151f, 0.862687f, 0.867161f, 0.871572f, + 0.875920f, 0.880203f, 0.884421f, 0.888573f, 0.892658f, 0.896677f, 0.900627f, 0.904508f, + 0.908321f, 0.912063f, 0.915735f, 0.919335f, 0.922864f, 0.926320f, 0.929703f, 0.933013f, + 0.936248f, 0.939409f, 0.942494f, 0.945503f, 0.948436f, 0.951293f, 0.954072f, 0.956773f, + 0.959396f, 0.961940f, 0.964405f, 0.966790f, 0.969096f, 0.971321f, 0.973465f, 0.975528f, + 0.977510f, 0.979410f, 0.981228f, 0.982963f, 0.984615f, 0.986185f, 0.987671f, 0.989074f, + 0.990393f, 0.991627f, 0.992778f, 0.993844f, 0.994826f, 0.995722f, 0.996534f, 0.997261f, + 0.997902f, 0.998459f, 0.998929f, 0.999315f, 0.999615f, 0.999829f, 0.999957f, 1.000000f,}; + + static final int[] tbands/*[NB_TBANDS + 1]*/ = { + 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 68, 80, 96, 120 + }; + + static final int[] extra_bands/*[NB_TOT_BANDS + 1]*/ = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 68, 80, 96, 120, 160, 200 + }; + + /*static final float tweight[NB_TBANDS+1] = { + .3, .4, .5, .6, .7, .8, .9, 1., 1., 1., 1., 1., 1., 1., .8, .7, .6, .5 + };*/ + + /* RMS error was 0.138320, seed was 1361535663 */ + static final float[] weights/*[422]*/ = { + /* hidden layer */ + -0.0941125f, -0.302976f, -0.603555f, -0.19393f, -0.185983f, + -0.601617f, -0.0465317f, -0.114563f, -0.103599f, -0.618938f, + -0.317859f, -0.169949f, -0.0702885f, 0.148065f, 0.409524f, + 0.548432f, 0.367649f, -0.494393f, 0.764306f, -1.83957f, + 0.170849f, 12.786f, -1.08848f, -1.27284f, -16.2606f, + 24.1773f, -5.57454f, -0.17276f, -0.163388f, -0.224421f, + -0.0948944f, -0.0728695f, -0.26557f, -0.100283f, -0.0515459f, + -0.146142f, -0.120674f, -0.180655f, 0.12857f, 0.442138f, + -0.493735f, 0.167767f, 0.206699f, -0.197567f, 0.417999f, + 1.50364f, -0.773341f, -10.0401f, 0.401872f, 2.97966f, + 15.2165f, -1.88905f, -1.19254f, 0.0285397f, -0.00405139f, + 0.0707565f, 0.00825699f, -0.0927269f, -0.010393f, -0.00428882f, + -0.00489743f, -0.0709731f, -0.00255992f, 0.0395619f, 0.226424f, + 0.0325231f, 0.162175f, -0.100118f, 0.485789f, 0.12697f, + 0.285937f, 0.0155637f, 0.10546f, 3.05558f, 1.15059f, + -1.00904f, -1.83088f, 3.31766f, -3.42516f, -0.119135f, + -0.0405654f, 0.00690068f, 0.0179877f, -0.0382487f, 0.00597941f, + -0.0183611f, 0.00190395f, -0.144322f, -0.0435671f, 0.000990594f, + 0.221087f, 0.142405f, 0.484066f, 0.404395f, 0.511955f, + -0.237255f, 0.241742f, 0.35045f, -0.699428f, 10.3993f, + 2.6507f, -2.43459f, -4.18838f, 1.05928f, 1.71067f, + 0.00667811f, -0.0721335f, -0.0397346f, 0.0362704f, -0.11496f, + -0.0235776f, 0.0082161f, -0.0141741f, -0.0329699f, -0.0354253f, + 0.00277404f, -0.290654f, -1.14767f, -0.319157f, -0.686544f, + 0.36897f, 0.478899f, 0.182579f, -0.411069f, 0.881104f, + -4.60683f, 1.4697f, 0.335845f, -1.81905f, -30.1699f, + 5.55225f, 0.0019508f, -0.123576f, -0.0727332f, -0.0641597f, + -0.0534458f, -0.108166f, -0.0937368f, -0.0697883f, -0.0275475f, + -0.192309f, -0.110074f, 0.285375f, -0.405597f, 0.0926724f, + -0.287881f, -0.851193f, -0.099493f, -0.233764f, -1.2852f, + 1.13611f, 3.12168f, -0.0699f, -1.86216f, 2.65292f, + -7.31036f, 2.44776f, -0.00111802f, -0.0632786f, -0.0376296f, + -0.149851f, 0.142963f, 0.184368f, 0.123433f, 0.0756158f, + 0.117312f, 0.0933395f, 0.0692163f, 0.0842592f, 0.0704683f, + 0.0589963f, 0.0942205f, -0.448862f, 0.0262677f, 0.270352f, + -0.262317f, 0.172586f, 2.00227f, -0.159216f, 0.038422f, + 10.2073f, 4.15536f, -2.3407f, -0.0550265f, 0.00964792f, + -0.141336f, 0.0274501f, 0.0343921f, -0.0487428f, 0.0950172f, + -0.00775017f, -0.0372492f, -0.00548121f, -0.0663695f, 0.0960506f, + -0.200008f, -0.0412827f, 0.58728f, 0.0515787f, 0.337254f, + 0.855024f, 0.668371f, -0.114904f, -3.62962f, -0.467477f, + -0.215472f, 2.61537f, 0.406117f, -1.36373f, 0.0425394f, + 0.12208f, 0.0934502f, 0.123055f, 0.0340935f, -0.142466f, + 0.035037f, -0.0490666f, 0.0733208f, 0.0576672f, 0.123984f, + -0.0517194f, -0.253018f, 0.590565f, 0.145849f, 0.315185f, + 0.221534f, -0.149081f, 0.216161f, -0.349575f, 24.5664f, + -0.994196f, 0.614289f, -18.7905f, -2.83277f, -0.716801f, + -0.347201f, 0.479515f, -0.246027f, 0.0758683f, 0.137293f, + -0.17781f, 0.118751f, -0.00108329f, -0.237334f, 0.355732f, + -0.12991f, -0.0547627f, -0.318576f, -0.325524f, 0.180494f, + -0.0625604f, 0.141219f, 0.344064f, 0.37658f, -0.591772f, + 5.8427f, -0.38075f, 0.221894f, -1.41934f, -1.87943e+06f, + 1.34114f, 0.0283355f, -0.0447856f, -0.0211466f, -0.0256927f, + 0.0139618f, 0.0207934f, -0.0107666f, 0.0110969f, 0.0586069f, + -0.0253545f, -0.0328433f, 0.11872f, -0.216943f, 0.145748f, + 0.119808f, -0.0915211f, -0.120647f, -0.0787719f, -0.143644f, + -0.595116f, -1.152f, -1.25335f, -1.17092f, 4.34023f, + -975268.0f, -1.37033f, -0.0401123f, 0.210602f, -0.136656f, + 0.135962f, -0.0523293f, 0.0444604f, 0.0143928f, 0.00412666f, + -0.0193003f, 0.218452f, -0.110204f, -2.02563f, 0.918238f, + -2.45362f, 1.19542f, -0.061362f, -1.92243f, 0.308111f, + 0.49764f, 0.912356f, 0.209272f, -2.34525f, 2.19326f, + -6.47121f, 1.69771f, -0.725123f, 0.0118929f, 0.0377944f, + 0.0554003f, 0.0226452f, -0.0704421f, -0.0300309f, 0.0122978f, + -0.0041782f, -0.0686612f, 0.0313115f, 0.039111f, 0.364111f, + -0.0945548f, 0.0229876f, -0.17414f, 0.329795f, 0.114714f, + 0.30022f, 0.106997f, 0.132355f, 5.79932f, 0.908058f, + -0.905324f, -3.3561f, 0.190647f, 0.184211f, -0.673648f, + 0.231807f, -0.0586222f, 0.230752f, -0.438277f, 0.245857f, + -0.17215f, 0.0876383f, -0.720512f, 0.162515f, 0.0170571f, + 0.101781f, 0.388477f, 1.32931f, 1.08548f, -0.936301f, + -2.36958f, -6.71988f, -3.44376f, 2.13818f, 14.2318f, + 4.91459f, -3.09052f, -9.69191f, -0.768234f, 1.79604f, + 0.0549653f, 0.163399f, 0.0797025f, 0.0343933f, -0.0555876f, + -0.00505673f, 0.0187258f, 0.0326628f, 0.0231486f, 0.15573f, + 0.0476223f, -0.254824f, 1.60155f, -0.801221f, 2.55496f, + 0.737629f, -1.36249f, -0.695463f, -2.44301f, -1.73188f, + 3.95279f, 1.89068f, 0.486087f, -11.3343f, 3.9416e+06f, + /* output layer */ + -0.381439f, 0.12115f, -0.906927f, 2.93878f, 1.6388f, + 0.882811f, 0.874344f, 1.21726f, -0.874545f, 0.321706f, + 0.785055f, 0.946558f, -0.575066f, -3.46553f, 0.884905f, + 0.0924047f, -9.90712f, 0.391338f, 0.160103f, -2.04954f, + 4.1455f, 0.0684029f, -0.144761f, -0.285282f, 0.379244f, + -1.1584f, -0.0277241f, -9.85f, -4.82386f, 3.71333f, + 3.87308f, 3.52558f}; + + static final int[] topo = {25, 15, 2}; + + // fixme: move this into an MLP class singleton or something? + static final MLPState net = new MLPState(); + + static { + net.layers = 3; + net.topo = topo; + net.weights = weights; + } + ; + + static final float[] tansig_table/*[201]*/ = { + 0.000000f, 0.039979f, 0.079830f, 0.119427f, 0.158649f, + 0.197375f, 0.235496f, 0.272905f, 0.309507f, 0.345214f, + 0.379949f, 0.413644f, 0.446244f, 0.477700f, 0.507977f, + 0.537050f, 0.564900f, 0.591519f, 0.616909f, 0.641077f, + 0.664037f, 0.685809f, 0.706419f, 0.725897f, 0.744277f, + 0.761594f, 0.777888f, 0.793199f, 0.807569f, 0.821040f, + 0.833655f, 0.845456f, 0.856485f, 0.866784f, 0.876393f, + 0.885352f, 0.893698f, 0.901468f, 0.908698f, 0.915420f, + 0.921669f, 0.927473f, 0.932862f, 0.937863f, 0.942503f, + 0.946806f, 0.950795f, 0.954492f, 0.957917f, 0.961090f, + 0.964028f, 0.966747f, 0.969265f, 0.971594f, 0.973749f, + 0.975743f, 0.977587f, 0.979293f, 0.980869f, 0.982327f, + 0.983675f, 0.984921f, 0.986072f, 0.987136f, 0.988119f, + 0.989027f, 0.989867f, 0.990642f, 0.991359f, 0.992020f, + 0.992631f, 0.993196f, 0.993718f, 0.994199f, 0.994644f, + 0.995055f, 0.995434f, 0.995784f, 0.996108f, 0.996407f, + 0.996682f, 0.996937f, 0.997172f, 0.997389f, 0.997590f, + 0.997775f, 0.997946f, 0.998104f, 0.998249f, 0.998384f, + 0.998508f, 0.998623f, 0.998728f, 0.998826f, 0.998916f, + 0.999000f, 0.999076f, 0.999147f, 0.999213f, 0.999273f, + 0.999329f, 0.999381f, 0.999428f, 0.999472f, 0.999513f, + 0.999550f, 0.999585f, 0.999617f, 0.999646f, 0.999673f, + 0.999699f, 0.999722f, 0.999743f, 0.999763f, 0.999781f, + 0.999798f, 0.999813f, 0.999828f, 0.999841f, 0.999853f, + 0.999865f, 0.999875f, 0.999885f, 0.999893f, 0.999902f, + 0.999909f, 0.999916f, 0.999923f, 0.999929f, 0.999934f, + 0.999939f, 0.999944f, 0.999948f, 0.999952f, 0.999956f, + 0.999959f, 0.999962f, 0.999965f, 0.999968f, 0.999970f, + 0.999973f, 0.999975f, 0.999977f, 0.999978f, 0.999980f, + 0.999982f, 0.999983f, 0.999984f, 0.999986f, 0.999987f, + 0.999988f, 0.999989f, 0.999990f, 0.999990f, 0.999991f, + 0.999992f, 0.999992f, 0.999993f, 0.999994f, 0.999994f, + 0.999994f, 0.999995f, 0.999995f, 0.999996f, 0.999996f, + 0.999996f, 0.999997f, 0.999997f, 0.999997f, 0.999997f, + 0.999997f, 0.999998f, 0.999998f, 0.999998f, 0.999998f, + 0.999998f, 0.999998f, 0.999999f, 0.999999f, 0.999999f, + 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, + 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, + 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, + 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, + 1.000000f,}; + + // from opus_encoder.c + /* Transition tables for the voice and music. First column is the + middle (memoriless) threshold. The second column is the hysteresis + (difference with the middle) */ + static final int[] mono_voice_bandwidth_thresholds = { + 11000, 1000, /* NB<->MB */ + 14000, 1000, /* MB<->WB */ + 17000, 1000, /* WB<->SWB */ + 21000, 2000, /* SWB<->FB */}; + static final int[] mono_music_bandwidth_thresholds = { + 12000, 1000, /* NB<->MB */ + 15000, 1000, /* MB<->WB */ + 18000, 2000, /* WB<->SWB */ + 22000, 2000, /* SWB<->FB */}; + static final int[] stereo_voice_bandwidth_thresholds = { + 11000, 1000, /* NB<->MB */ + 14000, 1000, /* MB<->WB */ + 21000, 2000, /* WB<->SWB */ + 28000, 2000, /* SWB<->FB */}; + static final int[] stereo_music_bandwidth_thresholds = { + 12000, 1000, /* NB<->MB */ + 18000, 2000, /* MB<->WB */ + 21000, 2000, /* WB<->SWB */ + 30000, 2000, /* SWB<->FB */}; + + /* Threshold bit-rates for switching between mono and stereo */ + public static final int stereo_voice_threshold = 30000; + public static final int stereo_music_threshold = 30000; + + /* Threshold bit-rate for switching between SILK/hybrid and CELT-only */ + static final int[][] mode_thresholds = { + /* voice */ /* music */ + new int[]{64000, 16000}, /* mono */ + new int[]{36000, 16000}, /* stereo */}; +} diff --git a/app/src/main/java/org/concentus/PLC.java b/app/src/main/java/org/concentus/PLC.java new file mode 100644 index 0000000000..7a2822cc26 --- /dev/null +++ b/app/src/main/java/org/concentus/PLC.java @@ -0,0 +1,466 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Routines for managing packet loss concealment +/// +class PLC { + + private static final int NB_ATT = 2; + private static final short[] HARM_ATT_Q15 = {32440, 31130}; + /* 0.99, 0.95 */ + private static final short[] PLC_RAND_ATTENUATE_V_Q15 = {31130, 26214}; + /* 0.95, 0.8 */ + private static final short[] PLC_RAND_ATTENUATE_UV_Q15 = {32440, 29491}; + + /* 0.99, 0.9 */ + + static void silk_PLC_Reset( + SilkChannelDecoder psDec /* I/O Decoder state */ + ) { + psDec.sPLC.pitchL_Q8 = Inlines.silk_LSHIFT(psDec.frame_length, 8 - 1); + psDec.sPLC.prevGain_Q16[0] = ((int) ((1) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(1, 16)*/; + psDec.sPLC.prevGain_Q16[1] = ((int) ((1) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(1, 16)*/; + psDec.sPLC.subfr_length = 20; + psDec.sPLC.nb_subfr = 2; + } + + static void silk_PLC( + SilkChannelDecoder psDec, /* I/O Decoder state */ + SilkDecoderControl psDecCtrl, /* I/O Decoder control */ + short[] frame, /* I/O signal */ + int frame_ptr, + int lost /* I Loss flag */ + ) { + /* PLC control function */ + if (psDec.fs_kHz != psDec.sPLC.fs_kHz) { + silk_PLC_Reset(psDec); + psDec.sPLC.fs_kHz = psDec.fs_kHz; + } + + if (lost != 0) { + /** + * ************************* + */ + /* Generate Signal */ + /** + * ************************* + */ + silk_PLC_conceal(psDec, psDecCtrl, frame, frame_ptr); + + psDec.lossCnt++; + } else { + /** + * ************************* + */ + /* Update state */ + /** + * ************************* + */ + silk_PLC_update(psDec, psDecCtrl); + } + } + + /** + * *********************************************** + */ + /* Update state of PLC */ + /** + * *********************************************** + */ + static void silk_PLC_update( + SilkChannelDecoder psDec, /* I/O Decoder state */ + SilkDecoderControl psDecCtrl /* I/O Decoder control */ + ) { + int LTP_Gain_Q14, temp_LTP_Gain_Q14; + int i, j; + PLCStruct psPLC = psDec.sPLC; // [porting note] pointer on the stack + + /* Update parameters used in case of packet loss */ + psDec.prevSignalType = psDec.indices.signalType; + LTP_Gain_Q14 = 0; + if (psDec.indices.signalType == SilkConstants.TYPE_VOICED) { + /* Find the parameters for the last subframe which contains a pitch pulse */ + for (j = 0; j * psDec.subfr_length < psDecCtrl.pitchL[psDec.nb_subfr - 1]; j++) { + if (j == psDec.nb_subfr) { + break; + } + temp_LTP_Gain_Q14 = 0; + for (i = 0; i < SilkConstants.LTP_ORDER; i++) { + temp_LTP_Gain_Q14 += psDecCtrl.LTPCoef_Q14[(psDec.nb_subfr - 1 - j) * SilkConstants.LTP_ORDER + i]; + } + if (temp_LTP_Gain_Q14 > LTP_Gain_Q14) { + LTP_Gain_Q14 = temp_LTP_Gain_Q14; + + System.arraycopy(psDecCtrl.LTPCoef_Q14, Inlines.silk_SMULBB(psDec.nb_subfr - 1 - j, SilkConstants.LTP_ORDER), psPLC.LTPCoef_Q14, 0, SilkConstants.LTP_ORDER); + + psPLC.pitchL_Q8 = Inlines.silk_LSHIFT(psDecCtrl.pitchL[psDec.nb_subfr - 1 - j], 8); + } + } + + Arrays.MemSet(psPLC.LTPCoef_Q14, (short) 0, SilkConstants.LTP_ORDER); + psPLC.LTPCoef_Q14[SilkConstants.LTP_ORDER / 2] = (short) (LTP_Gain_Q14); + + /* Limit LT coefs */ + if (LTP_Gain_Q14 < SilkConstants.V_PITCH_GAIN_START_MIN_Q14) { + int scale_Q10; + int tmp; + + tmp = Inlines.silk_LSHIFT(SilkConstants.V_PITCH_GAIN_START_MIN_Q14, 10); + scale_Q10 = Inlines.silk_DIV32(tmp, Inlines.silk_max(LTP_Gain_Q14, 1)); + for (i = 0; i < SilkConstants.LTP_ORDER; i++) { + psPLC.LTPCoef_Q14[i] = (short) (Inlines.silk_RSHIFT(Inlines.silk_SMULBB(psPLC.LTPCoef_Q14[i], scale_Q10), 10)); + } + } else if (LTP_Gain_Q14 > SilkConstants.V_PITCH_GAIN_START_MAX_Q14) { + int scale_Q14; + int tmp; + + tmp = Inlines.silk_LSHIFT(SilkConstants.V_PITCH_GAIN_START_MAX_Q14, 14); + scale_Q14 = Inlines.silk_DIV32(tmp, Inlines.silk_max(LTP_Gain_Q14, 1)); + for (i = 0; i < SilkConstants.LTP_ORDER; i++) { + psPLC.LTPCoef_Q14[i] = (short) (Inlines.silk_RSHIFT(Inlines.silk_SMULBB(psPLC.LTPCoef_Q14[i], scale_Q14), 14)); + } + } + } else { + psPLC.pitchL_Q8 = Inlines.silk_LSHIFT(Inlines.silk_SMULBB(psDec.fs_kHz, 18), 8); + Arrays.MemSet(psPLC.LTPCoef_Q14, (short) 0, SilkConstants.LTP_ORDER); + } + + /* Save LPC coeficients */ + System.arraycopy(psDecCtrl.PredCoef_Q12[1], 0, psPLC.prevLPC_Q12, 0, psDec.LPC_order); + psPLC.prevLTP_scale_Q14 = (short) (psDecCtrl.LTP_scale_Q14); + + /* Save last two gains */ + System.arraycopy(psDecCtrl.Gains_Q16, psDec.nb_subfr - 2, psPLC.prevGain_Q16, 0, 2); + + psPLC.subfr_length = psDec.subfr_length; + psPLC.nb_subfr = psDec.nb_subfr; + } + + /// + /// + /// + /// O + /// O + /// O + /// O + /// I + /// I + /// I + /// I + static void silk_PLC_energy( + BoxedValueInt energy1, + BoxedValueInt shift1, + BoxedValueInt energy2, + BoxedValueInt shift2, + int[] exc_Q14, + int[] prevGain_Q10, + int subfr_length, + int nb_subfr) { + int i, k; + int exc_buf_ptr = 0; + short[] exc_buf = new short[2 * subfr_length]; + + /* Find random noise component */ + /* Scale previous excitation signal */ + for (k = 0; k < 2; k++) { + for (i = 0; i < subfr_length; i++) { + exc_buf[exc_buf_ptr + i] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT( + Inlines.silk_SMULWW(exc_Q14[i + (k + nb_subfr - 2) * subfr_length], prevGain_Q10[k]), 8)); + } + exc_buf_ptr += subfr_length; + } + + /* Find the subframe with lowest energy of the last two and use that as random noise generator */ + SumSqrShift.silk_sum_sqr_shift(energy1, shift1, exc_buf, subfr_length); + SumSqrShift.silk_sum_sqr_shift(energy2, shift2, exc_buf, subfr_length, subfr_length); + } + + static void silk_PLC_conceal( + SilkChannelDecoder psDec, /* I/O Decoder state */ + SilkDecoderControl psDecCtrl, /* I/O Decoder control */ + short[] frame, /* O LPC residual signal */ + int frame_ptr + ) { + int i, j, k; + int lag, idx, sLTP_buf_idx; + int rand_seed, harm_Gain_Q15, rand_Gain_Q15, inv_gain_Q30; + BoxedValueInt energy1 = new BoxedValueInt(0); + BoxedValueInt energy2 = new BoxedValueInt(0); + BoxedValueInt shift1 = new BoxedValueInt(0); + BoxedValueInt shift2 = new BoxedValueInt(0); + int rand_ptr; + int pred_lag_ptr; + int LPC_pred_Q10, LTP_pred_Q12; + short rand_scale_Q14; + short[] B_Q14; + int sLPC_Q14_ptr; + short[] sLTP = new short[psDec.ltp_mem_length]; + int[] sLTP_Q14 = new int[psDec.ltp_mem_length + psDec.frame_length]; + PLCStruct psPLC = psDec.sPLC; + int[] prevGain_Q10 = new int[2]; + + prevGain_Q10[0] = Inlines.silk_RSHIFT(psPLC.prevGain_Q16[0], 6); + prevGain_Q10[1] = Inlines.silk_RSHIFT(psPLC.prevGain_Q16[1], 6); + + if (psDec.first_frame_after_reset != 0) { + Arrays.MemSet(psPLC.prevLPC_Q12, (short) 0, SilkConstants.MAX_LPC_ORDER); + } + + silk_PLC_energy(energy1, shift1, energy2, shift2, psDec.exc_Q14, prevGain_Q10, psDec.subfr_length, psDec.nb_subfr); + + if (Inlines.silk_RSHIFT(energy1.Val, shift2.Val) < Inlines.silk_RSHIFT(energy2.Val, shift1.Val)) { + /* First sub-frame has lowest energy */ + rand_ptr = Inlines.silk_max_int(0, (psPLC.nb_subfr - 1) * psPLC.subfr_length - SilkConstants.RAND_BUF_SIZE); + } else { + /* Second sub-frame has lowest energy */ + rand_ptr = Inlines.silk_max_int(0, psPLC.nb_subfr * psPLC.subfr_length - SilkConstants.RAND_BUF_SIZE); + } + + /* Set up Gain to random noise component */ + B_Q14 = psPLC.LTPCoef_Q14; + rand_scale_Q14 = psPLC.randScale_Q14; + + /* Set up attenuation gains */ + harm_Gain_Q15 = HARM_ATT_Q15[Inlines.silk_min_int(NB_ATT - 1, psDec.lossCnt)]; + if (psDec.prevSignalType == SilkConstants.TYPE_VOICED) { + rand_Gain_Q15 = PLC_RAND_ATTENUATE_V_Q15[Inlines.silk_min_int(NB_ATT - 1, psDec.lossCnt)]; + } else { + rand_Gain_Q15 = PLC_RAND_ATTENUATE_UV_Q15[Inlines.silk_min_int(NB_ATT - 1, psDec.lossCnt)]; + } + + /* LPC concealment. Apply BWE to previous LPC */ + BWExpander.silk_bwexpander(psPLC.prevLPC_Q12, psDec.LPC_order, ((int) ((SilkConstants.BWE_COEF) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.BWE_COEF, 16)*/); + + /* First Lost frame */ + if (psDec.lossCnt == 0) { + rand_scale_Q14 = 1 << 14; + + /* Reduce random noise Gain for voiced frames */ + if (psDec.prevSignalType == SilkConstants.TYPE_VOICED) { + for (i = 0; i < SilkConstants.LTP_ORDER; i++) { + rand_scale_Q14 -= B_Q14[i]; + } + rand_scale_Q14 = Inlines.silk_max_16((short) 3277, rand_scale_Q14); + /* 0.2 */ + rand_scale_Q14 = (short) Inlines.silk_RSHIFT(Inlines.silk_SMULBB(rand_scale_Q14, psPLC.prevLTP_scale_Q14), 14); + } else { + /* Reduce random noise for unvoiced frames with high LPC gain */ + int invGain_Q30, down_scale_Q30; + + invGain_Q30 = LPCInversePredGain.silk_LPC_inverse_pred_gain(psPLC.prevLPC_Q12, psDec.LPC_order); + + down_scale_Q30 = Inlines.silk_min_32(Inlines.silk_RSHIFT((int) 1 << 30, SilkConstants.LOG2_INV_LPC_GAIN_HIGH_THRES), invGain_Q30); + down_scale_Q30 = Inlines.silk_max_32(Inlines.silk_RSHIFT((int) 1 << 30, SilkConstants.LOG2_INV_LPC_GAIN_LOW_THRES), down_scale_Q30); + down_scale_Q30 = Inlines.silk_LSHIFT(down_scale_Q30, SilkConstants.LOG2_INV_LPC_GAIN_HIGH_THRES); + + rand_Gain_Q15 = Inlines.silk_RSHIFT(Inlines.silk_SMULWB(down_scale_Q30, rand_Gain_Q15), 14); + } + } + + rand_seed = psPLC.rand_seed; + lag = Inlines.silk_RSHIFT_ROUND(psPLC.pitchL_Q8, 8); + sLTP_buf_idx = psDec.ltp_mem_length; + + /* Rewhiten LTP state */ + idx = psDec.ltp_mem_length - lag - psDec.LPC_order - SilkConstants.LTP_ORDER / 2; + Inlines.OpusAssert(idx > 0); + Filters.silk_LPC_analysis_filter(sLTP, idx, psDec.outBuf, idx, psPLC.prevLPC_Q12, 0, psDec.ltp_mem_length - idx, psDec.LPC_order); + /* Scale LTP state */ + inv_gain_Q30 = Inlines.silk_INVERSE32_varQ(psPLC.prevGain_Q16[1], 46); + inv_gain_Q30 = Inlines.silk_min(inv_gain_Q30, Integer.MAX_VALUE >> 1); + for (i = idx + psDec.LPC_order; i < psDec.ltp_mem_length; i++) { + sLTP_Q14[i] = Inlines.silk_SMULWB(inv_gain_Q30, sLTP[i]); + } + + /** + * ************************ + */ + /* LTP synthesis filtering */ + /** + * ************************ + */ + for (k = 0; k < psDec.nb_subfr; k++) { + /* Set up pointer */ + pred_lag_ptr = sLTP_buf_idx - lag + SilkConstants.LTP_ORDER / 2; + for (i = 0; i < psDec.subfr_length; i++) { + /* Unrolled loop */ + /* Avoids introducing a bias because Inlines.silk_SMLAWB() always rounds to -inf */ + LTP_pred_Q12 = 2; + LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr], B_Q14[0]); + LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 1], B_Q14[1]); + LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 2], B_Q14[2]); + LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 3], B_Q14[3]); + LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 4], B_Q14[4]); + pred_lag_ptr++; + + /* Generate LPC excitation */ + rand_seed = Inlines.silk_RAND(rand_seed); + idx = Inlines.silk_RSHIFT(rand_seed, 25) & SilkConstants.RAND_BUF_MASK; + sLTP_Q14[sLTP_buf_idx] = Inlines.silk_LSHIFT32(Inlines.silk_SMLAWB(LTP_pred_Q12, psDec.exc_Q14[rand_ptr + idx], rand_scale_Q14), 2); + sLTP_buf_idx++; + } + + /* Gradually reduce LTP gain */ + for (j = 0; j < SilkConstants.LTP_ORDER; j++) { + B_Q14[j] = (short) (Inlines.silk_RSHIFT(Inlines.silk_SMULBB(harm_Gain_Q15, B_Q14[j]), 15)); + } + /* Gradually reduce excitation gain */ + rand_scale_Q14 = (short) (Inlines.silk_RSHIFT(Inlines.silk_SMULBB(rand_scale_Q14, rand_Gain_Q15), 15)); + + /* Slowly increase pitch lag */ + psPLC.pitchL_Q8 = Inlines.silk_SMLAWB(psPLC.pitchL_Q8, psPLC.pitchL_Q8, SilkConstants.PITCH_DRIFT_FAC_Q16); + psPLC.pitchL_Q8 = Inlines.silk_min_32(psPLC.pitchL_Q8, Inlines.silk_LSHIFT(Inlines.silk_SMULBB(SilkConstants.MAX_PITCH_LAG_MS, psDec.fs_kHz), 8)); + lag = Inlines.silk_RSHIFT_ROUND(psPLC.pitchL_Q8, 8); + } + + /** + * ************************ + */ + /* LPC synthesis filtering */ + /** + * ************************ + */ + sLPC_Q14_ptr = psDec.ltp_mem_length - SilkConstants.MAX_LPC_ORDER; + + /* Copy LPC state */ + System.arraycopy(psDec.sLPC_Q14_buf, 0, sLTP_Q14, sLPC_Q14_ptr, SilkConstants.MAX_LPC_ORDER); + + Inlines.OpusAssert(psDec.LPC_order >= 10); + /* check that unrolling works */ + for (i = 0; i < psDec.frame_length; i++) { + /* partly unrolled */ + int sLPCmaxi = sLPC_Q14_ptr + SilkConstants.MAX_LPC_ORDER + i; + /* Avoids introducing a bias because Inlines.silk_SMLAWB() always rounds to -inf */ + LPC_pred_Q10 = Inlines.silk_RSHIFT(psDec.LPC_order, 1); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 1], psPLC.prevLPC_Q12[0]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 2], psPLC.prevLPC_Q12[1]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 3], psPLC.prevLPC_Q12[2]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 4], psPLC.prevLPC_Q12[3]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 5], psPLC.prevLPC_Q12[4]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 6], psPLC.prevLPC_Q12[5]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 7], psPLC.prevLPC_Q12[6]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 8], psPLC.prevLPC_Q12[7]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 9], psPLC.prevLPC_Q12[8]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 10], psPLC.prevLPC_Q12[9]); + for (j = 10; j < psDec.LPC_order; j++) { + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - j - 1], psPLC.prevLPC_Q12[j]); + } + + /* Add prediction to LPC excitation */ + sLTP_Q14[sLPCmaxi] = Inlines.silk_ADD_LSHIFT32(sLTP_Q14[sLPCmaxi], LPC_pred_Q10, 4); + + /* Scale with Gain */ + frame[frame_ptr + i] = (short) Inlines.silk_SAT16(Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(sLTP_Q14[sLPCmaxi], prevGain_Q10[1]), 8))); + } + + /* Save LPC state */ + System.arraycopy(sLTP_Q14, sLPC_Q14_ptr + psDec.frame_length, psDec.sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER); + + /** + * *********************************** + */ + /* Update states */ + /** + * *********************************** + */ + psPLC.rand_seed = rand_seed; + psPLC.randScale_Q14 = rand_scale_Q14; + for (i = 0; i < SilkConstants.MAX_NB_SUBFR; i++) { + psDecCtrl.pitchL[i] = lag; + } + } + + /* Glues concealed frames with new good received frames */ + static void silk_PLC_glue_frames( + SilkChannelDecoder psDec, /* I/O decoder state */ + short[] frame, /* I/O signal */ + int frame_ptr, + int length /* I length of signal */ + ) { + int i; + BoxedValueInt energy_shift = new BoxedValueInt(0); + BoxedValueInt energy = new BoxedValueInt(0); + PLCStruct psPLC = psDec.sPLC; + + if (psDec.lossCnt != 0) { + /* Calculate energy in concealed residual */ + BoxedValueInt boxed_conc_e = new BoxedValueInt(0); + BoxedValueInt boxed_conc_shift = new BoxedValueInt(0); + SumSqrShift.silk_sum_sqr_shift(boxed_conc_e, boxed_conc_shift, frame, frame_ptr, length); + psPLC.conc_energy = boxed_conc_e.Val; + psPLC.conc_energy_shift = boxed_conc_shift.Val; + + psPLC.last_frame_lost = 1; + } else { + if (psDec.sPLC.last_frame_lost != 0) { + /* Calculate residual in decoded signal if last frame was lost */ + SumSqrShift.silk_sum_sqr_shift(energy, energy_shift, frame, frame_ptr, length); + + /* Normalize energies */ + if (energy_shift.Val > psPLC.conc_energy_shift) { + psPLC.conc_energy = Inlines.silk_RSHIFT(psPLC.conc_energy, energy_shift.Val - psPLC.conc_energy_shift); + } else if (energy_shift.Val < psPLC.conc_energy_shift) { + energy.Val = Inlines.silk_RSHIFT(energy.Val, psPLC.conc_energy_shift - energy_shift.Val); + } + + /* Fade in the energy difference */ + if (energy.Val > psPLC.conc_energy) { + int frac_Q24, LZ; + int gain_Q16, slope_Q16; + + LZ = Inlines.silk_CLZ32(psPLC.conc_energy); + LZ = LZ - 1; + psPLC.conc_energy = Inlines.silk_LSHIFT(psPLC.conc_energy, LZ); + energy.Val = Inlines.silk_RSHIFT(energy.Val, Inlines.silk_max_32(24 - LZ, 0)); + + frac_Q24 = Inlines.silk_DIV32(psPLC.conc_energy, Inlines.silk_max(energy.Val, 1)); + + gain_Q16 = Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(frac_Q24), 4); + slope_Q16 = Inlines.silk_DIV32_16(((int) 1 << 16) - gain_Q16, length); + /* Make slope 4x steeper to avoid missing onsets after DTX */ + slope_Q16 = Inlines.silk_LSHIFT(slope_Q16, 2); + + for (i = frame_ptr; i < frame_ptr + length; i++) { + frame[i] = (short) (Inlines.silk_SMULWB(gain_Q16, frame[i])); + gain_Q16 += slope_Q16; + if (gain_Q16 > (int) 1 << 16) { + break; + } + } + } + } + psPLC.last_frame_lost = 0; + } + } +} diff --git a/app/src/main/java/org/concentus/PLCStruct.java b/app/src/main/java/org/concentus/PLCStruct.java new file mode 100644 index 0000000000..d73665c5bd --- /dev/null +++ b/app/src/main/java/org/concentus/PLCStruct.java @@ -0,0 +1,73 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Struct for Packet Loss Concealment +/// +class PLCStruct { + + int pitchL_Q8 = 0; + /* Pitch lag to use for voiced concealment */ + final short[] LTPCoef_Q14 = new short[SilkConstants.LTP_ORDER]; + /* LTP coeficients to use for voiced concealment */ + final short[] prevLPC_Q12 = new short[SilkConstants.MAX_LPC_ORDER]; + int last_frame_lost = 0; + /* Was previous frame lost */ + int rand_seed = 0; + /* Seed for unvoiced signal generation */ + short randScale_Q14 = 0; + /* Scaling of unvoiced random signal */ + int conc_energy = 0; + int conc_energy_shift = 0; + short prevLTP_scale_Q14 = 0; + final int[] prevGain_Q16 = new int[2]; + int fs_kHz = 0; + int nb_subfr = 0; + int subfr_length = 0; + + void Reset() { + pitchL_Q8 = 0; + Arrays.MemSet(LTPCoef_Q14, (short) 0, SilkConstants.LTP_ORDER); + Arrays.MemSet(prevLPC_Q12, (short) 0, SilkConstants.MAX_LPC_ORDER); + last_frame_lost = 0; + rand_seed = 0; + randScale_Q14 = 0; + conc_energy = 0; + conc_energy_shift = 0; + prevLTP_scale_Q14 = 0; + Arrays.MemSet(prevGain_Q16, 0, 2); + fs_kHz = 0; + nb_subfr = 0; + subfr_length = 0; + } +} diff --git a/app/src/main/java/org/concentus/Pitch.java b/app/src/main/java/org/concentus/Pitch.java new file mode 100644 index 0000000000..22e9ffb249 --- /dev/null +++ b/app/src/main/java/org/concentus/Pitch.java @@ -0,0 +1,421 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class Pitch { + + static void find_best_pitch(int[] xcorr, int[] y, int len, + int max_pitch, int[] best_pitch, + int yshift, int maxcorr + ) { + int i, j; + int Syy = 1; + int best_num_0; + int best_num_1; + int best_den_0; + int best_den_1; + int xshift = Inlines.celt_ilog2(maxcorr) - 14; + + best_num_0 = -1; + best_num_1 = -1; + best_den_0 = 0; + best_den_1 = 0; + best_pitch[0] = 0; + best_pitch[1] = 1; + for (j = 0; j < len; j++) { + Syy = Inlines.ADD32(Syy, Inlines.SHR32(Inlines.MULT16_16(y[j], y[j]), yshift)); + } + for (i = 0; i < max_pitch; i++) { + if (xcorr[i] > 0) { + int num; + int xcorr16; + xcorr16 = Inlines.EXTRACT16(Inlines.VSHR32(xcorr[i], xshift)); + num = Inlines.MULT16_16_Q15((xcorr16), (xcorr16)); + if (Inlines.MULT16_32_Q15(num, best_den_1) > Inlines.MULT16_32_Q15(best_num_1, Syy)) { + if (Inlines.MULT16_32_Q15(num, best_den_0) > Inlines.MULT16_32_Q15(best_num_0, Syy)) { + best_num_1 = best_num_0; + best_den_1 = best_den_0; + best_pitch[1] = best_pitch[0]; + best_num_0 = num; + best_den_0 = Syy; + best_pitch[0] = i; + } else { + best_num_1 = num; + best_den_1 = Syy; + best_pitch[1] = i; + } + } + } + + Syy += Inlines.SHR32(Inlines.MULT16_16(y[i + len], y[i + len]), yshift) - Inlines.SHR32(Inlines.MULT16_16(y[i], y[i]), yshift); + Syy = Inlines.MAX32(1, Syy); + } + } + + static void celt_fir5(int[] x, + int[] num, + int[] y, + int N, + int[] mem) { + int i; + int num0, num1, num2, num3, num4; + int mem0, mem1, mem2, mem3, mem4; + num0 = num[0]; + num1 = num[1]; + num2 = num[2]; + num3 = num[3]; + num4 = num[4]; + mem0 = mem[0]; + mem1 = mem[1]; + mem2 = mem[2]; + mem3 = mem[3]; + mem4 = mem[4]; + for (i = 0; i < N; i++) { + int sum = Inlines.SHL32(Inlines.EXTEND32(x[i]), CeltConstants.SIG_SHIFT); + sum = Inlines.MAC16_16(sum, num0, (mem0)); + sum = Inlines.MAC16_16(sum, num1, (mem1)); + sum = Inlines.MAC16_16(sum, num2, (mem2)); + sum = Inlines.MAC16_16(sum, num3, (mem3)); + sum = Inlines.MAC16_16(sum, num4, (mem4)); + mem4 = mem3; + mem3 = mem2; + mem2 = mem1; + mem1 = mem0; + mem0 = x[i]; + y[i] = Inlines.ROUND16(sum, CeltConstants.SIG_SHIFT); + } + mem[0] = (mem0); + mem[1] = (mem1); + mem[2] = (mem2); + mem[3] = (mem3); + mem[4] = (mem4); + } + + static void pitch_downsample(int[][] x, int[] x_lp, int len, int C) { + int i; + int[] ac = new int[5]; + int tmp = CeltConstants.Q15ONE; + int[] lpc = new int[4]; + int[] mem = new int[]{0, 0, 0, 0, 0}; + int[] lpc2 = new int[5]; + int c1 = ((short) (0.5 + (0.8f) * (((int) 1) << (15))))/*Inlines.QCONST16(0.8f, 15)*/; + + int shift; + int maxabs = Inlines.celt_maxabs32(x[0], 0, len); + if (C == 2) { + int maxabs_1 = Inlines.celt_maxabs32(x[1], 0, len); + maxabs = Inlines.MAX32(maxabs, maxabs_1); + } + if (maxabs < 1) { + maxabs = 1; + } + shift = Inlines.celt_ilog2(maxabs) - 10; + if (shift < 0) { + shift = 0; + } + if (C == 2) { + shift++; + } + + int halflen = len >> 1; // cached for performance + for (i = 1; i < halflen; i++) { + x_lp[i] = (Inlines.SHR32(Inlines.HALF32(Inlines.HALF32(x[0][(2 * i - 1)] + x[0][(2 * i + 1)]) + x[0][2 * i]), shift)); + } + + x_lp[0] = (Inlines.SHR32(Inlines.HALF32(Inlines.HALF32(x[0][1]) + x[0][0]), shift)); + + if (C == 2) { + for (i = 1; i < halflen; i++) { + x_lp[i] += (Inlines.SHR32(Inlines.HALF32(Inlines.HALF32(x[1][(2 * i - 1)] + x[1][(2 * i + 1)]) + x[1][2 * i]), shift)); + } + x_lp[0] += (Inlines.SHR32(Inlines.HALF32(Inlines.HALF32(x[1][1]) + x[1][0]), shift)); + } + + Autocorrelation._celt_autocorr(x_lp, ac, null, 0, 4, halflen); + + /* Noise floor -40 dB */ + ac[0] += Inlines.SHR32(ac[0], 13); + /* Lag windowing */ + for (i = 1; i <= 4; i++) { + /*ac[i] *= exp(-.5*(2*M_PI*.002*i)*(2*M_PI*.002*i));*/ + ac[i] -= Inlines.MULT16_32_Q15((2 * i * i), ac[i]); + } + + CeltLPC.celt_lpc(lpc, ac, 4); + for (i = 0; i < 4; i++) { + tmp = Inlines.MULT16_16_Q15(((short) (0.5 + (.9f) * (((int) 1) << (15))))/*Inlines.QCONST16(.9f, 15)*/, tmp); + lpc[i] = Inlines.MULT16_16_Q15(lpc[i], tmp); + } + /* Add a zero */ + lpc2[0] = (lpc[0] + ((short) (0.5 + (0.8f) * (((int) 1) << (CeltConstants.SIG_SHIFT))))/*Inlines.QCONST16(0.8f, CeltConstants.SIG_SHIFT)*/); + lpc2[1] = (lpc[1] + Inlines.MULT16_16_Q15(c1, lpc[0])); + lpc2[2] = (lpc[2] + Inlines.MULT16_16_Q15(c1, lpc[1])); + lpc2[3] = (lpc[3] + Inlines.MULT16_16_Q15(c1, lpc[2])); + lpc2[4] = Inlines.MULT16_16_Q15(c1, lpc[3]); + + celt_fir5(x_lp, lpc2, x_lp, halflen, mem); + } + + // Fixme: remove pointers and optimize + static void pitch_search(int[] x_lp, int x_lp_ptr, int[] y, + int len, int max_pitch, BoxedValueInt pitch) { + int i, j; + int lag; + int[] best_pitch = new int[]{0, 0}; + int maxcorr; + int xmax, ymax; + int shift = 0; + int offset; + + Inlines.OpusAssert(len > 0); + Inlines.OpusAssert(max_pitch > 0); + lag = len + max_pitch; + + int[] x_lp4 = new int[len >> 2]; + int[] y_lp4 = new int[lag >> 2]; + int[] xcorr = new int[max_pitch >> 1]; + + /* Downsample by 2 again */ + for (j = 0; j < len >> 2; j++) { + x_lp4[j] = x_lp[x_lp_ptr + (2 * j)]; + } + for (j = 0; j < lag >> 2; j++) { + y_lp4[j] = y[2 * j]; + } + + xmax = Inlines.celt_maxabs32(x_lp4, 0, len >> 2); + ymax = Inlines.celt_maxabs32(y_lp4, 0, lag >> 2); + shift = Inlines.celt_ilog2(Inlines.MAX32(1, Inlines.MAX32(xmax, ymax))) - 11; + if (shift > 0) { + for (j = 0; j < len >> 2; j++) { + x_lp4[j] = Inlines.SHR16(x_lp4[j], shift); + } + for (j = 0; j < lag >> 2; j++) { + y_lp4[j] = Inlines.SHR16(y_lp4[j], shift); + } + /* Use double the shift for a MAC */ + shift *= 2; + } else { + shift = 0; + } + + /* Coarse search with 4x decimation */ + maxcorr = CeltPitchXCorr.pitch_xcorr(x_lp4, y_lp4, xcorr, len >> 2, max_pitch >> 2); + + find_best_pitch(xcorr, y_lp4, len >> 2, max_pitch >> 2, best_pitch, 0, maxcorr); + + /* Finer search with 2x decimation */ + maxcorr = 1; + for (i = 0; i < max_pitch >> 1; i++) { + int sum; + xcorr[i] = 0; + if (Inlines.abs(i - 2 * best_pitch[0]) > 2 && Inlines.abs(i - 2 * best_pitch[1]) > 2) { + continue; + } + sum = 0; + for (j = 0; j < len >> 1; j++) { + sum += Inlines.SHR32(Inlines.MULT16_16(x_lp[x_lp_ptr + j], y[i + j]), shift); + } + + xcorr[i] = Inlines.MAX32(-1, sum); + maxcorr = Inlines.MAX32(maxcorr, sum); + } + find_best_pitch(xcorr, y, len >> 1, max_pitch >> 1, best_pitch, shift + 1, maxcorr); + + /* Refine by pseudo-interpolation */ + if (best_pitch[0] > 0 && best_pitch[0] < (max_pitch >> 1) - 1) { + int a, b, c; + a = xcorr[best_pitch[0] - 1]; + b = xcorr[best_pitch[0]]; + c = xcorr[best_pitch[0] + 1]; + if ((c - a) > Inlines.MULT16_32_Q15(((short) (0.5 + (.7f) * (((int) 1) << (15))))/*Inlines.QCONST16(.7f, 15)*/, b - a)) { + offset = 1; + } else if ((a - c) > Inlines.MULT16_32_Q15(((short) (0.5 + (.7f) * (((int) 1) << (15))))/*Inlines.QCONST16(.7f, 15)*/, b - c)) { + offset = -1; + } else { + offset = 0; + } + } else { + offset = 0; + } + + pitch.Val = 2 * best_pitch[0] - offset; + } + + private static final int[] second_check = {0, 0, 3, 2, 3, 2, 5, 2, 3, 2, 3, 2, 5, 2, 3, 2}; + + static int remove_doubling(int[] x, int maxperiod, int minperiod, + int N, BoxedValueInt T0_, int prev_period, int prev_gain) { + int k, i, T, T0; + int g, g0; + int pg; + int yy, xx, xy, xy2; + int[] xcorr = new int[3]; + int best_xy, best_yy; + int offset; + int minperiod0 = minperiod; + maxperiod /= 2; + minperiod /= 2; + T0_.Val /= 2; + prev_period /= 2; + N /= 2; + int x_ptr = maxperiod; + if (T0_.Val >= maxperiod) { + T0_.Val = maxperiod - 1; + } + + T = T0 = T0_.Val; + int[] yy_lookup = new int[maxperiod + 1]; + BoxedValueInt boxed_xx = new BoxedValueInt(0); + BoxedValueInt boxed_xy = new BoxedValueInt(0); + BoxedValueInt boxed_xy2 = new BoxedValueInt(0); + Kernels.dual_inner_prod(x, x_ptr, x, x_ptr, x, x_ptr - T0, N, boxed_xx, boxed_xy); + xx = boxed_xx.Val; + xy = boxed_xy.Val; + yy_lookup[0] = xx; + yy = xx; + for (i = 1; i <= maxperiod; i++) { + int xi = x_ptr - i; + yy = yy + Inlines.MULT16_16(x[xi], x[xi]) - Inlines.MULT16_16(x[xi + N], x[xi + N]); + yy_lookup[i] = Inlines.MAX32(0, yy); + } + yy = yy_lookup[T0]; + best_xy = xy; + best_yy = yy; + + { + int x2y2; + int sh, t; + x2y2 = 1 + Inlines.HALF32(Inlines.MULT32_32_Q31(xx, yy)); + sh = Inlines.celt_ilog2(x2y2) >> 1; + t = Inlines.VSHR32(x2y2, 2 * (sh - 7)); + g = (Inlines.VSHR32(Inlines.MULT16_32_Q15(Inlines.celt_rsqrt_norm(t), xy), sh + 1)); + g0 = g; + } + + /* Look for any pitch at T/k */ + for (k = 2; k <= 15; k++) { + int T1, T1b; + int g1; + int cont = 0; + int thresh; + T1 = Inlines.celt_udiv(2 * T0 + k, 2 * k); + if (T1 < minperiod) { + break; + } + + /* Look for another strong correlation at T1b */ + if (k == 2) { + if (T1 + T0 > maxperiod) { + T1b = T0; + } else { + T1b = T0 + T1; + } + } else { + T1b = Inlines.celt_udiv(2 * second_check[k] * T0 + k, 2 * k); + } + + Kernels.dual_inner_prod(x, x_ptr, x, x_ptr - T1, x, x_ptr - T1b, N, boxed_xy, boxed_xy2); + xy = boxed_xy.Val; + xy2 = boxed_xy2.Val; + + xy += xy2; + yy = yy_lookup[T1] + yy_lookup[T1b]; + + { + int x2y2; + int sh, t; + x2y2 = 1 + Inlines.MULT32_32_Q31(xx, yy); + sh = Inlines.celt_ilog2(x2y2) >> 1; + t = Inlines.VSHR32(x2y2, 2 * (sh - 7)); + g1 = (Inlines.VSHR32(Inlines.MULT16_32_Q15(Inlines.celt_rsqrt_norm(t), xy), sh + 1)); + } + + if (Inlines.abs(T1 - prev_period) <= 1) { + cont = prev_gain; + } else if (Inlines.abs(T1 - prev_period) <= 2 && 5 * k * k < T0) { + cont = Inlines.HALF16(prev_gain); + } else { + cont = 0; + } + thresh = Inlines.MAX16(((short) (0.5 + (.3f) * (((int) 1) << (15))))/*Inlines.QCONST16(.3f, 15)*/, (Inlines.MULT16_16_Q15(((short) (0.5 + (.7f) * (((int) 1) << (15))))/*Inlines.QCONST16(.7f, 15)*/, g0) - cont)); + + /* Bias against very high pitch (very short period) to avoid false-positives + due to short-term correlation */ + if (T1 < 3 * minperiod) { + thresh = Inlines.MAX16(((short) (0.5 + (.4f) * (((int) 1) << (15))))/*Inlines.QCONST16(.4f, 15)*/, (Inlines.MULT16_16_Q15(((short) (0.5 + (.85f) * (((int) 1) << (15))))/*Inlines.QCONST16(.85f, 15)*/, g0) - cont)); + } else if (T1 < 2 * minperiod) { + thresh = Inlines.MAX16(((short) (0.5 + (.5f) * (((int) 1) << (15))))/*Inlines.QCONST16(.5f, 15)*/, (Inlines.MULT16_16_Q15(((short) (0.5 + (.9f) * (((int) 1) << (15))))/*Inlines.QCONST16(.9f, 15)*/, g0) - cont)); + } + if (g1 > thresh) { + best_xy = xy; + best_yy = yy; + T = T1; + g = g1; + } + } + + best_xy = Inlines.MAX32(0, best_xy); + if (best_yy <= best_xy) { + pg = CeltConstants.Q15ONE; + } else { + pg = (Inlines.SHR32(Inlines.frac_div32(best_xy, best_yy + 1), 16)); + } + + for (k = 0; k < 3; k++) { + xcorr[k] = Kernels.celt_inner_prod(x, x_ptr, x, x_ptr - (T + k - 1), N); + } + + if ((xcorr[2] - xcorr[0]) > Inlines.MULT16_32_Q15(((short) (0.5 + (.7f) * (((int) 1) << (15))))/*Inlines.QCONST16(.7f, 15)*/, xcorr[1] - xcorr[0])) { + offset = 1; + } else if ((xcorr[0] - xcorr[2]) > Inlines.MULT16_32_Q15(((short) (0.5 + (.7f) * (((int) 1) << (15))))/*Inlines.QCONST16(.7f, 15)*/, xcorr[1] - xcorr[2])) { + offset = -1; + } else { + offset = 0; + } + + if (pg > g) { + pg = g; + } + + T0_.Val = 2 * T + offset; + + if (T0_.Val < minperiod0) { + T0_.Val = minperiod0; + } + + return pg; + } +} diff --git a/app/src/main/java/org/concentus/PitchAnalysisCore.java b/app/src/main/java/org/concentus/PitchAnalysisCore.java new file mode 100644 index 0000000000..15f262bd96 --- /dev/null +++ b/app/src/main/java/org/concentus/PitchAnalysisCore.java @@ -0,0 +1,758 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class PitchAnalysisCore { + + private static final int SCRATCH_SIZE = 22; + private static final int SF_LENGTH_4KHZ = (SilkConstants.PE_SUBFR_LENGTH_MS * 4); + private static final int SF_LENGTH_8KHZ = (SilkConstants.PE_SUBFR_LENGTH_MS * 8); + private static final int MIN_LAG_4KHZ = (SilkConstants.PE_MIN_LAG_MS * 4); + private static final int MIN_LAG_8KHZ = (SilkConstants.PE_MIN_LAG_MS * 8); + private static final int MAX_LAG_4KHZ = (SilkConstants.PE_MAX_LAG_MS * 4); + private static final int MAX_LAG_8KHZ = (SilkConstants.PE_MAX_LAG_MS * 8 - 1); + private static final int CSTRIDE_4KHZ = (MAX_LAG_4KHZ + 1 - MIN_LAG_4KHZ); + private static final int CSTRIDE_8KHZ = (MAX_LAG_8KHZ + 3 - (MIN_LAG_8KHZ - 2)); + private static final int D_COMP_MIN = (MIN_LAG_8KHZ - 3); + private static final int D_COMP_MAX = (MAX_LAG_8KHZ + 4); + private static final int D_COMP_STRIDE = (D_COMP_MAX - D_COMP_MIN); + + // typedef int silk_pe_stage3_vals[SilkConstants.PE_NB_STAGE3_LAGS]; + // fixme can I linearize this? + static class silk_pe_stage3_vals { + + public final int[] Values = new int[SilkConstants.PE_NB_STAGE3_LAGS]; + } + + /** + * ********************************************************** + */ + /* FIXED POINT CORE PITCH ANALYSIS FUNCTION */ + /** + * ********************************************************** + */ + static int silk_pitch_analysis_core( /* O Voicing estimate: 0 voiced, 1 unvoiced */ + short[] frame, /* I Signal of length PE_FRAME_LENGTH_MS*Fs_kHz */ + int[] pitch_out, /* O 4 pitch lag values */ + BoxedValueShort lagIndex, /* O Lag Index */ + BoxedValueByte contourIndex, /* O Pitch contour Index */ + BoxedValueInt LTPCorr_Q15, /* I/O Normalized correlation; input: value from previous frame */ + int prevLag, /* I Last lag of previous frame; set to zero is unvoiced */ + int search_thres1_Q16, /* I First stage threshold for lag candidates 0 - 1 */ + int search_thres2_Q13, /* I Final threshold for lag candidates 0 - 1 */ + int Fs_kHz, /* I Sample frequency (kHz) */ + int complexity, /* I Complexity setting, 0-2, where 2 is highest */ + int nb_subfr /* I number of 5 ms subframes */ + ) { + short[] frame_8kHz; + short[] frame_4kHz; + int[] filt_state = new int[6]; + short[] input_frame_ptr; + int i, k, d, j; + short[] C; + int[] xcorr32; + short[] basis; + int basis_ptr; + short[] target; + int target_ptr; + int cross_corr, normalizer, energy, shift, energy_basis, energy_target; + int Cmax, length_d_srch, length_d_comp; + int[] d_srch = new int[SilkConstants.PE_D_SRCH_LENGTH]; + short[] d_comp; + int sum, threshold, lag_counter; + int CBimax, CBimax_new, CBimax_old, lag, start_lag, end_lag, lag_new; + int CCmax, CCmax_b, CCmax_new_b, CCmax_new; + int[] CC = new int[SilkConstants.PE_NB_CBKS_STAGE2_EXT]; + silk_pe_stage3_vals[] energies_st3; + silk_pe_stage3_vals[] cross_corr_st3; + int frame_length, frame_length_8kHz, frame_length_4kHz; + int sf_length; + int min_lag; + int max_lag; + int contour_bias_Q15, diff; + int nb_cbk_search; + int delta_lag_log2_sqr_Q7, lag_log2_Q7, prevLag_log2_Q7, prev_lag_bias_Q13; + byte[][] Lag_CB_ptr; + + /* Check for valid sampling frequency */ + Inlines.OpusAssert(Fs_kHz == 8 || Fs_kHz == 12 || Fs_kHz == 16); + + /* Check for valid complexity setting */ + Inlines.OpusAssert(complexity >= SilkConstants.SILK_PE_MIN_COMPLEX); + Inlines.OpusAssert(complexity <= SilkConstants.SILK_PE_MAX_COMPLEX); + + Inlines.OpusAssert(search_thres1_Q16 >= 0 && search_thres1_Q16 <= (1 << 16)); + Inlines.OpusAssert(search_thres2_Q13 >= 0 && search_thres2_Q13 <= (1 << 13)); + + /* Set up frame lengths max / min lag for the sampling frequency */ + frame_length = (SilkConstants.PE_LTP_MEM_LENGTH_MS + nb_subfr * SilkConstants.PE_SUBFR_LENGTH_MS) * Fs_kHz; + frame_length_4kHz = (SilkConstants.PE_LTP_MEM_LENGTH_MS + nb_subfr * SilkConstants.PE_SUBFR_LENGTH_MS) * 4; + frame_length_8kHz = (SilkConstants.PE_LTP_MEM_LENGTH_MS + nb_subfr * SilkConstants.PE_SUBFR_LENGTH_MS) * 8; + sf_length = SilkConstants.PE_SUBFR_LENGTH_MS * Fs_kHz; + min_lag = SilkConstants.PE_MIN_LAG_MS * Fs_kHz; + max_lag = SilkConstants.PE_MAX_LAG_MS * Fs_kHz - 1; + + /* Resample from input sampled at Fs_kHz to 8 kHz */ + frame_8kHz = new short[frame_length_8kHz]; + if (Fs_kHz == 16) { + Arrays.MemSet(filt_state, 0, 2); + Resampler.silk_resampler_down2(filt_state, frame_8kHz, frame, frame_length); + } else if (Fs_kHz == 12) { + Arrays.MemSet(filt_state, 0, 6); + Resampler.silk_resampler_down2_3(filt_state, frame_8kHz, frame, frame_length); + } else { + Inlines.OpusAssert(Fs_kHz == 8); + System.arraycopy(frame, 0, frame_8kHz, 0, frame_length_8kHz); + } + + /* Decimate again to 4 kHz */ + Arrays.MemSet(filt_state, 0, 2); + /* Set state to zero */ + frame_4kHz = new short[frame_length_4kHz]; + Resampler.silk_resampler_down2(filt_state, frame_4kHz, frame_8kHz, frame_length_8kHz); + + /* Low-pass filter */ + for (i = frame_length_4kHz - 1; i > 0; i--) { + frame_4kHz[i] = Inlines.silk_ADD_SAT16(frame_4kHz[i], frame_4kHz[i - 1]); + } + + /** + * ***************************************************************************** + ** Scale 4 kHz signal down to prevent correlations measures from + * overflowing * find scaling as max scaling for each 8kHz(?) subframe + ****************************************************************************** + */ + + /* Inner product is calculated with different lengths, so scale for the worst case */ + BoxedValueInt boxed_energy = new BoxedValueInt(0); + BoxedValueInt boxed_shift = new BoxedValueInt(0); + SumSqrShift.silk_sum_sqr_shift(boxed_energy, boxed_shift, frame_4kHz, frame_length_4kHz); + energy = boxed_energy.Val; + shift = boxed_shift.Val; + + if (shift > 0) { + shift = Inlines.silk_RSHIFT(shift, 1); + for (i = 0; i < frame_length_4kHz; i++) { + frame_4kHz[i] = Inlines.silk_RSHIFT16(frame_4kHz[i], shift); + } + } + + /** + * **************************************************************************** + * FIRST STAGE, operating in 4 khz + ***************************************************************************** + */ + C = new short[nb_subfr * CSTRIDE_8KHZ]; + xcorr32 = new int[MAX_LAG_4KHZ - MIN_LAG_4KHZ + 1]; + Arrays.MemSet(C, (short) 0, (nb_subfr >> 1) * CSTRIDE_4KHZ); + target = frame_4kHz; + target_ptr = Inlines.silk_LSHIFT(SF_LENGTH_4KHZ, 2); + for (k = 0; k < nb_subfr >> 1; k++) { + basis = target; + basis_ptr = target_ptr - MIN_LAG_4KHZ; + + CeltPitchXCorr.pitch_xcorr(target, target_ptr, target, target_ptr - MAX_LAG_4KHZ, xcorr32, SF_LENGTH_8KHZ, MAX_LAG_4KHZ - MIN_LAG_4KHZ + 1); + + /* Calculate first vector products before loop */ + cross_corr = xcorr32[MAX_LAG_4KHZ - MIN_LAG_4KHZ]; + normalizer = Inlines.silk_inner_prod_self(target, target_ptr, SF_LENGTH_8KHZ); + normalizer = Inlines.silk_ADD32(normalizer, Inlines.silk_inner_prod_self(basis, basis_ptr, SF_LENGTH_8KHZ)); + normalizer = Inlines.silk_ADD32(normalizer, Inlines.silk_SMULBB(SF_LENGTH_8KHZ, 4000)); + + Inlines.MatrixSet(C, k, 0, CSTRIDE_4KHZ, + (short) Inlines.silk_DIV32_varQ(cross_corr, normalizer, 13 + 1)); + /* Q13 */ + + /* From now on normalizer is computed recursively */ + for (d = MIN_LAG_4KHZ + 1; d <= MAX_LAG_4KHZ; d++) { + basis_ptr--; + + cross_corr = xcorr32[MAX_LAG_4KHZ - d]; + + /* Add contribution of new sample and remove contribution from oldest sample */ + normalizer = Inlines.silk_ADD32(normalizer, + Inlines.silk_SMULBB(basis[basis_ptr], basis[basis_ptr]) + - Inlines.silk_SMULBB(basis[basis_ptr + SF_LENGTH_8KHZ], basis[basis_ptr + SF_LENGTH_8KHZ])); + + Inlines.MatrixSet(C, k, d - MIN_LAG_4KHZ, CSTRIDE_4KHZ, + (short) Inlines.silk_DIV32_varQ(cross_corr, normalizer, 13 + 1)); + /* Q13 */ + } + /* Update target pointer */ + target_ptr += SF_LENGTH_8KHZ; + } + + /* Combine two subframes into single correlation measure and apply short-lag bias */ + if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR) { + for (i = MAX_LAG_4KHZ; i >= MIN_LAG_4KHZ; i--) { + sum = (int) Inlines.MatrixGet(C, 0, i - MIN_LAG_4KHZ, CSTRIDE_4KHZ) + + (int) Inlines.MatrixGet(C, 1, i - MIN_LAG_4KHZ, CSTRIDE_4KHZ); + /* Q14 */ + sum = Inlines.silk_SMLAWB(sum, sum, Inlines.silk_LSHIFT(-i, 4)); + /* Q14 */ + C[i - MIN_LAG_4KHZ] = (short) sum; + /* Q14 */ + } + } else { + /* Only short-lag bias */ + for (i = MAX_LAG_4KHZ; i >= MIN_LAG_4KHZ; i--) { + sum = Inlines.silk_LSHIFT((int) C[i - MIN_LAG_4KHZ], 1); + /* Q14 */ + sum = Inlines.silk_SMLAWB(sum, sum, Inlines.silk_LSHIFT(-i, 4)); + /* Q14 */ + C[i - MIN_LAG_4KHZ] = (short) sum; + /* Q14 */ + } + } + + /* Sort */ + length_d_srch = Inlines.silk_ADD_LSHIFT32(4, complexity, 1); + Inlines.OpusAssert(3 * length_d_srch <= SilkConstants.PE_D_SRCH_LENGTH); + Sort.silk_insertion_sort_decreasing_int16(C, d_srch, CSTRIDE_4KHZ, length_d_srch); + + /* Escape if correlation is very low already here */ + Cmax = (int) C[0]; + /* Q14 */ + if (Cmax < ((int) ((0.2f) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.2f, 14)*/) { + Arrays.MemSet(pitch_out, 0, nb_subfr); + LTPCorr_Q15.Val = 0; + lagIndex.Val = 0; + contourIndex.Val = 0; + + return 1; + } + + threshold = Inlines.silk_SMULWB(search_thres1_Q16, Cmax); + for (i = 0; i < length_d_srch; i++) { + /* Convert to 8 kHz indices for the sorted correlation that exceeds the threshold */ + if (C[i] > threshold) { + d_srch[i] = Inlines.silk_LSHIFT(d_srch[i] + MIN_LAG_4KHZ, 1); + } else { + length_d_srch = i; + break; + } + } + Inlines.OpusAssert(length_d_srch > 0); + + d_comp = new short[D_COMP_STRIDE]; + for (i = D_COMP_MIN; i < D_COMP_MAX; i++) { + d_comp[i - D_COMP_MIN] = 0; + } + for (i = 0; i < length_d_srch; i++) { + d_comp[d_srch[i] - D_COMP_MIN] = 1; + } + + /* Convolution */ + for (i = D_COMP_MAX - 1; i >= MIN_LAG_8KHZ; i--) { + d_comp[i - D_COMP_MIN] += (short) (d_comp[i - 1 - D_COMP_MIN] + d_comp[i - 2 - D_COMP_MIN]); + } + + length_d_srch = 0; + for (i = MIN_LAG_8KHZ; i < MAX_LAG_8KHZ + 1; i++) { + if (d_comp[i + 1 - D_COMP_MIN] > 0) { + d_srch[length_d_srch] = i; + length_d_srch++; + } + } + + /* Convolution */ + for (i = D_COMP_MAX - 1; i >= MIN_LAG_8KHZ; i--) { + d_comp[i - D_COMP_MIN] += (short) (d_comp[i - 1 - D_COMP_MIN] + d_comp[i - 2 - D_COMP_MIN] + d_comp[i - 3 - D_COMP_MIN]); + } + + length_d_comp = 0; + for (i = MIN_LAG_8KHZ; i < D_COMP_MAX; i++) { + if (d_comp[i - D_COMP_MIN] > 0) { + d_comp[length_d_comp] = (short) (i - 2); + length_d_comp++; + } + } + + /** + * ******************************************************************************** + ** SECOND STAGE, operating at 8 kHz, on lag sections with high + * correlation + ************************************************************************************ + */ + /** + * **************************************************************************** + ** Scale signal down to avoid correlations measures from overflowing + ****************************************************************************** + */ + /* find scaling as max scaling for each subframe */ + boxed_energy.Val = 0; + boxed_shift.Val = 0; + SumSqrShift.silk_sum_sqr_shift(boxed_energy, boxed_shift, frame_8kHz, frame_length_8kHz); + energy = boxed_energy.Val; + shift = boxed_shift.Val; + + if (shift > 0) { + shift = Inlines.silk_RSHIFT(shift, 1); + for (i = 0; i < frame_length_8kHz; i++) { + frame_8kHz[i] = Inlines.silk_RSHIFT16(frame_8kHz[i], shift); + } + } + + /** + * ******************************************************************************* + * Find energy of each subframe projected onto its history, for a range + * of delays + ******************************************************************************** + */ + Arrays.MemSet(C, (short) 0, nb_subfr * CSTRIDE_8KHZ); + + target = frame_8kHz; + target_ptr = SilkConstants.PE_LTP_MEM_LENGTH_MS * 8; + for (k = 0; k < nb_subfr; k++) { + + energy_target = Inlines.silk_ADD32(Inlines.silk_inner_prod(target, target_ptr, target, target_ptr, SF_LENGTH_8KHZ), 1); + for (j = 0; j < length_d_comp; j++) { + d = d_comp[j]; + basis = target; + basis_ptr = target_ptr - d; + + cross_corr = Inlines.silk_inner_prod(target, target_ptr, basis, basis_ptr, SF_LENGTH_8KHZ); + if (cross_corr > 0) { + energy_basis = Inlines.silk_inner_prod_self(basis, basis_ptr, SF_LENGTH_8KHZ); + Inlines.MatrixSet(C, k, d - (MIN_LAG_8KHZ - 2), CSTRIDE_8KHZ, + (short) Inlines.silk_DIV32_varQ(cross_corr, + Inlines.silk_ADD32(energy_target, + energy_basis), + 13 + 1)); + /* Q13 */ + } else { + Inlines.MatrixSet(C, k, d - (MIN_LAG_8KHZ - 2), CSTRIDE_8KHZ, (short) 0); + } + } + target_ptr += SF_LENGTH_8KHZ; + } + + /* search over lag range and lags codebook */ + /* scale factor for lag codebook, as a function of center lag */ + CCmax = Integer.MIN_VALUE; + CCmax_b = Integer.MIN_VALUE; + + CBimax = 0; + /* To avoid returning undefined lag values */ + lag = -1; + /* To check if lag with strong enough correlation has been found */ + + if (prevLag > 0) { + if (Fs_kHz == 12) { + prevLag = Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(prevLag, 1), 3); + } else if (Fs_kHz == 16) { + prevLag = Inlines.silk_RSHIFT(prevLag, 1); + } + prevLag_log2_Q7 = Inlines.silk_lin2log((int) prevLag); + } else { + prevLag_log2_Q7 = 0; + } + Inlines.OpusAssert(search_thres2_Q13 == Inlines.silk_SAT16(search_thres2_Q13)); + /* Set up stage 2 codebook based on number of subframes */ + if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR) { + Lag_CB_ptr = SilkTables.silk_CB_lags_stage2; + if (Fs_kHz == 8 && complexity > SilkConstants.SILK_PE_MIN_COMPLEX) { + /* If input is 8 khz use a larger codebook here because it is last stage */ + nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE2_EXT; + } else { + nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE2; + } + } else { + Lag_CB_ptr = SilkTables.silk_CB_lags_stage2_10_ms; + nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE2_10MS; + } + + for (k = 0; k < length_d_srch; k++) { + d = d_srch[k]; + for (j = 0; j < nb_cbk_search; j++) { + CC[j] = 0; + for (i = 0; i < nb_subfr; i++) { + int d_subfr; + /* Try all codebooks */ + d_subfr = d + Lag_CB_ptr[i][j]; + CC[j] = CC[j] + + (int) Inlines.MatrixGet(C, i, + d_subfr - (MIN_LAG_8KHZ - 2), + CSTRIDE_8KHZ); + } + } + /* Find best codebook */ + CCmax_new = Integer.MIN_VALUE; + CBimax_new = 0; + for (i = 0; i < nb_cbk_search; i++) { + if (CC[i] > CCmax_new) { + CCmax_new = CC[i]; + CBimax_new = i; + } + } + + /* Bias towards shorter lags */ + lag_log2_Q7 = Inlines.silk_lin2log(d); + /* Q7 */ + Inlines.OpusAssert(lag_log2_Q7 == Inlines.silk_SAT16(lag_log2_Q7)); + Inlines.OpusAssert(nb_subfr * ((int) ((SilkConstants.PE_SHORTLAG_BIAS) * ((long) 1 << (13)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_SHORTLAG_BIAS, 13)*/ == Inlines.silk_SAT16(nb_subfr * ((int) ((SilkConstants.PE_SHORTLAG_BIAS) * ((long) 1 << (13)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_SHORTLAG_BIAS, 13)*/)); + CCmax_new_b = CCmax_new - Inlines.silk_RSHIFT(Inlines.silk_SMULBB(nb_subfr * ((int) ((SilkConstants.PE_SHORTLAG_BIAS) * ((long) 1 << (13)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_SHORTLAG_BIAS, 13)*/, lag_log2_Q7), 7); + /* Q13 */ + + /* Bias towards previous lag */ + Inlines.OpusAssert(nb_subfr * ((int) ((SilkConstants.PE_PREVLAG_BIAS) * ((long) 1 << (13)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_PREVLAG_BIAS, 13)*/ == Inlines.silk_SAT16(nb_subfr * ((int) ((SilkConstants.PE_PREVLAG_BIAS) * ((long) 1 << (13)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_PREVLAG_BIAS, 13)*/)); + if (prevLag > 0) { + delta_lag_log2_sqr_Q7 = lag_log2_Q7 - prevLag_log2_Q7; + Inlines.OpusAssert(delta_lag_log2_sqr_Q7 == Inlines.silk_SAT16(delta_lag_log2_sqr_Q7)); + delta_lag_log2_sqr_Q7 = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(delta_lag_log2_sqr_Q7, delta_lag_log2_sqr_Q7), 7); + prev_lag_bias_Q13 = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(nb_subfr * ((int) ((SilkConstants.PE_PREVLAG_BIAS) * ((long) 1 << (13)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_PREVLAG_BIAS, 13)*/, LTPCorr_Q15.Val), 15); + /* Q13 */ + prev_lag_bias_Q13 = Inlines.silk_DIV32(Inlines.silk_MUL(prev_lag_bias_Q13, delta_lag_log2_sqr_Q7), delta_lag_log2_sqr_Q7 + ((int) ((0.5f) * ((long) 1 << (7)) + 0.5))/*Inlines.SILK_CONST(0.5f, 7)*/); + CCmax_new_b -= prev_lag_bias_Q13; + /* Q13 */ + } + + if (CCmax_new_b > CCmax_b + && /* Find maximum biased correlation */ CCmax_new > Inlines.silk_SMULBB(nb_subfr, search_thres2_Q13) + && /* Correlation needs to be high enough to be voiced */ SilkTables.silk_CB_lags_stage2[0][CBimax_new] <= MIN_LAG_8KHZ /* Lag must be in range */) { + CCmax_b = CCmax_new_b; + CCmax = CCmax_new; + lag = d; + CBimax = CBimax_new; + } + } + + if (lag == -1) { + /* No suitable candidate found */ + Arrays.MemSet(pitch_out, 0, nb_subfr); + LTPCorr_Q15.Val = 0; + lagIndex.Val = 0; + contourIndex.Val = 0; + + return 1; + } + + /* Output normalized correlation */ + LTPCorr_Q15.Val = (int) Inlines.silk_LSHIFT(Inlines.silk_DIV32_16(CCmax, nb_subfr), 2); + Inlines.OpusAssert(LTPCorr_Q15.Val >= 0); + + if (Fs_kHz > 8) { + short[] scratch_mem; + /** + * ************************************************************************ + */ + /* Scale input signal down to avoid correlations measures from overflowing */ + /** + * ************************************************************************ + */ + /* find scaling as max scaling for each subframe */ + boxed_energy.Val = 0; + boxed_shift.Val = 0; + SumSqrShift.silk_sum_sqr_shift(boxed_energy, boxed_shift, frame, frame_length); + energy = boxed_energy.Val; + shift = boxed_shift.Val; + + if (shift > 0) { + scratch_mem = new short[frame_length]; + /* Move signal to scratch mem because the input signal should be unchanged */ + shift = Inlines.silk_RSHIFT(shift, 1); + for (i = 0; i < frame_length; i++) { + scratch_mem[i] = Inlines.silk_RSHIFT16(frame[i], shift); + } + input_frame_ptr = scratch_mem; + } else { + input_frame_ptr = frame; + } + + /* Search in original signal */ + CBimax_old = CBimax; + /* Compensate for decimation */ + Inlines.OpusAssert(lag == Inlines.silk_SAT16(lag)); + if (Fs_kHz == 12) { + lag = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(lag, 3), 1); + } else if (Fs_kHz == 16) { + lag = Inlines.silk_LSHIFT(lag, 1); + } else { + lag = Inlines.silk_SMULBB(lag, 3); + } + + lag = Inlines.silk_LIMIT_int(lag, min_lag, max_lag); + start_lag = Inlines.silk_max_int(lag - 2, min_lag); + end_lag = Inlines.silk_min_int(lag + 2, max_lag); + lag_new = lag; + /* to avoid undefined lag */ + CBimax = 0; + /* to avoid undefined lag */ + + CCmax = Integer.MIN_VALUE; + /* pitch lags according to second stage */ + for (k = 0; k < nb_subfr; k++) { + pitch_out[k] = lag + 2 * SilkTables.silk_CB_lags_stage2[k][CBimax_old]; + } + + /* Set up codebook parameters according to complexity setting and frame length */ + if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR) { + nb_cbk_search = (int) SilkTables.silk_nb_cbk_searchs_stage3[complexity]; + Lag_CB_ptr = SilkTables.silk_CB_lags_stage3; + } else { + nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE3_10MS; + Lag_CB_ptr = SilkTables.silk_CB_lags_stage3_10_ms; + } + + /* Calculate the correlations and energies needed in stage 3 */ + energies_st3 = new silk_pe_stage3_vals[nb_subfr * nb_cbk_search]; + cross_corr_st3 = new silk_pe_stage3_vals[nb_subfr * nb_cbk_search]; + for (int c = 0; c < nb_subfr * nb_cbk_search; c++) { + energies_st3[c] = new silk_pe_stage3_vals(); // fixme: these can be replaced with a linearized array probably, or at least a struct + cross_corr_st3[c] = new silk_pe_stage3_vals(); + } + silk_P_Ana_calc_corr_st3(cross_corr_st3, input_frame_ptr, start_lag, sf_length, nb_subfr, complexity); + silk_P_Ana_calc_energy_st3(energies_st3, input_frame_ptr, start_lag, sf_length, nb_subfr, complexity); + + lag_counter = 0; + Inlines.OpusAssert(lag == Inlines.silk_SAT16(lag)); + contour_bias_Q15 = Inlines.silk_DIV32_16(((int) ((SilkConstants.PE_FLATCONTOUR_BIAS) * ((long) 1 << (15)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_FLATCONTOUR_BIAS, 15)*/, lag); + + target = input_frame_ptr; + target_ptr = SilkConstants.PE_LTP_MEM_LENGTH_MS * Fs_kHz; + energy_target = Inlines.silk_ADD32(Inlines.silk_inner_prod_self(target, target_ptr, nb_subfr * sf_length), 1); + for (d = start_lag; d <= end_lag; d++) { + for (j = 0; j < nb_cbk_search; j++) { + cross_corr = 0; + energy = energy_target; + for (k = 0; k < nb_subfr; k++) { + cross_corr = Inlines.silk_ADD32(cross_corr, + Inlines.MatrixGet(cross_corr_st3, k, j, + nb_cbk_search).Values[lag_counter]); + energy = Inlines.silk_ADD32(energy, + Inlines.MatrixGet(energies_st3, k, j, + nb_cbk_search).Values[lag_counter]); + Inlines.OpusAssert(energy >= 0); + } + if (cross_corr > 0) { + CCmax_new = Inlines.silk_DIV32_varQ(cross_corr, energy, 13 + 1); + /* Q13 */ + /* Reduce depending on flatness of contour */ + diff = Short.MAX_VALUE - Inlines.silk_MUL(contour_bias_Q15, j); + /* Q15 */ + Inlines.OpusAssert(diff == Inlines.silk_SAT16(diff)); + CCmax_new = Inlines.silk_SMULWB(CCmax_new, diff); + /* Q14 */ + } else { + CCmax_new = 0; + } + + if (CCmax_new > CCmax && (d + SilkTables.silk_CB_lags_stage3[0][j]) <= max_lag) { + CCmax = CCmax_new; + lag_new = d; + CBimax = j; + } + } + lag_counter++; + } + + for (k = 0; k < nb_subfr; k++) { + pitch_out[k] = lag_new + Lag_CB_ptr[k][CBimax]; + pitch_out[k] = Inlines.silk_LIMIT(pitch_out[k], min_lag, SilkConstants.PE_MAX_LAG_MS * Fs_kHz); + } + lagIndex.Val = (short) (lag_new - min_lag); + contourIndex.Val = (byte) CBimax; + } else { + /* Fs_kHz == 8 */ + /* Save Lags */ + for (k = 0; k < nb_subfr; k++) { + pitch_out[k] = lag + Lag_CB_ptr[k][CBimax]; + pitch_out[k] = Inlines.silk_LIMIT(pitch_out[k], MIN_LAG_8KHZ, SilkConstants.PE_MAX_LAG_MS * 8); + } + lagIndex.Val = (short) (lag - MIN_LAG_8KHZ); + contourIndex.Val = (byte) CBimax; + } + Inlines.OpusAssert(lagIndex.Val >= 0); + /* return as voiced */ + + return 0; + } + + /** + * ********************************************************************* + * Calculates the correlations used in stage 3 search. In order to cover the + * whole lag codebook for all the searched offset lags (lag +- 2), the + * following correlations are needed in each sub frame: + * + * sf1: lag range [-8,...,7] total 16 correlations sf2: lag range [-4,...,4] + * total 9 correlations sf3: lag range [-3,....4] total 8 correltions sf4: + * lag range [-6,....8] total 15 correlations + * + * In total 48 correlations. The direct implementation computed in worst + * case 4*12*5 = 240 correlations, but more likely around 120. + ********************************************************************** + */ + private static void silk_P_Ana_calc_corr_st3( + silk_pe_stage3_vals[] cross_corr_st3, /* O 3 DIM correlation array */ + short[] frame, /* I vector to correlate */ + int start_lag, /* I lag offset to search around */ + int sf_length, /* I length of a 5 ms subframe */ + int nb_subfr, /* I number of subframes */ + int complexity /* I Complexity setting */ + ) { + int target_ptr; + int i, j, k, lag_counter, lag_low, lag_high; + int nb_cbk_search, delta, idx; + int[] scratch_mem; + int[] xcorr32; + byte[][] Lag_range_ptr; + byte[][] Lag_CB_ptr; + + Inlines.OpusAssert(complexity >= SilkConstants.SILK_PE_MIN_COMPLEX); + Inlines.OpusAssert(complexity <= SilkConstants.SILK_PE_MAX_COMPLEX); + + if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR) { + Lag_range_ptr = SilkTables.silk_Lag_range_stage3[complexity]; + Lag_CB_ptr = SilkTables.silk_CB_lags_stage3; + nb_cbk_search = SilkTables.silk_nb_cbk_searchs_stage3[complexity]; + } else { + Inlines.OpusAssert(nb_subfr == SilkConstants.PE_MAX_NB_SUBFR >> 1); + Lag_range_ptr = SilkTables.silk_Lag_range_stage3_10_ms; + Lag_CB_ptr = SilkTables.silk_CB_lags_stage3_10_ms; + nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE3_10MS; + } + scratch_mem = new int[SCRATCH_SIZE]; + xcorr32 = new int[SCRATCH_SIZE]; + + target_ptr = Inlines.silk_LSHIFT(sf_length, 2); + /* Pointer to middle of frame */ + for (k = 0; k < nb_subfr; k++) { + lag_counter = 0; + + /* Calculate the correlations for each subframe */ + lag_low = Lag_range_ptr[k][0]; + lag_high = Lag_range_ptr[k][1]; + Inlines.OpusAssert(lag_high - lag_low + 1 <= SCRATCH_SIZE); + CeltPitchXCorr.pitch_xcorr(frame, target_ptr, frame, target_ptr - start_lag - lag_high, xcorr32, sf_length, lag_high - lag_low + 1); + for (j = lag_low; j <= lag_high; j++) { + Inlines.OpusAssert(lag_counter < SCRATCH_SIZE); + scratch_mem[lag_counter] = xcorr32[lag_high - j]; + lag_counter++; + } + + delta = Lag_range_ptr[k][0]; + for (i = 0; i < nb_cbk_search; i++) { + /* Fill out the 3 dim array that stores the correlations for */ + /* each code_book vector for each start lag */ + idx = Lag_CB_ptr[k][i] - delta; + for (j = 0; j < SilkConstants.PE_NB_STAGE3_LAGS; j++) { + Inlines.OpusAssert(idx + j < SCRATCH_SIZE); + Inlines.OpusAssert(idx + j < lag_counter); + Inlines.MatrixGet(cross_corr_st3, k, i, nb_cbk_search).Values[j] + = scratch_mem[idx + j]; + } + } + target_ptr += sf_length; + } + + } + + /** + * ***************************************************************** + */ + /* Calculate the energies for first two subframes. The energies are */ + /* calculated recursively. */ + /** + * ***************************************************************** + */ + static void silk_P_Ana_calc_energy_st3( + silk_pe_stage3_vals[] energies_st3, /* O 3 DIM energy array */ + short[] frame, /* I vector to calc energy in */ + int start_lag, /* I lag offset to search around */ + int sf_length, /* I length of one 5 ms subframe */ + int nb_subfr, /* I number of subframes */ + int complexity /* I Complexity setting */ + ) { + int target_ptr, basis_ptr; + int energy; + int k, i, j, lag_counter; + int nb_cbk_search, delta, idx, lag_diff; + int[] scratch_mem; + byte[][] Lag_range_ptr; + byte[][] Lag_CB_ptr; + + Inlines.OpusAssert(complexity >= SilkConstants.SILK_PE_MIN_COMPLEX); + Inlines.OpusAssert(complexity <= SilkConstants.SILK_PE_MAX_COMPLEX); + + if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR) { + Lag_range_ptr = SilkTables.silk_Lag_range_stage3[complexity]; + Lag_CB_ptr = SilkTables.silk_CB_lags_stage3; + nb_cbk_search = SilkTables.silk_nb_cbk_searchs_stage3[complexity]; + } else { + Inlines.OpusAssert(nb_subfr == SilkConstants.PE_MAX_NB_SUBFR >> 1); + Lag_range_ptr = SilkTables.silk_Lag_range_stage3_10_ms; + Lag_CB_ptr = SilkTables.silk_CB_lags_stage3_10_ms; + nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE3_10MS; + } + scratch_mem = new int[SCRATCH_SIZE]; + + target_ptr = Inlines.silk_LSHIFT(sf_length, 2); + for (k = 0; k < nb_subfr; k++) { + lag_counter = 0; + + /* Calculate the energy for first lag */ + basis_ptr = target_ptr - (start_lag + Lag_range_ptr[k][0]); + energy = Inlines.silk_inner_prod_self(frame, basis_ptr, sf_length); + Inlines.OpusAssert(energy >= 0); + scratch_mem[lag_counter] = energy; + lag_counter++; + + lag_diff = (Lag_range_ptr[k][1] - Lag_range_ptr[k][0] + 1); + for (i = 1; i < lag_diff; i++) { + /* remove part outside new window */ + energy -= Inlines.silk_SMULBB(frame[basis_ptr + sf_length - i], frame[basis_ptr + sf_length - i]); + Inlines.OpusAssert(energy >= 0); + + /* add part that comes into window */ + energy = Inlines.silk_ADD_SAT32(energy, Inlines.silk_SMULBB(frame[basis_ptr - i], frame[basis_ptr - i])); + Inlines.OpusAssert(energy >= 0); + Inlines.OpusAssert(lag_counter < SCRATCH_SIZE); + scratch_mem[lag_counter] = energy; + lag_counter++; + } + + delta = Lag_range_ptr[k][0]; + for (i = 0; i < nb_cbk_search; i++) { + /* Fill out the 3 dim array that stores the correlations for */ + /* each code_book vector for each start lag */ + idx = Lag_CB_ptr[k][i] - delta; + for (j = 0; j < SilkConstants.PE_NB_STAGE3_LAGS; j++) { + Inlines.OpusAssert(idx + j < SCRATCH_SIZE); + Inlines.OpusAssert(idx + j < lag_counter); + Inlines.MatrixGet(energies_st3, k, i, nb_cbk_search).Values[j] = scratch_mem[idx + j]; + Inlines.OpusAssert(Inlines.MatrixGet(energies_st3, k, i, nb_cbk_search).Values[j] >= 0); + } + } + target_ptr += sf_length; + } + } +} diff --git a/app/src/main/java/org/concentus/ProcessGains.java b/app/src/main/java/org/concentus/ProcessGains.java new file mode 100644 index 0000000000..a80ced26db --- /dev/null +++ b/app/src/main/java/org/concentus/ProcessGains.java @@ -0,0 +1,123 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class ProcessGains { + + /* Processing of gains */ + static void silk_process_gains( + SilkChannelEncoder psEnc, /* I/O Encoder state */ + SilkEncoderControl psEncCtrl, /* I/O Encoder control */ + int condCoding /* I The type of conditional coding to use */ + ) { + SilkShapeState psShapeSt = psEnc.sShape; + int k; + int s_Q16, InvMaxSqrVal_Q16, gain, gain_squared, ResNrg, ResNrgPart, quant_offset_Q10; + + /* Gain reduction when LTP coding gain is high */ + if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED) { + /*s = -0.5f * silk_sigmoid( 0.25f * ( psEncCtrl.LTPredCodGain - 12.0f ) ); */ + s_Q16 = 0 - Sigmoid.silk_sigm_Q15(Inlines.silk_RSHIFT_ROUND(psEncCtrl.LTPredCodGain_Q7 - ((int) ((12.0f) * ((long) 1 << (7)) + 0.5))/*Inlines.SILK_CONST(12.0f, 7)*/, 4)); + for (k = 0; k < psEnc.nb_subfr; k++) { + psEncCtrl.Gains_Q16[k] = Inlines.silk_SMLAWB(psEncCtrl.Gains_Q16[k], psEncCtrl.Gains_Q16[k], s_Q16); + } + } + + /* Limit the quantized signal */ + /* InvMaxSqrVal = pow( 2.0f, 0.33f * ( 21.0f - SNR_dB ) ) / subfr_length; */ + InvMaxSqrVal_Q16 = Inlines.silk_DIV32_16(Inlines.silk_log2lin( + Inlines.silk_SMULWB(((int) ((21 + 16 / 0.33f) * ((long) 1 << (7)) + 0.5))/*Inlines.SILK_CONST(21 + 16 / 0.33f, 7)*/ - psEnc.SNR_dB_Q7, ((int) ((0.33f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.33f, 16)*/)), psEnc.subfr_length); + + for (k = 0; k < psEnc.nb_subfr; k++) { + /* Soft limit on ratio residual energy and squared gains */ + ResNrg = psEncCtrl.ResNrg[k]; + ResNrgPart = Inlines.silk_SMULWW(ResNrg, InvMaxSqrVal_Q16); + if (psEncCtrl.ResNrgQ[k] > 0) { + ResNrgPart = Inlines.silk_RSHIFT_ROUND(ResNrgPart, psEncCtrl.ResNrgQ[k]); + } else if (ResNrgPart >= Inlines.silk_RSHIFT(Integer.MAX_VALUE, -psEncCtrl.ResNrgQ[k])) { + ResNrgPart = Integer.MAX_VALUE; + } else { + ResNrgPart = Inlines.silk_LSHIFT(ResNrgPart, -psEncCtrl.ResNrgQ[k]); + } + gain = psEncCtrl.Gains_Q16[k]; + gain_squared = Inlines.silk_ADD_SAT32(ResNrgPart, Inlines.silk_SMMUL(gain, gain)); + if (gain_squared < Short.MAX_VALUE) { + /* recalculate with higher precision */ + gain_squared = Inlines.silk_SMLAWW(Inlines.silk_LSHIFT(ResNrgPart, 16), gain, gain); + Inlines.OpusAssert(gain_squared > 0); + gain = Inlines.silk_SQRT_APPROX(gain_squared); + /* Q8 */ + gain = Inlines.silk_min(gain, Integer.MAX_VALUE >> 8); + psEncCtrl.Gains_Q16[k] = Inlines.silk_LSHIFT_SAT32(gain, 8); + /* Q16 */ + } else { + gain = Inlines.silk_SQRT_APPROX(gain_squared); + /* Q0 */ + gain = Inlines.silk_min(gain, Integer.MAX_VALUE >> 16); + psEncCtrl.Gains_Q16[k] = Inlines.silk_LSHIFT_SAT32(gain, 16); + /* Q16 */ + } + + } + + /* Save unquantized gains and gain Index */ + System.arraycopy(psEncCtrl.Gains_Q16, 0, psEncCtrl.GainsUnq_Q16, 0, psEnc.nb_subfr); + psEncCtrl.lastGainIndexPrev = psShapeSt.LastGainIndex; + + /* Quantize gains */ + BoxedValueByte boxed_lastGainIndex = new BoxedValueByte(psShapeSt.LastGainIndex); + GainQuantization.silk_gains_quant(psEnc.indices.GainsIndices, psEncCtrl.Gains_Q16, + boxed_lastGainIndex, condCoding == SilkConstants.CODE_CONDITIONALLY ? 1 : 0, psEnc.nb_subfr); + psShapeSt.LastGainIndex = boxed_lastGainIndex.Val; + + /* Set quantizer offset for voiced signals. Larger offset when LTP coding gain is low or tilt is high (ie low-pass) */ + if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED) { + if (psEncCtrl.LTPredCodGain_Q7 + Inlines.silk_RSHIFT(psEnc.input_tilt_Q15, 8) > ((int) ((1.0f) * ((long) 1 << (7)) + 0.5))/*Inlines.SILK_CONST(1.0f, 7)*/) { + psEnc.indices.quantOffsetType = 0; + } else { + psEnc.indices.quantOffsetType = 1; + } + } + + /* Quantizer boundary adjustment */ + quant_offset_Q10 = SilkTables.silk_Quantization_Offsets_Q10[psEnc.indices.signalType >> 1][psEnc.indices.quantOffsetType]; + psEncCtrl.Lambda_Q10 = ((int) ((TuningParameters.LAMBDA_OFFSET) * ((long) 1 << (10)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LAMBDA_OFFSET, 10)*/ + + Inlines.silk_SMULBB(((int) ((TuningParameters.LAMBDA_DELAYED_DECISIONS) * ((long) 1 << (10)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LAMBDA_DELAYED_DECISIONS, 10)*/, psEnc.nStatesDelayedDecision) + + Inlines.silk_SMULWB(((int) ((TuningParameters.LAMBDA_SPEECH_ACT) * ((long) 1 << (18)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LAMBDA_SPEECH_ACT, 18)*/, psEnc.speech_activity_Q8) + + Inlines.silk_SMULWB(((int) ((TuningParameters.LAMBDA_INPUT_QUALITY) * ((long) 1 << (12)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LAMBDA_INPUT_QUALITY, 12)*/, psEncCtrl.input_quality_Q14) + + Inlines.silk_SMULWB(((int) ((TuningParameters.LAMBDA_CODING_QUALITY) * ((long) 1 << (12)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LAMBDA_CODING_QUALITY, 12)*/, psEncCtrl.coding_quality_Q14) + + Inlines.silk_SMULWB(((int) ((TuningParameters.LAMBDA_QUANT_OFFSET) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LAMBDA_QUANT_OFFSET, 16)*/, quant_offset_Q10); + + Inlines.OpusAssert(psEncCtrl.Lambda_Q10 > 0); + Inlines.OpusAssert(psEncCtrl.Lambda_Q10 < ((int) ((2) * ((long) 1 << (10)) + 0.5))/*Inlines.SILK_CONST(2, 10)*/); + } +} diff --git a/app/src/main/java/org/concentus/PulseCache.java b/app/src/main/java/org/concentus/PulseCache.java new file mode 100644 index 0000000000..bb397d02ef --- /dev/null +++ b/app/src/main/java/org/concentus/PulseCache.java @@ -0,0 +1,50 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class PulseCache { + + int size = 0; + short[] index = null; + short[] bits = null; + short[] caps = null; + + void Reset() { + size = 0; + index = null; + bits = null; + caps = null; + } +} diff --git a/app/src/main/java/org/concentus/QuantizeBands.java b/app/src/main/java/org/concentus/QuantizeBands.java new file mode 100644 index 0000000000..1585b30c5b --- /dev/null +++ b/app/src/main/java/org/concentus/QuantizeBands.java @@ -0,0 +1,461 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class QuantizeBands { + + /* prediction coefficients: 0.9, 0.8, 0.65, 0.5 */ + private static final int[] pred_coef = new int[]{29440, 26112, 21248, 16384}; + private static final int[] beta_coef = new int[]{30147, 22282, 12124, 6554}; + private static final int beta_intra = 4915; + private static short[] small_energy_icdf = {2, 1, 0}; + + static int loss_distortion(int[][] eBands, int[][] oldEBands, int start, int end, int len, int C) { + int c, i; + int dist = 0; + c = 0; + do { + for (i = start; i < end; i++) { + int d = Inlines.SUB16(Inlines.SHR16(eBands[c][i], 3), Inlines.SHR16(oldEBands[c][i], 3)); + dist = Inlines.MAC16_16(dist, d, d); + } + } while (++c < C); + + return Inlines.MIN32(200, Inlines.SHR32(dist, 2 * CeltConstants.DB_SHIFT - 6)); + } + + static int quant_coarse_energy_impl(CeltMode m, int start, int end, + int[][] eBands, int[][] oldEBands, + int budget, int tell, + short[] prob_model, int[][] error, EntropyCoder enc, + int C, int LM, int intra, int max_decay, int lfe) { + int i, c; + int badness = 0; + int[] prev = {0, 0}; + int coef; + int beta; + + if (tell + 3 <= budget) { + enc.enc_bit_logp(intra, 3); + } + + if (intra != 0) { + coef = 0; + beta = beta_intra; + } else { + beta = beta_coef[LM]; + coef = pred_coef[LM]; + } + + /* Encode at a fixed coarse resolution */ + for (i = start; i < end; i++) { + c = 0; + do { + int bits_left; + int qi, qi0; + int q; + int x; + int f, tmp; + int oldE; + int decay_bound; + x = eBands[c][i]; + oldE = Inlines.MAX16(-((short) (0.5 + (9.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(9.0f, CeltConstants.DB_SHIFT)*/, oldEBands[c][i]); + f = Inlines.SHL32(Inlines.EXTEND32(x), 7) - Inlines.PSHR32(Inlines.MULT16_16(coef, oldE), 8) - prev[c]; + /* Rounding to nearest integer here is really important! */ + qi = (f + ((int) (0.5 + (.5f) * (((int) 1) << (CeltConstants.DB_SHIFT + 7))))/*Inlines.QCONST32(.5f, CeltConstants.DB_SHIFT + 7)*/) >> (CeltConstants.DB_SHIFT + 7); + decay_bound = Inlines.EXTRACT16(Inlines.MAX32(-((short) (0.5 + (28.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/, + Inlines.SUB32((int) oldEBands[c][i], max_decay))); + /* Prevent the energy from going down too quickly (e.g. for bands + that have just one bin) */ + if (qi < 0 && x < decay_bound) { + qi += (int) Inlines.SHR16(Inlines.SUB16(decay_bound, x), CeltConstants.DB_SHIFT); + if (qi > 0) { + qi = 0; + } + } + qi0 = qi; + /* If we don't have enough bits to encode all the energy, just assume + something safe. */ + tell = enc.tell(); + bits_left = budget - tell - 3 * C * (end - i); + if (i != start && bits_left < 30) { + if (bits_left < 24) { + qi = Inlines.IMIN(1, qi); + } + if (bits_left < 16) { + qi = Inlines.IMAX(-1, qi); + } + } + if (lfe != 0 && i >= 2) { + qi = Inlines.IMIN(qi, 0); + } + if (budget - tell >= 15) { + int pi; + pi = 2 * Inlines.IMIN(i, 20); + BoxedValueInt boxed_qi = new BoxedValueInt(qi); + Laplace.ec_laplace_encode(enc, boxed_qi, ((prob_model[pi]) << 7), ((int) prob_model[pi + 1]) << 6); + qi = boxed_qi.Val; + } else if (budget - tell >= 2) { + qi = Inlines.IMAX(-1, Inlines.IMIN(qi, 1)); + enc.enc_icdf(2 * qi ^ (0 - (qi < 0 ? 1 : 0)), small_energy_icdf, 2); + } else if (budget - tell >= 1) { + qi = Inlines.IMIN(0, qi); + enc.enc_bit_logp(-qi, 1); + } else { + qi = -1; + } + error[c][i] = (Inlines.PSHR32(f, 7) - Inlines.SHL16((qi), CeltConstants.DB_SHIFT)); + badness += Inlines.abs(qi0 - qi); + q = (int) Inlines.SHL32(qi, CeltConstants.DB_SHIFT); + + tmp = Inlines.PSHR32(Inlines.MULT16_16(coef, oldE), 8) + prev[c] + Inlines.SHL32(q, 7); + tmp = Inlines.MAX32(-((int) (0.5 + (28.0f) * (((int) 1) << (CeltConstants.DB_SHIFT + 7))))/*Inlines.QCONST32(28.0f, CeltConstants.DB_SHIFT + 7)*/, tmp); + oldEBands[c][i] = (Inlines.PSHR32(tmp, 7)); + prev[c] = prev[c] + Inlines.SHL32(q, 7) - Inlines.MULT16_16(beta, Inlines.PSHR32(q, 8)); + } while (++c < C); + } + return lfe != 0 ? 0 : badness; + } + + static void quant_coarse_energy(CeltMode m, int start, int end, int effEnd, + int[][] eBands, int[][] oldEBands, int budget, + int[][] error, EntropyCoder enc, int C, int LM, int nbAvailableBytes, + int force_intra, BoxedValueInt delayedIntra, int two_pass, int loss_rate, int lfe) { + int intra; + int max_decay; + int[][] oldEBands_intra; + int[][] error_intra; + EntropyCoder enc_start_state = new EntropyCoder(); // [porting note] stack variable + int tell; + int badness1 = 0; + int intra_bias; + int new_distortion; + + intra = (force_intra != 0 || (two_pass == 0 && delayedIntra.Val > 2 * C * (end - start) && nbAvailableBytes > (end - start) * C)) ? 1 : 0; + intra_bias = ((budget * delayedIntra.Val * loss_rate) / (C * 512)); + new_distortion = loss_distortion(eBands, oldEBands, start, effEnd, m.nbEBands, C); + + tell = enc.tell(); + if (tell + 3 > budget) { + two_pass = intra = 0; + } + + max_decay = ((short) (0.5 + (16.0f) * (1 << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(16.0f, CeltConstants.DB_SHIFT)*/; + if (end - start > 10) { + max_decay = (Inlines.MIN32(max_decay, Inlines.SHL32(nbAvailableBytes, CeltConstants.DB_SHIFT - 3))); // opus bug: useless extend32 + } + if (lfe != 0) { + max_decay = ((short) (0.5 + (3.0f) * (1 << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(3.0f, CeltConstants.DB_SHIFT)*/; + } + enc_start_state.Assign(enc); + + oldEBands_intra = Arrays.InitTwoDimensionalArrayInt(C, m.nbEBands); + error_intra = Arrays.InitTwoDimensionalArrayInt(C, m.nbEBands); + System.arraycopy(oldEBands[0], 0, oldEBands_intra[0], 0, m.nbEBands); + if (C == 2) { + System.arraycopy(oldEBands[1], 0, oldEBands_intra[1], 0, m.nbEBands); + } + + if (two_pass != 0 || intra != 0) { + badness1 = quant_coarse_energy_impl(m, start, end, eBands, oldEBands_intra, budget, + tell, CeltTables.e_prob_model[LM][1], error_intra, enc, C, LM, 1, max_decay, lfe); + } + + if (intra == 0) { + int intra_buf; + EntropyCoder enc_intra_state = new EntropyCoder(); // [porting note] stack variable + int tell_intra; + int nstart_bytes; + int nintra_bytes; + int save_bytes; + int badness2; + byte[] intra_bits = null; + + tell_intra = (int) enc.tell_frac(); + + enc_intra_state.Assign(enc); + + nstart_bytes = enc_start_state.range_bytes(); + nintra_bytes = enc_intra_state.range_bytes(); + intra_buf = nstart_bytes; + save_bytes = nintra_bytes - nstart_bytes; + + if (save_bytes != 0) { + intra_bits = new byte[save_bytes]; + /* Copy bits from intra bit-stream */ + System.arraycopy(enc_intra_state.get_buffer(), intra_buf, intra_bits, 0, save_bytes); + } + + enc.Assign(enc_start_state); + + badness2 = quant_coarse_energy_impl(m, start, end, eBands, oldEBands, budget, + tell, CeltTables.e_prob_model[LM][intra], error, enc, C, LM, 0, max_decay, lfe); + + if (two_pass != 0 && (badness1 < badness2 || (badness1 == badness2 && (enc.tell_frac()) + intra_bias > tell_intra))) { + enc.Assign(enc_intra_state); + /* Copy intra bits to bit-stream */ + if (intra_bits != null) { + enc_intra_state.write_buffer(intra_bits, 0, intra_buf, (nintra_bytes - nstart_bytes)); + } + System.arraycopy(oldEBands_intra[0], 0, oldEBands[0], 0, m.nbEBands); + System.arraycopy(error_intra[0], 0, error[0], 0, m.nbEBands); + if (C == 2) { + System.arraycopy(oldEBands_intra[1], 0, oldEBands[1], 0, m.nbEBands); + System.arraycopy(error_intra[1], 0, error[1], 0, m.nbEBands); + } + intra = 1; + } + } else { + System.arraycopy(oldEBands_intra[0], 0, oldEBands[0], 0, m.nbEBands); + System.arraycopy(error_intra[0], 0, error[0], 0, m.nbEBands); + if (C == 2) { + System.arraycopy(oldEBands_intra[1], 0, oldEBands[1], 0, m.nbEBands); + System.arraycopy(error_intra[1], 0, error[1], 0, m.nbEBands); + } + } + + if (intra != 0) { + delayedIntra.Val = new_distortion; + } else { + delayedIntra.Val = Inlines.ADD32(Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15(pred_coef[LM], pred_coef[LM]), delayedIntra.Val), + new_distortion); + } + } + + static void quant_fine_energy(CeltMode m, int start, int end, int[][] oldEBands, int[][] error, int[] fine_quant, EntropyCoder enc, int C) { + int i, c; + + /* Encode finer resolution */ + for (i = start; i < end; i++) { + int frac = (1 << fine_quant[i]); + if (fine_quant[i] <= 0) { + continue; + } + c = 0; + do { + int q2; + int offset; + /* Has to be without rounding */ + q2 = (error[c][i] + ((short) (0.5 + (.5f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/) >> (CeltConstants.DB_SHIFT - fine_quant[i]); + if (q2 > frac - 1) { + q2 = frac - 1; + } + if (q2 < 0) { + q2 = 0; + } + enc.enc_bits(q2, fine_quant[i]); + offset = Inlines.SUB16( + (Inlines.SHR32( + Inlines.SHL32(q2, CeltConstants.DB_SHIFT) + ((short) (0.5 + (.5f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/, + fine_quant[i])), + ((short) (0.5 + (.5f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/); + oldEBands[c][i] += offset; + error[c][i] -= offset; + } while (++c < C); + } + } + + static void quant_energy_finalise(CeltMode m, int start, int end, int[][] oldEBands, int[][] error, int[] fine_quant, int[] fine_priority, int bits_left, EntropyCoder enc, int C) { + int i, prio, c; + + /* Use up the remaining bits */ + for (prio = 0; prio < 2; prio++) { + for (i = start; i < end && bits_left >= C; i++) { + if (fine_quant[i] >= CeltConstants.MAX_FINE_BITS || fine_priority[i] != prio) { + continue; + } + + c = 0; + do { + int q2; + int offset; + q2 = error[c][i] < 0 ? 0 : 1; + enc.enc_bits(q2, 1); + offset = Inlines.SHR16((Inlines.SHL16((q2), CeltConstants.DB_SHIFT) - ((short) (0.5 + (.5f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/), fine_quant[i] + 1); + oldEBands[c][i] += offset; + bits_left--; + } while (++c < C); + } + } + } + + static void unquant_coarse_energy(CeltMode m, int start, int end, int[] oldEBands, int intra, EntropyCoder dec, int C, int LM) { + short[] prob_model = CeltTables.e_prob_model[LM][intra]; + int i, c; + int[] prev = {0, 0}; + int coef; + int beta; + int budget; + int tell; + + if (intra != 0) { + coef = 0; + beta = beta_intra; + } else { + beta = beta_coef[LM]; + coef = pred_coef[LM]; + } + + budget = (int) dec.storage * 8; + + /* Decode at a fixed coarse resolution */ + for (i = start; i < end; i++) { + c = 0; + do { + int qi; + int q; + int tmp; + /* It would be better to express this invariant as a + test on C at function entry, but that isn't enough + to make the static analyzer happy. */ + Inlines.OpusAssert(c < 2); + tell = dec.tell(); + if (budget - tell >= 15) { + int pi; + pi = 2 * Inlines.IMIN(i, 20); + qi = Laplace.ec_laplace_decode(dec, + prob_model[pi] << 7, prob_model[pi + 1] << 6); + } else if (budget - tell >= 2) { + qi = dec.dec_icdf(small_energy_icdf, 2); + qi = (qi >> 1) ^ -(qi & 1); + } else if (budget - tell >= 1) { + qi = 0 - dec.dec_bit_logp(1); + } else { + qi = -1; + } + q = (int) Inlines.SHL32(qi, CeltConstants.DB_SHIFT); // opus bug: useless extend32 + + oldEBands[i + c * m.nbEBands] = Inlines.MAX16((0 - ((short) (0.5 + (9.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(9.0f, CeltConstants.DB_SHIFT)*/), oldEBands[i + c * m.nbEBands]); + tmp = Inlines.PSHR32(Inlines.MULT16_16(coef, oldEBands[i + c * m.nbEBands]), 8) + prev[c] + Inlines.SHL32(q, 7); + tmp = Inlines.MAX32(-((int) (0.5 + (28.0f) * (((int) 1) << (CeltConstants.DB_SHIFT + 7))))/*Inlines.QCONST32(28.0f, CeltConstants.DB_SHIFT + 7)*/, tmp); + oldEBands[i + c * m.nbEBands] = (Inlines.PSHR32(tmp, 7)); + prev[c] = prev[c] + Inlines.SHL32(q, 7) - Inlines.MULT16_16(beta, Inlines.PSHR32(q, 8)); + } while (++c < C); + } + } + + static void unquant_fine_energy(CeltMode m, int start, int end, int[] oldEBands, int[] fine_quant, EntropyCoder dec, int C) { + int i, c; + /* Decode finer resolution */ + for (i = start; i < end; i++) { + if (fine_quant[i] <= 0) { + continue; + } + c = 0; + do { + int q2; + int offset; + q2 = dec.dec_bits(fine_quant[i]); + offset = Inlines.SUB16((Inlines.SHR32( + Inlines.SHL32(q2, CeltConstants.DB_SHIFT) + + ((short) (0.5 + (.5f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/, fine_quant[i])), + ((short) (0.5 + (.5f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/); // opus bug: unnecessary extend32 + oldEBands[i + c * m.nbEBands] += offset; + } while (++c < C); + } + } + + static void unquant_energy_finalise(CeltMode m, int start, int end, int[] oldEBands, int[] fine_quant, int[] fine_priority, int bits_left, EntropyCoder dec, int C) { + int i, prio, c; + + /* Use up the remaining bits */ + for (prio = 0; prio < 2; prio++) { + for (i = start; i < end && bits_left >= C; i++) { + if (fine_quant[i] >= CeltConstants.MAX_FINE_BITS || fine_priority[i] != prio) { + continue; + } + c = 0; + do { + int q2; + int offset; + q2 = dec.dec_bits(1); + offset = Inlines.SHR16((Inlines.SHL16((q2), CeltConstants.DB_SHIFT) - ((short) (0.5 + (.5f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/), fine_quant[i] + 1); + oldEBands[i + c * m.nbEBands] += offset; + bits_left--; + } while (++c < C); + } + } + } + + /// + /// non-pointer case + /// + /// + /// + /// + /// + /// + /// + static void amp2Log2(CeltMode m, int effEnd, int end, + int[][] bandE, int[][] bandLogE, int C) { + int c, i; + c = 0; + do { + for (i = 0; i < effEnd; i++) { + bandLogE[c][i] + = (Inlines.celt_log2(Inlines.SHL32(bandE[c][i], 2)) + - Inlines.SHL16((int) CeltTables.eMeans[i], 6)); + } + for (i = effEnd; i < end; i++) { + bandLogE[c][i] = (0 - ((short) (0.5 + (14.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(14.0f, CeltConstants.DB_SHIFT)*/); + } + } while (++c < C); + } + + /// + /// only needed in one place + /// + /// + /// + /// + /// + /// + /// + static void amp2Log2(CeltMode m, int effEnd, int end, + int[] bandE, int[] bandLogE, int bandLogE_ptr, int C) { + int c, i; + c = 0; + do { + for (i = 0; i < effEnd; i++) { + bandLogE[bandLogE_ptr + (c * m.nbEBands) + i] + = (Inlines.celt_log2(Inlines.SHL32(bandE[i + c * m.nbEBands], 2)) + - Inlines.SHL16((int) CeltTables.eMeans[i], 6)); + } + for (i = effEnd; i < end; i++) { + bandLogE[bandLogE_ptr + (c * m.nbEBands) + i] = (0 - ((short) (0.5 + (14.0f) * (((int) 1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(14.0f, CeltConstants.DB_SHIFT)*/); + } + } while (++c < C); + } +} diff --git a/app/src/main/java/org/concentus/QuantizeLTPGains.java b/app/src/main/java/org/concentus/QuantizeLTPGains.java new file mode 100644 index 0000000000..41a53f18b2 --- /dev/null +++ b/app/src/main/java/org/concentus/QuantizeLTPGains.java @@ -0,0 +1,141 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class QuantizeLTPGains { + + static void silk_quant_LTP_gains( + short[] B_Q14, /* I/O (un)quantized LTP gains [MAX_NB_SUBFR * LTP_ORDER] */ + byte[] cbk_index, /* O Codebook Index [MAX_NB_SUBFR] */ + BoxedValueByte periodicity_index, /* O Periodicity Index */ + BoxedValueInt sum_log_gain_Q7, /* I/O Cumulative max prediction gain */ + int[] W_Q18, /* I Error Weights in Q18 [MAX_NB_SUBFR * LTP_ORDER * LTP_ORDER] */ + int mu_Q9, /* I Mu value (R/D tradeoff) */ + int lowComplexity, /* I Flag for low complexity */ + int nb_subfr /* I number of subframes */ + ) { + int j, k, cbk_size; + byte[] temp_idx = new byte[SilkConstants.MAX_NB_SUBFR]; + short[] cl_ptr_Q5; + byte[][] cbk_ptr_Q7; + short[] cbk_gain_ptr_Q7; + int b_Q14_ptr; + int W_Q18_ptr; + int rate_dist_Q14_subfr, rate_dist_Q14, min_rate_dist_Q14; + int sum_log_gain_tmp_Q7, best_sum_log_gain_Q7, max_gain_Q7, gain_Q7; + + /** + * ************************************************ + */ + /* iterate over different codebooks with different */ + /* rates/distortions, and choose best */ + /** + * ************************************************ + */ + min_rate_dist_Q14 = Integer.MAX_VALUE; + best_sum_log_gain_Q7 = 0; + for (k = 0; k < 3; k++) { + /* Safety margin for pitch gain control, to take into account factors + such as state rescaling/rewhitening. */ + int gain_safety = ((int) ((0.4f) * ((long) 1 << (7)) + 0.5))/*Inlines.SILK_CONST(0.4f, 7)*/; + + cl_ptr_Q5 = SilkTables.silk_LTP_gain_BITS_Q5_ptrs[k]; + cbk_ptr_Q7 = SilkTables.silk_LTP_vq_ptrs_Q7[k]; + cbk_gain_ptr_Q7 = SilkTables.silk_LTP_vq_gain_ptrs_Q7[k]; + cbk_size = SilkTables.silk_LTP_vq_sizes[k]; + + /* Set up pointer to first subframe */ + W_Q18_ptr = 0; + b_Q14_ptr = 0; + + rate_dist_Q14 = 0; + sum_log_gain_tmp_Q7 = sum_log_gain_Q7.Val; + for (j = 0; j < nb_subfr; j++) { + max_gain_Q7 = Inlines.silk_log2lin((((int) ((TuningParameters.MAX_SUM_LOG_GAIN_DB / 6.0f) * ((long) 1 << (7)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.MAX_SUM_LOG_GAIN_DB / 6.0f, 7)*/ - sum_log_gain_tmp_Q7) + + ((int) ((7) * ((long) 1 << (7)) + 0.5))/*Inlines.SILK_CONST(7, 7)*/) - gain_safety; + + BoxedValueByte temp_idx_box = new BoxedValueByte(temp_idx[j]); + BoxedValueInt rate_dist_Q14_subfr_box = new BoxedValueInt(0); + BoxedValueInt gain_Q7_box = new BoxedValueInt(0); + VQ_WMat_EC.silk_VQ_WMat_EC( + temp_idx_box, /* O index of best codebook vector */ + rate_dist_Q14_subfr_box, /* O best weighted quantization error + mu * rate */ + gain_Q7_box, /* O sum of absolute LTP coefficients */ + B_Q14, + b_Q14_ptr, /* I input vector to be quantized */ + W_Q18, + W_Q18_ptr, /* I weighting matrix */ + cbk_ptr_Q7, /* I codebook */ + cbk_gain_ptr_Q7, /* I codebook effective gains */ + cl_ptr_Q5, /* I code length for each codebook vector */ + mu_Q9, /* I tradeoff between weighted error and rate */ + max_gain_Q7, /* I maximum sum of absolute LTP coefficients */ + cbk_size /* I number of vectors in codebook */ + ); + rate_dist_Q14_subfr = rate_dist_Q14_subfr_box.Val; + gain_Q7 = gain_Q7_box.Val; + temp_idx[j] = temp_idx_box.Val; + + rate_dist_Q14 = Inlines.silk_ADD_POS_SAT32(rate_dist_Q14, rate_dist_Q14_subfr); + sum_log_gain_tmp_Q7 = Inlines.silk_max(0, sum_log_gain_tmp_Q7 + + Inlines.silk_lin2log(gain_safety + gain_Q7) - ((int) ((7) * ((long) 1 << (7)) + 0.5))/*Inlines.SILK_CONST(7, 7)*/); + + b_Q14_ptr += SilkConstants.LTP_ORDER; + W_Q18_ptr += SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER; + } + + /* Avoid never finding a codebook */ + rate_dist_Q14 = Inlines.silk_min(Integer.MAX_VALUE - 1, rate_dist_Q14); + + if (rate_dist_Q14 < min_rate_dist_Q14) { + min_rate_dist_Q14 = rate_dist_Q14; + periodicity_index.Val = (byte) k; + System.arraycopy(temp_idx, 0, cbk_index, 0, nb_subfr); + best_sum_log_gain_Q7 = sum_log_gain_tmp_Q7; + } + + /* Break early in low-complexity mode if rate distortion is below threshold */ + if (lowComplexity != 0 && (rate_dist_Q14 < SilkTables.silk_LTP_gain_middle_avg_RD_Q14)) { + break; + } + } + + cbk_ptr_Q7 = SilkTables.silk_LTP_vq_ptrs_Q7[periodicity_index.Val]; + for (j = 0; j < nb_subfr; j++) { + for (k = 0; k < SilkConstants.LTP_ORDER; k++) { + B_Q14[j * SilkConstants.LTP_ORDER + k] = (short) (Inlines.silk_LSHIFT(cbk_ptr_Q7[cbk_index[j]][k], 7)); + } + } + + sum_log_gain_Q7.Val = best_sum_log_gain_Q7; + } +} diff --git a/app/src/main/java/org/concentus/Rate.java b/app/src/main/java/org/concentus/Rate.java new file mode 100644 index 0000000000..41b5d7a296 --- /dev/null +++ b/app/src/main/java/org/concentus/Rate.java @@ -0,0 +1,460 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class Rate { + + private static final byte[] LOG2_FRAC_TABLE = { + 0, + 8, 13, + 16, 19, 21, 23, + 24, 26, 27, 28, 29, 30, 31, 32, + 32, 33, 34, 34, 35, 36, 36, 37, 37 + }; + + private static final int ALLOC_STEPS = 6; + + static int get_pulses(int i) { + return i < 8 ? i : (8 + (i & 7)) << ((i >> 3) - 1); + } + + static int bits2pulses(CeltMode m, int band, int LM, int bits) { + int i; + int lo, hi; + + LM++; + short[] cache = m.cache.bits; + int cache_ptr = m.cache.index[LM * m.nbEBands + band]; + + lo = 0; + hi = cache[cache_ptr]; + bits--; + for (i = 0; i < CeltConstants.LOG_MAX_PSEUDO; i++) { + int mid = (lo + hi + 1) >> 1; + /* OPT: Make sure this is implemented with a conditional move */ + if ((int) cache[cache_ptr + mid] >= bits) { + hi = mid; + } else { + lo = mid; + } + } + if (bits - (lo == 0 ? -1 : (int) cache[cache_ptr + lo]) <= (int) cache[cache_ptr + hi] - bits) { + return lo; + } else { + return hi; + } + } + + static int pulses2bits(CeltMode m, int band, int LM, int pulses) { + LM++; + return pulses == 0 ? 0 : m.cache.bits[m.cache.index[LM * m.nbEBands + band] + pulses] + 1; + } + + static int interp_bits2pulses(CeltMode m, int start, int end, int skip_start, + int[] bits1, int[] bits2, int[] thresh, int[] cap, int total, BoxedValueInt _balance, + int skip_rsv, BoxedValueInt intensity, int intensity_rsv, BoxedValueInt dual_stereo, int dual_stereo_rsv, int[] bits, + int[] ebits, int[] fine_priority, int C, int LM, EntropyCoder ec, int encode, int prev, int signalBandwidth) { + int psum; + int lo, hi; + int i, j; + int logM; + int stereo; + int codedBands = -1; + int alloc_floor; + int left, percoeff; + int done; + int balance; + + alloc_floor = C << EntropyCoder.BITRES; + stereo = C > 1 ? 1 : 0; + + logM = LM << EntropyCoder.BITRES; + lo = 0; + hi = 1 << ALLOC_STEPS; + for (i = 0; i < ALLOC_STEPS; i++) { + int mid = (lo + hi) >> 1; + psum = 0; + done = 0; + for (j = end; j-- > start;) { + int tmp = bits1[j] + (mid * (int) bits2[j] >> ALLOC_STEPS); + if (tmp >= thresh[j] || done != 0) { + done = 1; + /* Don't allocate more than we can actually use */ + psum += Inlines.IMIN(tmp, cap[j]); + } else if (tmp >= alloc_floor) { + psum += alloc_floor; + } + } + if (psum > total) { + hi = mid; + } else { + lo = mid; + } + } + psum = 0; + /*printf ("interp bisection gave %d\n", lo);*/ + done = 0; + for (j = end; j-- > start;) { + int tmp = bits1[j] + (lo * bits2[j] >> ALLOC_STEPS); + if (tmp < thresh[j] && done == 0) { + if (tmp >= alloc_floor) { + tmp = alloc_floor; + } else { + tmp = 0; + } + } else { + done = 1; + } + + /* Don't allocate more than we can actually use */ + tmp = Inlines.IMIN(tmp, cap[j]); + bits[j] = tmp; + psum += tmp; + } + + /* Decide which bands to skip, working backwards from the end. */ + for (codedBands = end;; codedBands--) { + int band_width; + int band_bits; + int rem; + j = codedBands - 1; + /* Never skip the first band, nor a band that has been boosted by + dynalloc. + In the first case, we'd be coding a bit to signal we're going to waste + all the other bits. + In the second case, we'd be coding a bit to redistribute all the bits + we just signaled should be cocentrated in this band. */ + if (j <= skip_start) { + /* Give the bit we reserved to end skipping back. */ + total += skip_rsv; + break; + } + + /*Figure out how many left-over bits we would be adding to this band. + This can include bits we've stolen back from higher, skipped bands.*/ + left = total - psum; + percoeff = Inlines.celt_udiv(left, m.eBands[codedBands] - m.eBands[start]); + left -= (m.eBands[codedBands] - m.eBands[start]) * percoeff; + rem = Inlines.IMAX(left - (m.eBands[j] - m.eBands[start]), 0); + band_width = m.eBands[codedBands] - m.eBands[j]; + band_bits = (int) (bits[j] + percoeff * band_width + rem); + /*Only code a skip decision if we're above the threshold for this band. + Otherwise it is force-skipped. + This ensures that we have enough bits to code the skip flag.*/ + if (band_bits >= Inlines.IMAX(thresh[j], alloc_floor + (1 << EntropyCoder.BITRES))) { + if (encode != 0) { + /*This if() block is the only part of the allocation function that + is not a mandatory part of the bitstream: any bands we choose to + skip here must be explicitly signaled.*/ + /*Choose a threshold with some hysteresis to keep bands from + fluctuating in and out.*/ + if (codedBands <= start + 2 || (band_bits > ((j < prev ? 7 : 9) * band_width << LM << EntropyCoder.BITRES) >> 4 && j <= signalBandwidth)) { + ec.enc_bit_logp(1, 1); + break; + } + ec.enc_bit_logp(0, 1); + } else if (ec.dec_bit_logp(1) != 0) { + break; + } + /*We used a bit to skip this band.*/ + psum += 1 << EntropyCoder.BITRES; + band_bits -= 1 << EntropyCoder.BITRES; + } + /*Reclaim the bits originally allocated to this band.*/ + psum -= bits[j] + intensity_rsv; + if (intensity_rsv > 0) { + intensity_rsv = LOG2_FRAC_TABLE[j - start]; + } + psum += intensity_rsv; + if (band_bits >= alloc_floor) { + /*If we have enough for a fine energy bit per channel, use it.*/ + psum += alloc_floor; + bits[j] = alloc_floor; + } else { + /*Otherwise this band gets nothing at all.*/ + bits[j] = 0; + } + } + + Inlines.OpusAssert(codedBands > start); + /* Code the intensity and dual stereo parameters. */ + if (intensity_rsv > 0) { + if (encode != 0) { + intensity.Val = Inlines.IMIN(intensity.Val, codedBands); + ec.enc_uint((intensity.Val - start), (codedBands + 1 - start)); + } else { + intensity.Val = start + (int) ec.dec_uint(codedBands + 1 - start); + } + } else { + intensity.Val = 0; + } + + if (intensity.Val <= start) { + total += dual_stereo_rsv; + dual_stereo_rsv = 0; + } + if (dual_stereo_rsv > 0) { + if (encode != 0) { + ec.enc_bit_logp(dual_stereo.Val, 1); + } else { + dual_stereo.Val = ec.dec_bit_logp(1); + } + } else { + dual_stereo.Val = 0; + } + + /* Allocate the remaining bits */ + left = total - psum; + percoeff = Inlines.celt_udiv(left, m.eBands[codedBands] - m.eBands[start]); + left -= (m.eBands[codedBands] - m.eBands[start]) * percoeff; + for (j = start; j < codedBands; j++) { + bits[j] += ((int) percoeff * (m.eBands[j + 1] - m.eBands[j])); + } + for (j = start; j < codedBands; j++) { + int tmp = (int) Inlines.IMIN(left, m.eBands[j + 1] - m.eBands[j]); + bits[j] += tmp; + left -= tmp; + } + /*for (j=0;j= 0); + N0 = m.eBands[j + 1] - m.eBands[j]; + N = N0 << LM; + bit = (int) bits[j] + balance; + + if (N > 1) { + excess = Inlines.MAX32(bit - cap[j], 0); + bits[j] = bit - excess; + + /* Compensate for the extra DoF in stereo */ + den = (C * N + ((C == 2 && N > 2 && (dual_stereo.Val == 0) && j < intensity.Val) ? 1 : 0)); + + NClogN = den * (m.logN[j] + logM); + + /* Offset for the number of fine bits by log2(N)/2 + FINE_OFFSET + compared to their "fair share" of total/N */ + offset = (NClogN >> 1) - den * CeltConstants.FINE_OFFSET; + + /* N=2 is the only point that doesn't match the curve */ + if (N == 2) { + offset += den << EntropyCoder.BITRES >> 2; + } + + /* Changing the offset for allocating the second and third + fine energy bit */ + if (bits[j] + offset < den * 2 << EntropyCoder.BITRES) { + offset += NClogN >> 2; + } else if (bits[j] + offset < den * 3 << EntropyCoder.BITRES) { + offset += NClogN >> 3; + } + + /* Divide with rounding */ + ebits[j] = Inlines.IMAX(0, (bits[j] + offset + (den << (EntropyCoder.BITRES - 1)))); + ebits[j] = Inlines.celt_udiv(ebits[j], den) >> EntropyCoder.BITRES; + + /* Make sure not to bust */ + if (C * ebits[j] > (bits[j] >> EntropyCoder.BITRES)) { + ebits[j] = bits[j] >> stereo >> EntropyCoder.BITRES; + } + + /* More than that is useless because that's about as far as PVQ can go */ + ebits[j] = Inlines.IMIN(ebits[j], CeltConstants.MAX_FINE_BITS); + + /* If we rounded down or capped this band, make it a candidate for the + final fine energy pass */ + fine_priority[j] = (ebits[j] * (den << EntropyCoder.BITRES) >= bits[j] + offset) ? 1 : 0; + + /* Remove the allocated fine bits; the rest are assigned to PVQ */ + bits[j] -= C * ebits[j] << EntropyCoder.BITRES; + + } else { + /* For N=1, all bits go to fine energy except for a single sign bit */ + excess = Inlines.MAX32(0, bit - (C << EntropyCoder.BITRES)); + bits[j] = bit - excess; + ebits[j] = 0; + fine_priority[j] = 1; + } + + /* Fine energy can't take advantage of the re-balancing in + quant_all_bands(). + Instead, do the re-balancing here.*/ + if (excess > 0) { + int extra_fine; + int extra_bits; + extra_fine = Inlines.IMIN(excess >> (stereo + EntropyCoder.BITRES), CeltConstants.MAX_FINE_BITS - ebits[j]); + ebits[j] += extra_fine; + extra_bits = extra_fine * C << EntropyCoder.BITRES; + fine_priority[j] = (extra_bits >= excess - balance) ? 1 : 0; + excess -= extra_bits; + } + balance = excess; + + Inlines.OpusAssert(bits[j] >= 0); + Inlines.OpusAssert(ebits[j] >= 0); + } + /* Save any remaining bits over the cap for the rebalancing in + quant_all_bands(). */ + _balance.Val = balance; + + /* The skipped bands use all their bits for fine energy. */ + for (; j < end; j++) { + ebits[j] = bits[j] >> stereo >> EntropyCoder.BITRES; + Inlines.OpusAssert(C * ebits[j] << EntropyCoder.BITRES == bits[j]); + bits[j] = 0; + fine_priority[j] = (ebits[j] < 1) ? 1 : 0; + } + + return codedBands; + } + + static int compute_allocation(CeltMode m, int start, int end, int[] offsets, int[] cap, int alloc_trim, BoxedValueInt intensity, BoxedValueInt dual_stereo, + int total, BoxedValueInt balance, int[] pulses, int[] ebits, int[] fine_priority, int C, int LM, EntropyCoder ec, int encode, int prev, int signalBandwidth) { + int lo, hi, len, j; + int codedBands; + int skip_start; + int skip_rsv; + int intensity_rsv; + int dual_stereo_rsv; + + total = Inlines.IMAX(total, 0); + len = m.nbEBands; + skip_start = start; + /* Reserve a bit to signal the end of manually skipped bands. */ + skip_rsv = total >= 1 << EntropyCoder.BITRES ? 1 << EntropyCoder.BITRES : 0; + total -= skip_rsv; + /* Reserve bits for the intensity and dual stereo parameters. */ + intensity_rsv = dual_stereo_rsv = 0; + if (C == 2) { + intensity_rsv = LOG2_FRAC_TABLE[end - start]; + if (intensity_rsv > total) { + intensity_rsv = 0; + } else { + total -= intensity_rsv; + dual_stereo_rsv = total >= 1 << EntropyCoder.BITRES ? 1 << EntropyCoder.BITRES : 0; + total -= dual_stereo_rsv; + } + } + + int[] bits1 = new int[len]; + int[] bits2 = new int[len]; + int[] thresh = new int[len]; + int[] trim_offset = new int[len]; + + for (j = start; j < end; j++) { + /* Below this threshold, we're sure not to allocate any PVQ bits */ + thresh[j] = Inlines.IMAX((C) << EntropyCoder.BITRES, (3 * (m.eBands[j + 1] - m.eBands[j]) << LM << EntropyCoder.BITRES) >> 4); + /* Tilt of the allocation curve */ + trim_offset[j] = C * (m.eBands[j + 1] - m.eBands[j]) * (alloc_trim - 5 - LM) * (end - j - 1) + * (1 << (LM + EntropyCoder.BITRES)) >> 6; + /* Giving less resolution to single-coefficient bands because they get + more benefit from having one coarse value per coefficient*/ + if ((m.eBands[j + 1] - m.eBands[j]) << LM == 1) { + trim_offset[j] -= C << EntropyCoder.BITRES; + } + } + lo = 1; + hi = m.nbAllocVectors - 1; + do { + int done = 0; + int psum = 0; + int mid = (lo + hi) >> 1; + for (j = end; j-- > start;) { + int bitsj; + int N = m.eBands[j + 1] - m.eBands[j]; + bitsj = C * N * m.allocVectors[mid * len + j] << LM >> 2; + + if (bitsj > 0) { + bitsj = Inlines.IMAX(0, bitsj + trim_offset[j]); + } + + bitsj += offsets[j]; + + if (bitsj >= thresh[j] || done != 0) { + done = 1; + /* Don't allocate more than we can actually use */ + psum += Inlines.IMIN(bitsj, cap[j]); + } else if (bitsj >= C << EntropyCoder.BITRES) { + psum += C << EntropyCoder.BITRES; + } + } + if (psum > total) { + hi = mid - 1; + } else { + lo = mid + 1; + } + /*printf ("lo = %d, hi = %d\n", lo, hi);*/ + } while (lo <= hi); + + hi = lo--; + /*printf ("interp between %d and %d\n", lo, hi);*/ + + for (j = start; j < end; j++) { + int bits1j, bits2j; + int N = m.eBands[j + 1] - m.eBands[j]; + bits1j = C * N * m.allocVectors[lo * len + j] << LM >> 2; + bits2j = hi >= m.nbAllocVectors + ? cap[j] : C * N * m.allocVectors[hi * len + j] << LM >> 2; + if (bits1j > 0) { + bits1j = Inlines.IMAX(0, bits1j + trim_offset[j]); + } + if (bits2j > 0) { + bits2j = Inlines.IMAX(0, bits2j + trim_offset[j]); + } + if (lo > 0) { + bits1j += offsets[j]; + } + bits2j += offsets[j]; + if (offsets[j] > 0) { + skip_start = j; + } + bits2j = Inlines.IMAX(0, bits2j - bits1j); + bits1[j] = bits1j; + bits2[j] = bits2j; + } + + codedBands = interp_bits2pulses(m, start, end, skip_start, bits1, bits2, thresh, cap, + total, balance, skip_rsv, intensity, intensity_rsv, dual_stereo, dual_stereo_rsv, + pulses, ebits, fine_priority, C, LM, ec, encode, prev, signalBandwidth); + + return codedBands; + } +} diff --git a/app/src/main/java/org/concentus/RegularizeCorrelations.java b/app/src/main/java/org/concentus/RegularizeCorrelations.java new file mode 100644 index 0000000000..b68d02b3f4 --- /dev/null +++ b/app/src/main/java/org/concentus/RegularizeCorrelations.java @@ -0,0 +1,51 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class RegularizeCorrelations { + + /* Add noise to matrix diagonal */ + static void silk_regularize_correlations( + int[] XX, /* I/O Correlation matrices */ + int XX_ptr, + int[] xx, /* I/O Correlation values */ + int xx_ptr, + int noise, /* I Noise to add */ + int D /* I Dimension of XX */ + ) { + int i; + for (i = 0; i < D; i++) { + Inlines.MatrixSet(XX, XX_ptr, i, i, D, Inlines.silk_ADD32(Inlines.MatrixGet(XX, XX_ptr, i, i, D), noise)); + } + xx[xx_ptr] += noise; + } +} diff --git a/app/src/main/java/org/concentus/Resampler.java b/app/src/main/java/org/concentus/Resampler.java new file mode 100644 index 0000000000..3bb9602c2e --- /dev/null +++ b/app/src/main/java/org/concentus/Resampler.java @@ -0,0 +1,683 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/* + * Matrix of resampling methods used: + * Fs_out (kHz) + * 8 12 16 24 48 + * + * 8 C UF U UF UF + * 12 AF C UF U UF + * Fs_in (kHz) 16 D AF C UF UF + * 24 AF D AF C U + * 48 AF AF AF D C + * + * C . Copy (no resampling) + * D . Allpass-based 2x downsampling + * U . Allpass-based 2x upsampling + * UF . Allpass-based 2x upsampling followed by FIR interpolation + * AF . AR2 filter followed by FIR interpolation + */ +class Resampler { + + private static final int USE_silk_resampler_copy = 0; + private static final int USE_silk_resampler_private_up2_HQ_wrapper = 1; + private static final int USE_silk_resampler_private_IIR_FIR = 2; + private static final int USE_silk_resampler_private_down_FIR = 3; + + private static final int ORDER_FIR = 4; + + /// + /// Simple way to make [8000, 12000, 16000, 24000, 48000] to [0, 1, 2, 3, 4] + /// + /// + /// + private static int rateID(int R) { + return (((((R) >> 12) - ((R > 16000) ? 1 : 0)) >> ((R > 24000) ? 1 : 0)) - 1); + } + + /// + /// Initialize/reset the resampler state for a given pair of input/output sampling rates + /// + /// I/O Resampler state + /// I Input sampling rate (Hz) + /// I Output sampling rate (Hz) + /// I If 1: encoder; if 0: decoder + /// + static int silk_resampler_init( + SilkResamplerState S, + int Fs_Hz_in, + int Fs_Hz_out, + int forEnc) { + int up2x; + + /* Clear state */ + S.Reset(); + + /* Input checking */ + if (forEnc != 0) { + if ((Fs_Hz_in != 8000 && Fs_Hz_in != 12000 && Fs_Hz_in != 16000 && Fs_Hz_in != 24000 && Fs_Hz_in != 48000) + || (Fs_Hz_out != 8000 && Fs_Hz_out != 12000 && Fs_Hz_out != 16000)) { + Inlines.OpusAssert(false); + return -1; + } + S.inputDelay = SilkTables.delay_matrix_enc[rateID(Fs_Hz_in)][rateID(Fs_Hz_out)]; + } else { + if ((Fs_Hz_in != 8000 && Fs_Hz_in != 12000 && Fs_Hz_in != 16000) + || (Fs_Hz_out != 8000 && Fs_Hz_out != 12000 && Fs_Hz_out != 16000 && Fs_Hz_out != 24000 && Fs_Hz_out != 48000)) { + Inlines.OpusAssert(false); + return -1; + } + S.inputDelay = SilkTables.delay_matrix_dec[rateID(Fs_Hz_in)][rateID(Fs_Hz_out)]; + } + + S.Fs_in_kHz = Inlines.silk_DIV32_16(Fs_Hz_in, 1000); + S.Fs_out_kHz = Inlines.silk_DIV32_16(Fs_Hz_out, 1000); + + /* Number of samples processed per batch */ + S.batchSize = S.Fs_in_kHz * SilkConstants.RESAMPLER_MAX_BATCH_SIZE_MS; + + /* Find resampler with the right sampling ratio */ + up2x = 0; + if (Fs_Hz_out > Fs_Hz_in) { + /* Upsample */ + if (Fs_Hz_out == Inlines.silk_MUL(Fs_Hz_in, 2)) { + /* Fs_out : Fs_in = 2 : 1 */ + /* Special case: directly use 2x upsampler */ + S.resampler_function = USE_silk_resampler_private_up2_HQ_wrapper; + } else { + /* Default resampler */ + S.resampler_function = USE_silk_resampler_private_IIR_FIR; + up2x = 1; + } + } else if (Fs_Hz_out < Fs_Hz_in) { + /* Downsample */ + S.resampler_function = USE_silk_resampler_private_down_FIR; + if (Inlines.silk_MUL(Fs_Hz_out, 4) == Inlines.silk_MUL(Fs_Hz_in, 3)) { + /* Fs_out : Fs_in = 3 : 4 */ + S.FIR_Fracs = 3; + S.FIR_Order = SilkConstants.RESAMPLER_DOWN_ORDER_FIR0; + S.Coefs = SilkTables.silk_Resampler_3_4_COEFS; + } else if (Inlines.silk_MUL(Fs_Hz_out, 3) == Inlines.silk_MUL(Fs_Hz_in, 2)) { + /* Fs_out : Fs_in = 2 : 3 */ + S.FIR_Fracs = 2; + S.FIR_Order = SilkConstants.RESAMPLER_DOWN_ORDER_FIR0; + S.Coefs = SilkTables.silk_Resampler_2_3_COEFS; + } else if (Inlines.silk_MUL(Fs_Hz_out, 2) == Fs_Hz_in) { + /* Fs_out : Fs_in = 1 : 2 */ + S.FIR_Fracs = 1; + S.FIR_Order = SilkConstants.RESAMPLER_DOWN_ORDER_FIR1; + S.Coefs = SilkTables.silk_Resampler_1_2_COEFS; + } else if (Inlines.silk_MUL(Fs_Hz_out, 3) == Fs_Hz_in) { + /* Fs_out : Fs_in = 1 : 3 */ + S.FIR_Fracs = 1; + S.FIR_Order = SilkConstants.RESAMPLER_DOWN_ORDER_FIR2; + S.Coefs = SilkTables.silk_Resampler_1_3_COEFS; + } else if (Inlines.silk_MUL(Fs_Hz_out, 4) == Fs_Hz_in) { + /* Fs_out : Fs_in = 1 : 4 */ + S.FIR_Fracs = 1; + S.FIR_Order = SilkConstants.RESAMPLER_DOWN_ORDER_FIR2; + S.Coefs = SilkTables.silk_Resampler_1_4_COEFS; + } else if (Inlines.silk_MUL(Fs_Hz_out, 6) == Fs_Hz_in) { + /* Fs_out : Fs_in = 1 : 6 */ + S.FIR_Fracs = 1; + S.FIR_Order = SilkConstants.RESAMPLER_DOWN_ORDER_FIR2; + S.Coefs = SilkTables.silk_Resampler_1_6_COEFS; + } else { + /* None available */ + Inlines.OpusAssert(false); + return -1; + } + } else { + /* Input and output sampling rates are equal: copy */ + S.resampler_function = USE_silk_resampler_copy; + } + + /* Ratio of input/output samples */ + S.invRatio_Q16 = Inlines.silk_LSHIFT32(Inlines.silk_DIV32(Inlines.silk_LSHIFT32(Fs_Hz_in, 14 + up2x), Fs_Hz_out), 2); + + /* Make sure the ratio is rounded up */ + while (Inlines.silk_SMULWW(S.invRatio_Q16, Fs_Hz_out) < Inlines.silk_LSHIFT32(Fs_Hz_in, up2x)) { + S.invRatio_Q16++; + } + + return 0; + } + + /// + /// Resampler: convert from one sampling rate to another + /// Input and output sampling rate are at most 48000 Hz + /// + /// I/O Resampler state + /// O Output signal + /// I Input signal + /// I Number of input samples + /// + static int silk_resampler( + SilkResamplerState S, + short[] output, + int output_ptr, + short[] input, + int input_ptr, + int inLen) { + int nSamples; + + /* Need at least 1 ms of input data */ + Inlines.OpusAssert(inLen >= S.Fs_in_kHz); + /* Delay can't exceed the 1 ms of buffering */ + Inlines.OpusAssert(S.inputDelay <= S.Fs_in_kHz); + + nSamples = S.Fs_in_kHz - S.inputDelay; + + short[] delayBufPtr = S.delayBuf; + + /* Copy to delay buffer */ + System.arraycopy(input, input_ptr, delayBufPtr, S.inputDelay, nSamples); + + switch (S.resampler_function) { + case USE_silk_resampler_private_up2_HQ_wrapper: + silk_resampler_private_up2_HQ(S.sIIR, output, output_ptr, delayBufPtr, 0, S.Fs_in_kHz); + silk_resampler_private_up2_HQ(S.sIIR, output, output_ptr + S.Fs_out_kHz, input, input_ptr + nSamples, inLen - S.Fs_in_kHz); + break; + case USE_silk_resampler_private_IIR_FIR: + silk_resampler_private_IIR_FIR(S, output, output_ptr, delayBufPtr, 0, S.Fs_in_kHz); + silk_resampler_private_IIR_FIR(S, output, output_ptr + S.Fs_out_kHz, input, input_ptr + nSamples, inLen - S.Fs_in_kHz); + break; + case USE_silk_resampler_private_down_FIR: + silk_resampler_private_down_FIR(S, output, output_ptr, delayBufPtr, 0, S.Fs_in_kHz); + silk_resampler_private_down_FIR(S, output, output_ptr + S.Fs_out_kHz, input, input_ptr + nSamples, inLen - S.Fs_in_kHz); + break; + default: + System.arraycopy(delayBufPtr, 0, output, output_ptr, S.Fs_in_kHz); + System.arraycopy(input, input_ptr + nSamples, output, output_ptr + S.Fs_out_kHz, inLen - S.Fs_in_kHz); + break; + } + + /* Copy to delay buffer */ + System.arraycopy(input, input_ptr + inLen - S.inputDelay, delayBufPtr, 0, S.inputDelay); + + return SilkError.SILK_NO_ERROR; + } + + /// + /// Downsample by a factor 2 + /// + /// I/O State vector [ 2 ] + /// O Output signal [ floor(len/2) ] + /// I Input signal [ len ] + /// I Number of input samples + static void silk_resampler_down2( + int[] S, + short[] output, + short[] input, + int inLen) { + int k, len2 = Inlines.silk_RSHIFT32(inLen, 1); + int in32, out32, Y, X; + + Inlines.OpusAssert(SilkTables.silk_resampler_down2_0 > 0); + Inlines.OpusAssert(SilkTables.silk_resampler_down2_1 < 0); + + /* Internal variables and state are in Q10 format */ + for (k = 0; k < len2; k++) { + /* Convert to Q10 */ + in32 = Inlines.silk_LSHIFT((int) input[2 * k], 10); + + /* All-pass section for even input sample */ + Y = Inlines.silk_SUB32(in32, S[0]); + X = Inlines.silk_SMLAWB(Y, Y, SilkTables.silk_resampler_down2_1); + out32 = Inlines.silk_ADD32(S[0], X); + S[0] = Inlines.silk_ADD32(in32, X); + + /* Convert to Q10 */ + in32 = Inlines.silk_LSHIFT((int) input[2 * k + 1], 10); + + /* All-pass section for odd input sample, and add to output of previous section */ + Y = Inlines.silk_SUB32(in32, S[1]); + X = Inlines.silk_SMULWB(Y, SilkTables.silk_resampler_down2_0); + out32 = Inlines.silk_ADD32(out32, S[1]); + out32 = Inlines.silk_ADD32(out32, X); + S[1] = Inlines.silk_ADD32(in32, X); + + /* Add, convert back to int16 and store to output */ + output[k] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(out32, 11)); + } + } + + /// + /// Downsample by a factor 2/3, low quality + /// + /// I/O State vector [ 6 ] + /// O Output signal [ floor(2*inLen/3) ] + /// I Input signal [ inLen ] + /// I Number of input samples + static void silk_resampler_down2_3( + int[] S, + short[] output, + short[] input, + int inLen) { + int nSamplesIn, counter, res_Q6; + int[] buf = new int[SilkConstants.RESAMPLER_MAX_BATCH_SIZE_IN + ORDER_FIR]; + int buf_ptr; + int input_ptr = 0; + int output_ptr = 0; + + /* Copy buffered samples to start of buffer */ + System.arraycopy(S, 0, buf, 0, ORDER_FIR); + + /* Iterate over blocks of frameSizeIn input samples */ + while (true) { + nSamplesIn = Inlines.silk_min(inLen, SilkConstants.RESAMPLER_MAX_BATCH_SIZE_IN); + + /* Second-order AR filter (output in Q8) */ + silk_resampler_private_AR2(S, ORDER_FIR, buf, ORDER_FIR, input, input_ptr, + SilkTables.silk_Resampler_2_3_COEFS_LQ, nSamplesIn); + + /* Interpolate filtered signal */ + buf_ptr = 0; + counter = nSamplesIn; + while (counter > 2) { + /* Inner product */ + res_Q6 = Inlines.silk_SMULWB(buf[buf_ptr], SilkTables.silk_Resampler_2_3_COEFS_LQ[2]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 1], SilkTables.silk_Resampler_2_3_COEFS_LQ[3]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 2], SilkTables.silk_Resampler_2_3_COEFS_LQ[5]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 3], SilkTables.silk_Resampler_2_3_COEFS_LQ[4]); + + /* Scale down, saturate and store in output array */ + output[output_ptr++] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6)); + + res_Q6 = Inlines.silk_SMULWB(buf[buf_ptr + 1], SilkTables.silk_Resampler_2_3_COEFS_LQ[4]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 2], SilkTables.silk_Resampler_2_3_COEFS_LQ[5]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 3], SilkTables.silk_Resampler_2_3_COEFS_LQ[3]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 4], SilkTables.silk_Resampler_2_3_COEFS_LQ[2]); + + /* Scale down, saturate and store in output array */ + output[output_ptr++] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6)); + + buf_ptr += 3; + counter -= 3; + } + + input_ptr += nSamplesIn; + inLen -= nSamplesIn; + + if (inLen > 0) { + /* More iterations to do; copy last part of filtered signal to beginning of buffer */ + System.arraycopy(buf, nSamplesIn, buf, 0, ORDER_FIR); + } else { + break; + } + } + + /* Copy last part of filtered signal to the state for the next call */ + System.arraycopy(buf, nSamplesIn, S, 0, ORDER_FIR); + } + + /// + /// Second order AR filter with single delay elements + /// + /// I/O State vector [ 2 ] + /// O Output signal + /// I Input signal + /// I AR coefficients, Q14 + /// I Signal length + static void silk_resampler_private_AR2( + int[] S, + int S_ptr, + int[] out_Q8, + int out_Q8_ptr, + short[] input, + int input_ptr, + short[] A_Q14, + int len) { + int k, out32; + + for (k = 0; k < len; k++) { + out32 = Inlines.silk_ADD_LSHIFT32(S[S_ptr], (int) input[input_ptr + k], 8); + out_Q8[out_Q8_ptr + k] = out32; + out32 = Inlines.silk_LSHIFT(out32, 2); + S[S_ptr] = Inlines.silk_SMLAWB(S[S_ptr + 1], out32, A_Q14[0]); + S[S_ptr + 1] = Inlines.silk_SMULWB(out32, A_Q14[1]); + } + } + + static int silk_resampler_private_down_FIR_INTERPOL( + short[] output, + int output_ptr, + int[] buf, + short[] FIR_Coefs, + int FIR_Coefs_ptr, + int FIR_Order, + int FIR_Fracs, + int max_index_Q16, + int index_increment_Q16) { + int index_Q16, res_Q6; + int buf_ptr; + int interpol_ind; + int interpol_ptr; + + switch (FIR_Order) { + case SilkConstants.RESAMPLER_DOWN_ORDER_FIR0: + for (index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16) { + /* Integer part gives pointer to buffered input */ + buf_ptr = Inlines.silk_RSHIFT(index_Q16, 16); + + /* Fractional part gives interpolation coefficients */ + interpol_ind = Inlines.silk_SMULWB(index_Q16 & 0xFFFF, FIR_Fracs); + + /* Inner product */ + interpol_ptr = FIR_Coefs_ptr + (SilkConstants.RESAMPLER_DOWN_ORDER_FIR0 / 2 * interpol_ind); + res_Q6 = Inlines.silk_SMULWB(buf[buf_ptr + 0], FIR_Coefs[interpol_ptr + 0]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 1], FIR_Coefs[interpol_ptr + 1]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 2], FIR_Coefs[interpol_ptr + 2]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 3], FIR_Coefs[interpol_ptr + 3]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 4], FIR_Coefs[interpol_ptr + 4]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 5], FIR_Coefs[interpol_ptr + 5]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 6], FIR_Coefs[interpol_ptr + 6]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 7], FIR_Coefs[interpol_ptr + 7]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 8], FIR_Coefs[interpol_ptr + 8]); + interpol_ptr = FIR_Coefs_ptr + (SilkConstants.RESAMPLER_DOWN_ORDER_FIR0 / 2 * (FIR_Fracs - 1 - interpol_ind)); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 17], FIR_Coefs[interpol_ptr + 0]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 16], FIR_Coefs[interpol_ptr + 1]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 15], FIR_Coefs[interpol_ptr + 2]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 14], FIR_Coefs[interpol_ptr + 3]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 13], FIR_Coefs[interpol_ptr + 4]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 12], FIR_Coefs[interpol_ptr + 5]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 11], FIR_Coefs[interpol_ptr + 6]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 10], FIR_Coefs[interpol_ptr + 7]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 9], FIR_Coefs[interpol_ptr + 8]); + + /* Scale down, saturate and store in output array */ + output[output_ptr++] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6)); + } + break; + case SilkConstants.RESAMPLER_DOWN_ORDER_FIR1: + for (index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16) { + /* Integer part gives pointer to buffered input */ + buf_ptr = Inlines.silk_RSHIFT(index_Q16, 16); + + /* Inner product */ + res_Q6 = Inlines.silk_SMULWB(Inlines.silk_ADD32(buf[buf_ptr + 0], buf[buf_ptr + 23]), FIR_Coefs[FIR_Coefs_ptr + 0]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 1], buf[buf_ptr + 22]), FIR_Coefs[FIR_Coefs_ptr + 1]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 2], buf[buf_ptr + 21]), FIR_Coefs[FIR_Coefs_ptr + 2]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 3], buf[buf_ptr + 20]), FIR_Coefs[FIR_Coefs_ptr + 3]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 4], buf[buf_ptr + 19]), FIR_Coefs[FIR_Coefs_ptr + 4]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 5], buf[buf_ptr + 18]), FIR_Coefs[FIR_Coefs_ptr + 5]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 6], buf[buf_ptr + 17]), FIR_Coefs[FIR_Coefs_ptr + 6]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 7], buf[buf_ptr + 16]), FIR_Coefs[FIR_Coefs_ptr + 7]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 8], buf[buf_ptr + 15]), FIR_Coefs[FIR_Coefs_ptr + 8]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 9], buf[buf_ptr + 14]), FIR_Coefs[FIR_Coefs_ptr + 9]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 10], buf[buf_ptr + 13]), FIR_Coefs[FIR_Coefs_ptr + 10]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 11], buf[buf_ptr + 12]), FIR_Coefs[FIR_Coefs_ptr + 11]); + + /* Scale down, saturate and store in output array */ + output[output_ptr++] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6)); + } + break; + case SilkConstants.RESAMPLER_DOWN_ORDER_FIR2: + for (index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16) { + /* Integer part gives pointer to buffered input */ + buf_ptr = Inlines.silk_RSHIFT(index_Q16, 16); + + /* Inner product */ + res_Q6 = Inlines.silk_SMULWB(Inlines.silk_ADD32(buf[buf_ptr + 0], buf[buf_ptr + 35]), FIR_Coefs[FIR_Coefs_ptr + 0]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 1], buf[buf_ptr + 34]), FIR_Coefs[FIR_Coefs_ptr + 1]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 2], buf[buf_ptr + 33]), FIR_Coefs[FIR_Coefs_ptr + 2]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 3], buf[buf_ptr + 32]), FIR_Coefs[FIR_Coefs_ptr + 3]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 4], buf[buf_ptr + 31]), FIR_Coefs[FIR_Coefs_ptr + 4]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 5], buf[buf_ptr + 30]), FIR_Coefs[FIR_Coefs_ptr + 5]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 6], buf[buf_ptr + 29]), FIR_Coefs[FIR_Coefs_ptr + 6]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 7], buf[buf_ptr + 28]), FIR_Coefs[FIR_Coefs_ptr + 7]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 8], buf[buf_ptr + 27]), FIR_Coefs[FIR_Coefs_ptr + 8]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 9], buf[buf_ptr + 26]), FIR_Coefs[FIR_Coefs_ptr + 9]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 10], buf[buf_ptr + 25]), FIR_Coefs[FIR_Coefs_ptr + 10]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 11], buf[buf_ptr + 24]), FIR_Coefs[FIR_Coefs_ptr + 11]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 12], buf[buf_ptr + 23]), FIR_Coefs[FIR_Coefs_ptr + 12]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 13], buf[buf_ptr + 22]), FIR_Coefs[FIR_Coefs_ptr + 13]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 14], buf[buf_ptr + 21]), FIR_Coefs[FIR_Coefs_ptr + 14]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 15], buf[buf_ptr + 20]), FIR_Coefs[FIR_Coefs_ptr + 15]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 16], buf[buf_ptr + 19]), FIR_Coefs[FIR_Coefs_ptr + 16]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 17], buf[buf_ptr + 18]), FIR_Coefs[FIR_Coefs_ptr + 17]); + + /* Scale down, saturate and store in output array */ + output[output_ptr++] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6)); + } + break; + default: + Inlines.OpusAssert(false); + break; + } + + return output_ptr; + } + + /// + /// Resample with a 2nd order AR filter followed by FIR interpolation + /// + /// I/O Resampler state + /// O Output signal + /// I Input signal + /// I Number of input samples + static void silk_resampler_private_down_FIR( + SilkResamplerState S, + short[] output, + int output_ptr, + short[] input, + int input_ptr, + int inLen) { + int nSamplesIn; + int max_index_Q16, index_increment_Q16; + int[] buf = new int[S.batchSize + S.FIR_Order]; + + /* Copy buffered samples to start of buffer */ + System.arraycopy(S.sFIR_i32, 0, buf, 0, S.FIR_Order); + + /* Iterate over blocks of frameSizeIn input samples */ + index_increment_Q16 = S.invRatio_Q16; + while (true) { + nSamplesIn = Inlines.silk_min(inLen, S.batchSize); + + /* Second-order AR filter (output in Q8) */ + silk_resampler_private_AR2(S.sIIR, 0, buf, S.FIR_Order, input, input_ptr, S.Coefs, nSamplesIn); + + max_index_Q16 = Inlines.silk_LSHIFT32(nSamplesIn, 16); + + /* Interpolate filtered signal */ + output_ptr = silk_resampler_private_down_FIR_INTERPOL(output, output_ptr, buf, S.Coefs, 2, S.FIR_Order, + S.FIR_Fracs, max_index_Q16, index_increment_Q16); + + input_ptr += nSamplesIn; + inLen -= nSamplesIn; + + if (inLen > 1) { + /* More iterations to do; copy last part of filtered signal to beginning of buffer */ + System.arraycopy(buf, nSamplesIn, buf, 0, S.FIR_Order); + } else { + break; + } + } + + /* Copy last part of filtered signal to the state for the next call */ + System.arraycopy(buf, nSamplesIn, S.sFIR_i32, 0, S.FIR_Order); + } + + static int silk_resampler_private_IIR_FIR_INTERPOL( + short[] output, + int output_ptr, + short[] buf, + int max_index_Q16, + int index_increment_Q16) { + int index_Q16, res_Q15; + int buf_ptr; + int table_index; + + /* Interpolate upsampled signal and store in output array */ + for (index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16) { + table_index = Inlines.silk_SMULWB(index_Q16 & 0xFFFF, 12); + buf_ptr = index_Q16 >> 16; + + res_Q15 = Inlines.silk_SMULBB(buf[buf_ptr], SilkTables.silk_resampler_frac_FIR_12[table_index][0]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 1], SilkTables.silk_resampler_frac_FIR_12[table_index][1]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 2], SilkTables.silk_resampler_frac_FIR_12[table_index][2]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 3], SilkTables.silk_resampler_frac_FIR_12[table_index][3]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 4], SilkTables.silk_resampler_frac_FIR_12[11 - table_index][3]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 5], SilkTables.silk_resampler_frac_FIR_12[11 - table_index][2]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 6], SilkTables.silk_resampler_frac_FIR_12[11 - table_index][1]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 7], SilkTables.silk_resampler_frac_FIR_12[11 - table_index][0]); + output[output_ptr++] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q15, 15)); + } + return output_ptr; + } + + /// + /// Upsample using a combination of allpass-based 2x upsampling and FIR interpolation + /// + /// I/O Resampler state + /// O Output signal + /// I Input signal + /// I Number of input samples + static void silk_resampler_private_IIR_FIR( + SilkResamplerState S, + short[] output, + int output_ptr, + short[] input, + int input_ptr, + int inLen) { + int nSamplesIn; + int max_index_Q16, index_increment_Q16; + + short[] buf = new short[2 * S.batchSize + SilkConstants.RESAMPLER_ORDER_FIR_12]; + + /* Copy buffered samples to start of buffer */ + System.arraycopy(S.sFIR_i16, 0, buf, 0, SilkConstants.RESAMPLER_ORDER_FIR_12); + + /* Iterate over blocks of frameSizeIn input samples */ + index_increment_Q16 = S.invRatio_Q16; + while (true) { + nSamplesIn = Inlines.silk_min(inLen, S.batchSize); + + /* Upsample 2x */ + silk_resampler_private_up2_HQ(S.sIIR, buf, SilkConstants.RESAMPLER_ORDER_FIR_12, input, input_ptr, nSamplesIn); + + max_index_Q16 = Inlines.silk_LSHIFT32(nSamplesIn, 16 + 1); + /* + 1 because 2x upsampling */ + output_ptr = silk_resampler_private_IIR_FIR_INTERPOL(output, output_ptr, buf, max_index_Q16, index_increment_Q16); + input_ptr += nSamplesIn; + inLen -= nSamplesIn; + + if (inLen > 0) { + /* More iterations to do; copy last part of filtered signal to beginning of buffer */ + System.arraycopy(buf, nSamplesIn << 1, buf, 0, SilkConstants.RESAMPLER_ORDER_FIR_12); + } else { + break; + } + } + + /* Copy last part of filtered signal to the state for the next call */ + System.arraycopy(buf, nSamplesIn << 1, S.sFIR_i16, 0, SilkConstants.RESAMPLER_ORDER_FIR_12); + } + + /// + /// Upsample by a factor 2, high quality + /// Uses 2nd order allpass filters for the 2x upsampling, followed by a + /// notch filter just above Nyquist. + /// + /// I/O Resampler state [ 6 ] + /// O Output signal [ 2 * len ] + /// I Input signal [ len ] + /// I Number of input samples + static void silk_resampler_private_up2_HQ( + int[] S, + short[] output, + int output_ptr, + short[] input, + int input_ptr, + int len) { + int k; + int in32, out32_1, out32_2, Y, X; + + Inlines.OpusAssert(SilkTables.silk_resampler_up2_hq_0[0] > 0); + Inlines.OpusAssert(SilkTables.silk_resampler_up2_hq_0[1] > 0); + Inlines.OpusAssert(SilkTables.silk_resampler_up2_hq_0[2] < 0); + Inlines.OpusAssert(SilkTables.silk_resampler_up2_hq_1[0] > 0); + Inlines.OpusAssert(SilkTables.silk_resampler_up2_hq_1[1] > 0); + Inlines.OpusAssert(SilkTables.silk_resampler_up2_hq_1[2] < 0); + + /* Internal variables and state are in Q10 format */ + for (k = 0; k < len; k++) { + /* Convert to Q10 */ + in32 = Inlines.silk_LSHIFT((int) input[input_ptr + k], 10); + + /* First all-pass section for even output sample */ + Y = Inlines.silk_SUB32(in32, S[0]); + X = Inlines.silk_SMULWB(Y, SilkTables.silk_resampler_up2_hq_0[0]); + out32_1 = Inlines.silk_ADD32(S[0], X); + S[0] = Inlines.silk_ADD32(in32, X); + + /* Second all-pass section for even output sample */ + Y = Inlines.silk_SUB32(out32_1, S[1]); + X = Inlines.silk_SMULWB(Y, SilkTables.silk_resampler_up2_hq_0[1]); + out32_2 = Inlines.silk_ADD32(S[1], X); + S[1] = Inlines.silk_ADD32(out32_1, X); + + /* Third all-pass section for even output sample */ + Y = Inlines.silk_SUB32(out32_2, S[2]); + X = Inlines.silk_SMLAWB(Y, Y, SilkTables.silk_resampler_up2_hq_0[2]); + out32_1 = Inlines.silk_ADD32(S[2], X); + S[2] = Inlines.silk_ADD32(out32_2, X); + + /* Apply gain in Q15, convert back to int16 and store to output */ + output[output_ptr + (2 * k)] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(out32_1, 10)); + + /* First all-pass section for odd output sample */ + Y = Inlines.silk_SUB32(in32, S[3]); + X = Inlines.silk_SMULWB(Y, SilkTables.silk_resampler_up2_hq_1[0]); + out32_1 = Inlines.silk_ADD32(S[3], X); + S[3] = Inlines.silk_ADD32(in32, X); + + /* Second all-pass section for odd output sample */ + Y = Inlines.silk_SUB32(out32_1, S[4]); + X = Inlines.silk_SMULWB(Y, SilkTables.silk_resampler_up2_hq_1[1]); + out32_2 = Inlines.silk_ADD32(S[4], X); + S[4] = Inlines.silk_ADD32(out32_1, X); + + /* Third all-pass section for odd output sample */ + Y = Inlines.silk_SUB32(out32_2, S[5]); + X = Inlines.silk_SMLAWB(Y, Y, SilkTables.silk_resampler_up2_hq_1[2]); + out32_1 = Inlines.silk_ADD32(S[5], X); + S[5] = Inlines.silk_ADD32(out32_2, X); + + /* Apply gain in Q15, convert back to int16 and store to output */ + output[output_ptr + (2 * k) + 1] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(out32_1, 10)); + } + } +} diff --git a/app/src/main/java/org/concentus/ResidualEnergy.java b/app/src/main/java/org/concentus/ResidualEnergy.java new file mode 100644 index 0000000000..87bdfc222c --- /dev/null +++ b/app/src/main/java/org/concentus/ResidualEnergy.java @@ -0,0 +1,178 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class ResidualEnergy { + + /* Calculates residual energies of input subframes where all subframes have LPC_order */ + /* of preceding samples */ + static void silk_residual_energy( + int[] nrgs, /* O Residual energy per subframe [MAX_NB_SUBFR] */ + int[] nrgsQ, /* O Q value per subframe [MAX_NB_SUBFR] */ + short[] x, /* I Input signal */ + short[][] a_Q12, /* I AR coefs for each frame half [2][MAX_LPC_ORDER] */ + int[] gains, /* I Quantization gains [SilkConstants.MAX_NB_SUBFR] */ + int subfr_length, /* I Subframe length */ + int nb_subfr, /* I Number of subframes */ + int LPC_order /* I LPC order */ + ) { + int offset, i, j, lz1, lz2; + BoxedValueInt rshift = new BoxedValueInt(0); + BoxedValueInt energy = new BoxedValueInt(0); + int LPC_res_ptr; + short[] LPC_res; + int x_ptr; + int tmp32; + + x_ptr = 0; + offset = LPC_order + subfr_length; + + /* Filter input to create the LPC residual for each frame half, and measure subframe energies */ + LPC_res = new short[(SilkConstants.MAX_NB_SUBFR >> 1) * offset]; + Inlines.OpusAssert((nb_subfr >> 1) * (SilkConstants.MAX_NB_SUBFR >> 1) == nb_subfr); + for (i = 0; i < nb_subfr >> 1; i++) { + /* Calculate half frame LPC residual signal including preceding samples */ + Filters.silk_LPC_analysis_filter(LPC_res, 0, x, x_ptr, a_Q12[i], 0, (SilkConstants.MAX_NB_SUBFR >> 1) * offset, LPC_order); + + /* Point to first subframe of the just calculated LPC residual signal */ + LPC_res_ptr = LPC_order; + for (j = 0; j < (SilkConstants.MAX_NB_SUBFR >> 1); j++) { + /* Measure subframe energy */ + SumSqrShift.silk_sum_sqr_shift(energy, rshift, LPC_res, LPC_res_ptr, subfr_length); + nrgs[i * (SilkConstants.MAX_NB_SUBFR >> 1) + j] = energy.Val; + + /* Set Q values for the measured energy */ + nrgsQ[i * (SilkConstants.MAX_NB_SUBFR >> 1) + j] = 0 - rshift.Val; + + /* Move to next subframe */ + LPC_res_ptr += offset; + } + /* Move to next frame half */ + x_ptr += (SilkConstants.MAX_NB_SUBFR >> 1) * offset; + } + + /* Apply the squared subframe gains */ + for (i = 0; i < nb_subfr; i++) { + /* Fully upscale gains and energies */ + lz1 = Inlines.silk_CLZ32(nrgs[i]) - 1; + lz2 = Inlines.silk_CLZ32(gains[i]) - 1; + + tmp32 = Inlines.silk_LSHIFT32(gains[i], lz2); + + /* Find squared gains */ + tmp32 = Inlines.silk_SMMUL(tmp32, tmp32); + /* Q( 2 * lz2 - 32 )*/ + + /* Scale energies */ + nrgs[i] = Inlines.silk_SMMUL(tmp32, Inlines.silk_LSHIFT32(nrgs[i], lz1)); + /* Q( nrgsQ[ i ] + lz1 + 2 * lz2 - 32 - 32 )*/ + nrgsQ[i] += lz1 + 2 * lz2 - 32 - 32; + } + + } + + /* Residual energy: nrg = wxx - 2 * wXx * c + c' * wXX * c */ + static int silk_residual_energy16_covar( + short[] c, /* I Prediction vector */ + int c_ptr, + int[] wXX, /* I Correlation matrix */ + int wXX_ptr, + int[] wXx, /* I Correlation vector */ + int wxx, /* I Signal energy */ + int D, /* I Dimension */ + int cQ /* I Q value for c vector 0 - 15 */ + ) { + int i, j, lshifts, Qxtra; + int c_max, w_max, tmp, tmp2, nrg; + int[] cn = new int[D]; //SilkConstants.MAX_MATRIX_SIZE + int pRow; + + /* Safety checks */ + Inlines.OpusAssert(D >= 0); + Inlines.OpusAssert(D <= 16); + Inlines.OpusAssert(cQ > 0); + Inlines.OpusAssert(cQ < 16); + + lshifts = 16 - cQ; + Qxtra = lshifts; + + c_max = 0; + for (i = c_ptr; i < c_ptr + D; i++) { + c_max = Inlines.silk_max_32(c_max, Inlines.silk_abs((int) c[i])); + } + Qxtra = Inlines.silk_min_int(Qxtra, Inlines.silk_CLZ32(c_max) - 17); + + w_max = Inlines.silk_max_32(wXX[wXX_ptr], wXX[wXX_ptr + (D * D) - 1]); + Qxtra = Inlines.silk_min_int(Qxtra, Inlines.silk_CLZ32(Inlines.silk_MUL(D, Inlines.silk_RSHIFT(Inlines.silk_SMULWB(w_max, c_max), 4))) - 5); + Qxtra = Inlines.silk_max_int(Qxtra, 0); + for (i = 0; i < D; i++) { + cn[i] = Inlines.silk_LSHIFT((int) c[c_ptr + i], Qxtra); + Inlines.OpusAssert(Inlines.silk_abs(cn[i]) <= (Short.MAX_VALUE + 1)); + /* Check that Inlines.silk_SMLAWB can be used */ + } + lshifts -= Qxtra; + + /* Compute wxx - 2 * wXx * c */ + tmp = 0; + for (i = 0; i < D; i++) { + tmp = Inlines.silk_SMLAWB(tmp, wXx[i], cn[i]); + } + nrg = Inlines.silk_RSHIFT(wxx, 1 + lshifts) - tmp; + /* Q: -lshifts - 1 */ + + /* Add c' * wXX * c, assuming wXX is symmetric */ + tmp2 = 0; + for (i = 0; i < D; i++) { + tmp = 0; + pRow = wXX_ptr + (i * D); + for (j = i + 1; j < D; j++) { + tmp = Inlines.silk_SMLAWB(tmp, wXX[pRow + j], cn[j]); + } + tmp = Inlines.silk_SMLAWB(tmp, Inlines.silk_RSHIFT(wXX[pRow + i], 1), cn[i]); + tmp2 = Inlines.silk_SMLAWB(tmp2, tmp, cn[i]); + } + nrg = Inlines.silk_ADD_LSHIFT32(nrg, tmp2, lshifts); + /* Q: -lshifts - 1 */ + + /* Keep one bit free always, because we add them for LSF interpolation */ + if (nrg < 1) { + nrg = 1; + } else if (nrg > Inlines.silk_RSHIFT(Integer.MAX_VALUE, lshifts + 2)) { + nrg = Integer.MAX_VALUE >> 1; + } else { + nrg = Inlines.silk_LSHIFT(nrg, lshifts + 1); + /* Q0 */ + } + return nrg; + + } +} diff --git a/app/src/main/java/org/concentus/Schur.java b/app/src/main/java/org/concentus/Schur.java new file mode 100644 index 0000000000..adeb2f2aa5 --- /dev/null +++ b/app/src/main/java/org/concentus/Schur.java @@ -0,0 +1,167 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class Schur { + + /* Faster than schur64(), but much less accurate. */ + /* uses SMLAWB(), requiring armv5E and higher. */ + static int silk_schur( /* O Returns residual energy */ + short[] rc_Q15, /* O reflection coefficients [order] Q15 */ + int[] c, /* I correlations [order+1] */ + int order /* I prediction order */ + ) { + int k, n, lz; + int[][] C = Arrays.InitTwoDimensionalArrayInt(SilkConstants.SILK_MAX_ORDER_LPC + 1, 2); + int Ctmp1, Ctmp2, rc_tmp_Q15; + + Inlines.OpusAssert(order == 6 || order == 8 || order == 10 || order == 12 || order == 14 || order == 16); + + /* Get number of leading zeros */ + lz = Inlines.silk_CLZ32(c[0]); + + /* Copy correlations and adjust level to Q30 */ + if (lz < 2) { + /* lz must be 1, so shift one to the right */ + for (k = 0; k < order + 1; k++) { + C[k][0] = C[k][1] = Inlines.silk_RSHIFT(c[k], 1); + } + } else if (lz > 2) { + /* Shift to the left */ + lz -= 2; + for (k = 0; k < order + 1; k++) { + C[k][0] = C[k][1] = Inlines.silk_LSHIFT(c[k], lz); + } + } else { + /* No need to shift */ + for (k = 0; k < order + 1; k++) { + C[k][0] = C[k][1] = c[k]; + } + } + + for (k = 0; k < order; k++) { + /* Check that we won't be getting an unstable rc, otherwise stop here. */ + if (Inlines.silk_abs_int32(C[k + 1][0]) >= C[0][1]) { + if (C[k + 1][0] > 0) { + rc_Q15[k] = (short) (0 - ((int) ((.99f) * ((long) 1 << (15)) + 0.5))/*Inlines.SILK_CONST(.99f, 15)*/); + } else { + rc_Q15[k] = (short) (((int) ((.99f) * ((long) 1 << (15)) + 0.5))/*Inlines.SILK_CONST(.99f, 15)*/); + } + k++; + break; + } + + /* Get reflection coefficient */ + rc_tmp_Q15 = 0 - Inlines.silk_DIV32_16(C[k + 1][0], Inlines.silk_max_32(Inlines.silk_RSHIFT(C[0][1], 15), 1)); + + /* Clip (shouldn't happen for properly conditioned inputs) */ + rc_tmp_Q15 = Inlines.silk_SAT16(rc_tmp_Q15); + + /* Store */ + rc_Q15[k] = (short) rc_tmp_Q15; + + /* Update correlations */ + for (n = 0; n < order - k; n++) { + Ctmp1 = C[n + k + 1][0]; + Ctmp2 = C[n][1]; + C[n + k + 1][0] = Inlines.silk_SMLAWB(Ctmp1, Inlines.silk_LSHIFT(Ctmp2, 1), rc_tmp_Q15); + C[n][1] = Inlines.silk_SMLAWB(Ctmp2, Inlines.silk_LSHIFT(Ctmp1, 1), rc_tmp_Q15); + } + } + + for (; k < order; k++) { + rc_Q15[k] = 0; + } + + /* return residual energy */ + return Inlines.silk_max_32(1, C[0][1]); + } + + /* Slower than schur(), but more accurate. */ + /* Uses SMULL(), available on armv4 */ + static int silk_schur64( /* O returns residual energy */ + int[] rc_Q16, /* O Reflection coefficients [order] Q16 */ + int[] c, /* I Correlations [order+1] */ + int order /* I Prediction order */ + ) { + int k, n; + int[][] C = Arrays.InitTwoDimensionalArrayInt(SilkConstants.SILK_MAX_ORDER_LPC + 1, 2); + int Ctmp1_Q30, Ctmp2_Q30, rc_tmp_Q31; + + Inlines.OpusAssert(order == 6 || order == 8 || order == 10 || order == 12 || order == 14 || order == 16); + + /* Check for invalid input */ + if (c[0] <= 0) { + Arrays.MemSet(rc_Q16, 0, order); + return 0; + } + + for (k = 0; k < order + 1; k++) { + C[k][0] = C[k][1] = c[k]; + } + + for (k = 0; k < order; k++) { + /* Check that we won't be getting an unstable rc, otherwise stop here. */ + if (Inlines.silk_abs_int32(C[k + 1][0]) >= C[0][1]) { + if (C[k + 1][0] > 0) { + rc_Q16[k] = -((int) ((.99f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(.99f, 16)*/; + } else { + rc_Q16[k] = ((int) ((.99f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(.99f, 16)*/; + } + k++; + break; + } + + /* Get reflection coefficient: divide two Q30 values and get result in Q31 */ + rc_tmp_Q31 = Inlines.silk_DIV32_varQ(-C[k + 1][0], C[0][1], 31); + + /* Save the output */ + rc_Q16[k] = Inlines.silk_RSHIFT_ROUND(rc_tmp_Q31, 15); + + /* Update correlations */ + for (n = 0; n < order - k; n++) { + Ctmp1_Q30 = C[n + k + 1][0]; + Ctmp2_Q30 = C[n][1]; + + /* Multiply and add the highest int32 */ + C[n + k + 1][0] = Ctmp1_Q30 + Inlines.silk_SMMUL(Inlines.silk_LSHIFT(Ctmp2_Q30, 1), rc_tmp_Q31); + C[n][1] = Ctmp2_Q30 + Inlines.silk_SMMUL(Inlines.silk_LSHIFT(Ctmp1_Q30, 1), rc_tmp_Q31); + } + } + + for (; k < order; k++) { + rc_Q16[k] = 0; + } + + return Inlines.silk_max_32(1, C[0][1]); + } +} diff --git a/app/src/main/java/org/concentus/ShellCoder.java b/app/src/main/java/org/concentus/ShellCoder.java new file mode 100644 index 0000000000..87f4355d7e --- /dev/null +++ b/app/src/main/java/org/concentus/ShellCoder.java @@ -0,0 +1,186 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// shell coder; pulse-subframe length is hardcoded +/// +class ShellCoder { + + /// + /// + /// O combined pulses vector [len] + /// I input vector [2 * len] + /// I number of OUTPUT samples + static void combine_pulses( + int[] output, + int[] input, + int input_ptr, + int len) { + int k; + for (k = 0; k < len; k++) { + output[k] = input[input_ptr + (2 * k)] + input[input_ptr + (2 * k) + 1]; + } + } + + /// + /// + /// O combined pulses vector [len] + /// I input vector [2 * len] + /// I number of OUTPUT samples + static void combine_pulses( + int[] output, + int[] input, + int len) { + int k; + for (k = 0; k < len; k++) { + output[k] = input[2 * k] + input[2 * k + 1]; + } + } + + static void encode_split( + EntropyCoder psRangeEnc, /* I/O compressor data structure */ + int p_child1, /* I pulse amplitude of first child subframe */ + int p, /* I pulse amplitude of current subframe */ + short[] shell_table /* I table of shell cdfs */ + ) { + if (p > 0) { + psRangeEnc.enc_icdf(p_child1, shell_table, SilkTables.silk_shell_code_table_offsets[p], 8); + } + } + + /// + /// + /// + /// O pulse amplitude of first child subframe + /// O pulse amplitude of second child subframe + /// I/O Compressor data structure + /// I pulse amplitude of current subframe + /// I table of shell cdfs + static void decode_split( + short[] p_child1, + int child1_ptr, + short[] p_child2, + int p_child2_ptr, + EntropyCoder psRangeDec, + int p, + short[] shell_table) { + if (p > 0) { + p_child1[child1_ptr] = (short) (psRangeDec.dec_icdf(shell_table, (SilkTables.silk_shell_code_table_offsets[p]), 8)); + p_child2[p_child2_ptr] = (short) (p - p_child1[child1_ptr]); + } else { + p_child1[child1_ptr] = 0; + p_child2[p_child2_ptr] = 0; + } + } + + /// + /// Shell encoder, operates on one shell code frame of 16 pulses + /// + /// I/O compressor data structure + /// I data: nonnegative pulse amplitudes + static void silk_shell_encoder(EntropyCoder psRangeEnc, int[] pulses0, int pulses0_ptr) { + int[] pulses1 = new int[8]; + int[] pulses2 = new int[4]; + int[] pulses3 = new int[2]; + int[] pulses4 = new int[1]; + + /* this function operates on one shell code frame of 16 pulses */ + Inlines.OpusAssert(SilkConstants.SHELL_CODEC_FRAME_LENGTH == 16); + + /* tree representation per pulse-subframe */ + combine_pulses(pulses1, pulses0, pulses0_ptr, 8); + combine_pulses(pulses2, pulses1, 4); + combine_pulses(pulses3, pulses2, 2); + combine_pulses(pulses4, pulses3, 1); + + encode_split(psRangeEnc, pulses3[0], pulses4[0], SilkTables.silk_shell_code_table3); + + encode_split(psRangeEnc, pulses2[0], pulses3[0], SilkTables.silk_shell_code_table2); + + encode_split(psRangeEnc, pulses1[0], pulses2[0], SilkTables.silk_shell_code_table1); + encode_split(psRangeEnc, pulses0[pulses0_ptr], pulses1[0], SilkTables.silk_shell_code_table0); + encode_split(psRangeEnc, pulses0[pulses0_ptr + 2], pulses1[1], SilkTables.silk_shell_code_table0); + + encode_split(psRangeEnc, pulses1[2], pulses2[1], SilkTables.silk_shell_code_table1); + encode_split(psRangeEnc, pulses0[pulses0_ptr + 4], pulses1[2], SilkTables.silk_shell_code_table0); + encode_split(psRangeEnc, pulses0[pulses0_ptr + 6], pulses1[3], SilkTables.silk_shell_code_table0); + + encode_split(psRangeEnc, pulses2[2], pulses3[1], SilkTables.silk_shell_code_table2); + + encode_split(psRangeEnc, pulses1[4], pulses2[2], SilkTables.silk_shell_code_table1); + encode_split(psRangeEnc, pulses0[pulses0_ptr + 8], pulses1[4], SilkTables.silk_shell_code_table0); + encode_split(psRangeEnc, pulses0[pulses0_ptr + 10], pulses1[5], SilkTables.silk_shell_code_table0); + + encode_split(psRangeEnc, pulses1[6], pulses2[3], SilkTables.silk_shell_code_table1); + encode_split(psRangeEnc, pulses0[pulses0_ptr + 12], pulses1[6], SilkTables.silk_shell_code_table0); + encode_split(psRangeEnc, pulses0[pulses0_ptr + 14], pulses1[7], SilkTables.silk_shell_code_table0); + } + + + /* Shell decoder, operates on one shell code frame of 16 pulses */ + static void silk_shell_decoder( + short[] pulses0, /* O data: nonnegative pulse amplitudes */ + int pulses0_ptr, + EntropyCoder psRangeDec, /* I/O Compressor data structure */ + int pulses4 /* I number of pulses per pulse-subframe */ + ) { + short[] pulses1 = new short[8]; + short[] pulses2 = new short[4]; + short[] pulses3 = new short[2]; + + /* this function operates on one shell code frame of 16 pulses */ + Inlines.OpusAssert(SilkConstants.SHELL_CODEC_FRAME_LENGTH == 16); + + decode_split(pulses3, 0, pulses3, 1, psRangeDec, pulses4, SilkTables.silk_shell_code_table3); + + decode_split(pulses2, 0, pulses2, 1, psRangeDec, pulses3[0], SilkTables.silk_shell_code_table2); + + decode_split(pulses1, 0, pulses1, 1, psRangeDec, pulses2[0], SilkTables.silk_shell_code_table1); + decode_split(pulses0, pulses0_ptr, pulses0, pulses0_ptr + 1, psRangeDec, pulses1[0], SilkTables.silk_shell_code_table0); + decode_split(pulses0, pulses0_ptr + 2, pulses0, pulses0_ptr + 3, psRangeDec, pulses1[1], SilkTables.silk_shell_code_table0); + + decode_split(pulses1, 2, pulses1, 3, psRangeDec, pulses2[1], SilkTables.silk_shell_code_table1); + decode_split(pulses0, pulses0_ptr + 4, pulses0, pulses0_ptr + 5, psRangeDec, pulses1[2], SilkTables.silk_shell_code_table0); + decode_split(pulses0, pulses0_ptr + 6, pulses0, pulses0_ptr + 7, psRangeDec, pulses1[3], SilkTables.silk_shell_code_table0); + + decode_split(pulses2, 2, pulses2, 3, psRangeDec, pulses3[1], SilkTables.silk_shell_code_table2); + + decode_split(pulses1, 4, pulses1, 5, psRangeDec, pulses2[2], SilkTables.silk_shell_code_table1); + decode_split(pulses0, pulses0_ptr + 8, pulses0, pulses0_ptr + 9, psRangeDec, pulses1[4], SilkTables.silk_shell_code_table0); + decode_split(pulses0, pulses0_ptr + 10, pulses0, pulses0_ptr + 11, psRangeDec, pulses1[5], SilkTables.silk_shell_code_table0); + + decode_split(pulses1, 6, pulses1, 7, psRangeDec, pulses2[3], SilkTables.silk_shell_code_table1); + decode_split(pulses0, pulses0_ptr + 12, pulses0, pulses0_ptr + 13, psRangeDec, pulses1[6], SilkTables.silk_shell_code_table0); + decode_split(pulses0, pulses0_ptr + 14, pulses0, pulses0_ptr + 15, psRangeDec, pulses1[7], SilkTables.silk_shell_code_table0); + } +} diff --git a/app/src/main/java/org/concentus/SideInfoIndices.java b/app/src/main/java/org/concentus/SideInfoIndices.java new file mode 100644 index 0000000000..ab564d8c37 --- /dev/null +++ b/app/src/main/java/org/concentus/SideInfoIndices.java @@ -0,0 +1,79 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class SideInfoIndices { + + final byte[] GainsIndices = new byte[SilkConstants.MAX_NB_SUBFR]; + final byte[] LTPIndex = new byte[SilkConstants.MAX_NB_SUBFR]; + final byte[] NLSFIndices = new byte[SilkConstants.MAX_LPC_ORDER + 1]; + short lagIndex = 0; + byte contourIndex = 0; + byte signalType = 0; + byte quantOffsetType = 0; + byte NLSFInterpCoef_Q2 = 0; + byte PERIndex = 0; + byte LTP_scaleIndex = 0; + byte Seed = 0; + + void Reset() { + Arrays.MemSet(GainsIndices, (byte) 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSet(LTPIndex, (byte) 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSet(NLSFIndices, (byte) 0, SilkConstants.MAX_LPC_ORDER + 1); + lagIndex = 0; + contourIndex = 0; + signalType = 0; + quantOffsetType = 0; + NLSFInterpCoef_Q2 = 0; + PERIndex = 0; + LTP_scaleIndex = 0; + Seed = 0; + } + + /// + /// Overwrites this struct with values from another one. Equivalent to C struct assignment this = other + /// + /// + void Assign(SideInfoIndices other) { + System.arraycopy(other.GainsIndices, 0, this.GainsIndices, 0, SilkConstants.MAX_NB_SUBFR); + System.arraycopy(other.LTPIndex, 0, this.LTPIndex, 0, SilkConstants.MAX_NB_SUBFR); + System.arraycopy(other.NLSFIndices, 0, this.NLSFIndices, 0, SilkConstants.MAX_LPC_ORDER + 1); + this.lagIndex = other.lagIndex; + this.contourIndex = other.contourIndex; + this.signalType = other.signalType; + this.quantOffsetType = other.quantOffsetType; + this.NLSFInterpCoef_Q2 = other.NLSFInterpCoef_Q2; + this.PERIndex = other.PERIndex; + this.LTP_scaleIndex = other.LTP_scaleIndex; + this.Seed = other.Seed; + } +} diff --git a/app/src/main/java/org/concentus/Sigmoid.java b/app/src/main/java/org/concentus/Sigmoid.java new file mode 100644 index 0000000000..915bb1af04 --- /dev/null +++ b/app/src/main/java/org/concentus/Sigmoid.java @@ -0,0 +1,74 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Approximate sigmoid function +/// +class Sigmoid { + + private static final int[] sigm_LUT_slope_Q10 = { + 237, 153, 73, 30, 12, 7 + }; + + private static final int[] sigm_LUT_pos_Q15 = { + 16384, 23955, 28861, 31213, 32178, 32548 + }; + + private static final int[] sigm_LUT_neg_Q15 = { + 16384, 8812, 3906, 1554, 589, 219 + }; + + static int silk_sigm_Q15(int in_Q5) { + int ind; + + if (in_Q5 < 0) { + /* Negative input */ + in_Q5 = -in_Q5; + if (in_Q5 >= 6 * 32) { + return 0; + /* Clip */ + } else { + /* Linear interpolation of look up table */ + ind = Inlines.silk_RSHIFT(in_Q5, 5); + return (sigm_LUT_neg_Q15[ind] - Inlines.silk_SMULBB(sigm_LUT_slope_Q10[ind], in_Q5 & 0x1F)); + } + } else /* Positive input */ if (in_Q5 >= 6 * 32) { + return 32767; + /* clip */ + } else { + /* Linear interpolation of look up table */ + ind = Inlines.silk_RSHIFT(in_Q5, 5); + return (sigm_LUT_pos_Q15[ind] + Inlines.silk_SMULBB(sigm_LUT_slope_Q10[ind], in_Q5 & 0x1F)); + } + } +} diff --git a/app/src/main/java/org/concentus/SilkChannelDecoder.java b/app/src/main/java/org/concentus/SilkChannelDecoder.java new file mode 100644 index 0000000000..64132221ad --- /dev/null +++ b/app/src/main/java/org/concentus/SilkChannelDecoder.java @@ -0,0 +1,378 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Decoder state +/// +class SilkChannelDecoder { + + int prev_gain_Q16 = 0; + final int[] exc_Q14 = new int[SilkConstants.MAX_FRAME_LENGTH]; + final int[] sLPC_Q14_buf = new int[SilkConstants.MAX_LPC_ORDER]; + final short[] outBuf = new short[SilkConstants.MAX_FRAME_LENGTH + 2 * SilkConstants.MAX_SUB_FRAME_LENGTH]; + /* Buffer for output signal */ + int lagPrev = 0; + /* Previous Lag */ + byte LastGainIndex = 0; + /* Previous gain index */ + int fs_kHz = 0; + /* Sampling frequency in kHz */ + int fs_API_hz = 0; + /* API sample frequency (Hz) */ + int nb_subfr = 0; + /* Number of 5 ms subframes in a frame */ + int frame_length = 0; + /* Frame length (samples) */ + int subfr_length = 0; + /* Subframe length (samples) */ + int ltp_mem_length = 0; + /* Length of LTP memory */ + int LPC_order = 0; + /* LPC order */ + final short[] prevNLSF_Q15 = new short[SilkConstants.MAX_LPC_ORDER]; + /* Used to interpolate LSFs */ + int first_frame_after_reset = 0; + /* Flag for deactivating NLSF interpolation */ + short[] pitch_lag_low_bits_iCDF; + /* Pointer to iCDF table for low bits of pitch lag index */ + short[] pitch_contour_iCDF; + /* Pointer to iCDF table for pitch contour index */ + + /* For buffering payload in case of more frames per packet */ + int nFramesDecoded = 0; + int nFramesPerPacket = 0; + + /* Specifically for entropy coding */ + int ec_prevSignalType = 0; + short ec_prevLagIndex = 0; + + final int[] VAD_flags = new int[SilkConstants.MAX_FRAMES_PER_PACKET]; + int LBRR_flag = 0; + final int[] LBRR_flags = new int[SilkConstants.MAX_FRAMES_PER_PACKET]; + + final SilkResamplerState resampler_state = new SilkResamplerState(); + + NLSFCodebook psNLSF_CB = null; + /* Pointer to NLSF codebook */ + + /* Quantization indices */ + final SideInfoIndices indices = new SideInfoIndices(); + + /* CNG state */ + final CNGState sCNG = new CNGState(); + + /* Stuff used for PLC */ + int lossCnt = 0; + int prevSignalType = 0; + + final PLCStruct sPLC = new PLCStruct(); + + void Reset() { + prev_gain_Q16 = 0; + Arrays.MemSet(exc_Q14, 0, SilkConstants.MAX_FRAME_LENGTH); + Arrays.MemSet(sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER); + Arrays.MemSet(outBuf, (short) 0, SilkConstants.MAX_FRAME_LENGTH + 2 * SilkConstants.MAX_SUB_FRAME_LENGTH); + lagPrev = 0; + LastGainIndex = 0; + fs_kHz = 0; + fs_API_hz = 0; + nb_subfr = 0; + frame_length = 0; + subfr_length = 0; + ltp_mem_length = 0; + LPC_order = 0; + Arrays.MemSet(prevNLSF_Q15, (short) 0, SilkConstants.MAX_LPC_ORDER); + first_frame_after_reset = 0; + pitch_lag_low_bits_iCDF = null; + pitch_contour_iCDF = null; + nFramesDecoded = 0; + nFramesPerPacket = 0; + ec_prevSignalType = 0; + ec_prevLagIndex = 0; + Arrays.MemSet(VAD_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET); + LBRR_flag = 0; + Arrays.MemSet(LBRR_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET); + resampler_state.Reset(); + psNLSF_CB = null; + indices.Reset(); + sCNG.Reset(); + lossCnt = 0; + prevSignalType = 0; + sPLC.Reset(); + } + + /// + /// Init Decoder State + /// + /// I/O Decoder state pointer + /// + int silk_init_decoder() { + /* Clear the entire encoder state, except anything copied */ + this.Reset(); + + /* Used to deactivate LSF interpolation */ + this.first_frame_after_reset = 1; + this.prev_gain_Q16 = 65536; + + /* Reset CNG state */ + silk_CNG_Reset(); + + /* Reset PLC state */ + silk_PLC_Reset(); + + return (0); + } + + /// + /// Resets CNG state + /// + /// I/O Decoder state + private void silk_CNG_Reset() { + int i, NLSF_step_Q15, NLSF_acc_Q15; + + NLSF_step_Q15 = Inlines.silk_DIV32_16(Short.MAX_VALUE, this.LPC_order + 1); + NLSF_acc_Q15 = 0; + for (i = 0; i < this.LPC_order; i++) { + NLSF_acc_Q15 += NLSF_step_Q15; + this.sCNG.CNG_smth_NLSF_Q15[i] = (short) (NLSF_acc_Q15); + } + this.sCNG.CNG_smth_Gain_Q16 = 0; + this.sCNG.rand_seed = 3176576; + } + + /// + /// Resets PLC state + /// + /// I/O Decoder state + private void silk_PLC_Reset() { + this.sPLC.pitchL_Q8 = Inlines.silk_LSHIFT(this.frame_length, 8 - 1); + this.sPLC.prevGain_Q16[0] = ((int) ((1) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(1, 16)*/; + this.sPLC.prevGain_Q16[1] = ((int) ((1) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(1, 16)*/; + this.sPLC.subfr_length = 20; + this.sPLC.nb_subfr = 2; + } + + /* Set decoder sampling rate */ + int silk_decoder_set_fs( + int fs_kHz, /* I Sampling frequency (kHz) */ + int fs_API_Hz /* I API Sampling frequency (Hz) */ + ) { + int frame_length, ret = 0; + + Inlines.OpusAssert(fs_kHz == 8 || fs_kHz == 12 || fs_kHz == 16); + Inlines.OpusAssert(this.nb_subfr == SilkConstants.MAX_NB_SUBFR || this.nb_subfr == SilkConstants.MAX_NB_SUBFR / 2); + + /* New (sub)frame length */ + this.subfr_length = Inlines.silk_SMULBB(SilkConstants.SUB_FRAME_LENGTH_MS, fs_kHz); + frame_length = Inlines.silk_SMULBB(this.nb_subfr, this.subfr_length); + + /* Initialize resampler when switching or external sampling frequency */ + if (this.fs_kHz != fs_kHz || this.fs_API_hz != fs_API_Hz) { + /* Initialize the resampler for dec_API.c preparing resampling from fs_kHz to API_fs_Hz */ + ret += Resampler.silk_resampler_init(this.resampler_state, Inlines.silk_SMULBB(fs_kHz, 1000), fs_API_Hz, 0); + + this.fs_API_hz = fs_API_Hz; + } + + if (this.fs_kHz != fs_kHz || frame_length != this.frame_length) { + if (fs_kHz == 8) { + if (this.nb_subfr == SilkConstants.MAX_NB_SUBFR) { + this.pitch_contour_iCDF = SilkTables.silk_pitch_contour_NB_iCDF; + } else { + this.pitch_contour_iCDF = SilkTables.silk_pitch_contour_10_ms_NB_iCDF; + } + } else if (this.nb_subfr == SilkConstants.MAX_NB_SUBFR) { + this.pitch_contour_iCDF = SilkTables.silk_pitch_contour_iCDF; + } else { + this.pitch_contour_iCDF = SilkTables.silk_pitch_contour_10_ms_iCDF; + } + if (this.fs_kHz != fs_kHz) { + this.ltp_mem_length = Inlines.silk_SMULBB(SilkConstants.LTP_MEM_LENGTH_MS, fs_kHz); + if (fs_kHz == 8 || fs_kHz == 12) { + this.LPC_order = SilkConstants.MIN_LPC_ORDER; + this.psNLSF_CB = SilkTables.silk_NLSF_CB_NB_MB; + } else { + this.LPC_order = SilkConstants.MAX_LPC_ORDER; + this.psNLSF_CB = SilkTables.silk_NLSF_CB_WB; + } + if (fs_kHz == 16) { + this.pitch_lag_low_bits_iCDF = SilkTables.silk_uniform8_iCDF; + } else if (fs_kHz == 12) { + this.pitch_lag_low_bits_iCDF = SilkTables.silk_uniform6_iCDF; + } else if (fs_kHz == 8) { + this.pitch_lag_low_bits_iCDF = SilkTables.silk_uniform4_iCDF; + } else { + /* unsupported sampling rate */ + Inlines.OpusAssert(false); + } + this.first_frame_after_reset = 1; + this.lagPrev = 100; + this.LastGainIndex = 10; + this.prevSignalType = SilkConstants.TYPE_NO_VOICE_ACTIVITY; + Arrays.MemSet(this.outBuf, (short) 0, SilkConstants.MAX_FRAME_LENGTH + 2 * SilkConstants.MAX_SUB_FRAME_LENGTH); + Arrays.MemSet(this.sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER); + } + + this.fs_kHz = fs_kHz; + this.frame_length = frame_length; + } + + /* Check that settings are valid */ + Inlines.OpusAssert(this.frame_length > 0 && this.frame_length <= SilkConstants.MAX_FRAME_LENGTH); + + return ret; + } + + /** + * ************* + */ + /* Decode frame */ + /** + * ************* + */ + int silk_decode_frame( + EntropyCoder psRangeDec, /* I/O Compressor data structure */ + short[] pOut, /* O Pointer to output speech frame */ + int pOut_ptr, + BoxedValueInt pN, /* O Pointer to size of output frame */ + int lostFlag, /* I 0: no loss, 1 loss, 2 decode fec */ + int condCoding /* I The type of conditional coding to use */ + ) { + SilkDecoderControl thisCtrl = new SilkDecoderControl(); + int L, mv_len, ret = 0; + + L = this.frame_length; + thisCtrl.LTP_scale_Q14 = 0; + + /* Safety checks */ + Inlines.OpusAssert(L > 0 && L <= SilkConstants.MAX_FRAME_LENGTH); + + if (lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL + || (lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR && this.LBRR_flags[this.nFramesDecoded] == 1)) { + short[] pulses = new short[(L + SilkConstants.SHELL_CODEC_FRAME_LENGTH - 1) & ~(SilkConstants.SHELL_CODEC_FRAME_LENGTH - 1)]; + /** + * ****************************************** + */ + /* Decode quantization indices of side info */ + /** + * ****************************************** + */ + DecodeIndices.silk_decode_indices(this, psRangeDec, this.nFramesDecoded, lostFlag, condCoding); + + /** + * ****************************************** + */ + /* Decode quantization indices of excitation */ + /** + * ****************************************** + */ + DecodePulses.silk_decode_pulses(psRangeDec, pulses, this.indices.signalType, + this.indices.quantOffsetType, this.frame_length); + + /** + * ***************************************** + */ + /* Decode parameters and pulse signal */ + /** + * ***************************************** + */ + DecodeParameters.silk_decode_parameters(this, thisCtrl, condCoding); + + /** + * ***************************************************** + */ + /* Run inverse NSQ */ + /** + * ***************************************************** + */ + DecodeCore.silk_decode_core(this, thisCtrl, pOut, pOut_ptr, pulses); + + /** + * ***************************************************** + */ + /* Update PLC state */ + /** + * ***************************************************** + */ + PLC.silk_PLC(this, thisCtrl, pOut, pOut_ptr, 0); + + this.lossCnt = 0; + this.prevSignalType = this.indices.signalType; + Inlines.OpusAssert(this.prevSignalType >= 0 && this.prevSignalType <= 2); + + /* A frame has been decoded without errors */ + this.first_frame_after_reset = 0; + } else { + /* Handle packet loss by extrapolation */ + PLC.silk_PLC(this, thisCtrl, pOut, pOut_ptr, 1); + } + + /** + * ********************** + */ + /* Update output buffer. */ + /** + * ********************** + */ + Inlines.OpusAssert(this.ltp_mem_length >= this.frame_length); + mv_len = this.ltp_mem_length - this.frame_length; + Arrays.MemMove(this.outBuf, this.frame_length, 0, mv_len); + System.arraycopy(pOut, pOut_ptr, this.outBuf, mv_len, this.frame_length); + + /** + * ********************************************* + */ + /* Comfort noise generation / estimation */ + /** + * ********************************************* + */ + CNG.silk_CNG(this, thisCtrl, pOut, pOut_ptr, L); + + /** + * ************************************************************* + */ + /* Ensure smooth connection of extrapolated and good frames */ + /** + * ************************************************************* + */ + PLC.silk_PLC_glue_frames(this, pOut, pOut_ptr, L); + + /* Update some decoder state variables */ + this.lagPrev = thisCtrl.pitchL[this.nb_subfr - 1]; + + /* Set output frame length */ + pN.Val = L; + + return ret; + } +} diff --git a/app/src/main/java/org/concentus/SilkChannelEncoder.java b/app/src/main/java/org/concentus/SilkChannelEncoder.java new file mode 100644 index 0000000000..f4ab7d8193 --- /dev/null +++ b/app/src/main/java/org/concentus/SilkChannelEncoder.java @@ -0,0 +1,1267 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Encoder state +/// +class SilkChannelEncoder { + + final int[] In_HP_State = new int[2]; + /* High pass filter state */ + int variable_HP_smth1_Q15 = 0; + /* State of first smoother */ + int variable_HP_smth2_Q15 = 0; + /* State of second smoother */ + final SilkLPState sLP = new SilkLPState(); + /* Low pass filter state */ + final SilkVADState sVAD = new SilkVADState(); + /* Voice activity detector state */ + final SilkNSQState sNSQ = new SilkNSQState(); + /* Noise Shape Quantizer State */ + final short[] prev_NLSFq_Q15 = new short[SilkConstants.MAX_LPC_ORDER]; + /* Previously quantized NLSF vector */ + int speech_activity_Q8 = 0; + /* Speech activity */ + int allow_bandwidth_switch = 0; + /* Flag indicating that switching of bandwidth is allowed */ + byte LBRRprevLastGainIndex = 0; + byte prevSignalType = 0; + int prevLag = 0; + int pitch_LPC_win_length = 0; + int max_pitch_lag = 0; + /* Highest possible pitch lag (samples) */ + int API_fs_Hz = 0; + /* API sampling frequency (Hz) */ + int prev_API_fs_Hz = 0; + /* Previous API sampling frequency (Hz) */ + int maxInternal_fs_Hz = 0; + /* Maximum sampling frequency (Hz) */ + int minInternal_fs_Hz = 0; + /* Minimum sampling frequency (Hz) */ + int desiredInternal_fs_Hz = 0; + /* Soft request for sampling frequency (Hz) */ + int fs_kHz = 0; + /* Internal sampling frequency (kHz) */ + int nb_subfr = 0; + /* Number of 5 ms subframes in a frame */ + int frame_length = 0; + /* Frame length (samples) */ + int subfr_length = 0; + /* Subframe length (samples) */ + int ltp_mem_length = 0; + /* Length of LTP memory */ + int la_pitch = 0; + /* Look-ahead for pitch analysis (samples) */ + int la_shape = 0; + /* Look-ahead for noise shape analysis (samples) */ + int shapeWinLength = 0; + /* Window length for noise shape analysis (samples) */ + int TargetRate_bps = 0; + /* Target bitrate (bps) */ + int PacketSize_ms = 0; + /* Number of milliseconds to put in each packet */ + int PacketLoss_perc = 0; + /* Packet loss rate measured by farend */ + int frameCounter = 0; + int Complexity = 0; + /* Complexity setting */ + int nStatesDelayedDecision = 0; + /* Number of states in delayed decision quantization */ + int useInterpolatedNLSFs = 0; + /* Flag for using NLSF interpolation */ + int shapingLPCOrder = 0; + /* Filter order for noise shaping filters */ + int predictLPCOrder = 0; + /* Filter order for prediction filters */ + int pitchEstimationComplexity = 0; + /* Complexity level for pitch estimator */ + int pitchEstimationLPCOrder = 0; + /* Whitening filter order for pitch estimator */ + int pitchEstimationThreshold_Q16 = 0; + /* Threshold for pitch estimator */ + int LTPQuantLowComplexity = 0; + /* Flag for low complexity LTP quantization */ + int mu_LTP_Q9 = 0; + /* Rate-distortion tradeoff in LTP quantization */ + int sum_log_gain_Q7 = 0; + /* Cumulative max prediction gain */ + int NLSF_MSVQ_Survivors = 0; + /* Number of survivors in NLSF MSVQ */ + int first_frame_after_reset = 0; + /* Flag for deactivating NLSF interpolation, pitch prediction */ + int controlled_since_last_payload = 0; + /* Flag for ensuring codec_control only runs once per packet */ + int warping_Q16 = 0; + /* Warping parameter for warped noise shaping */ + int useCBR = 0; + /* Flag to enable constant bitrate */ + int prefillFlag = 0; + /* Flag to indicate that only buffers are prefilled, no coding */ + short[] pitch_lag_low_bits_iCDF = null; + /* Pointer to iCDF table for low bits of pitch lag index */ + short[] pitch_contour_iCDF = null; + /* Pointer to iCDF table for pitch contour index */ + NLSFCodebook psNLSF_CB = null; + /* Pointer to NLSF codebook */ + final int[] input_quality_bands_Q15 = new int[SilkConstants.VAD_N_BANDS]; + int input_tilt_Q15 = 0; + int SNR_dB_Q7 = 0; + /* Quality setting */ + + final byte[] VAD_flags = new byte[SilkConstants.MAX_FRAMES_PER_PACKET]; + byte LBRR_flag = 0; + final int[] LBRR_flags = new int[SilkConstants.MAX_FRAMES_PER_PACKET]; + + final SideInfoIndices indices = new SideInfoIndices(); + final byte[] pulses = new byte[SilkConstants.MAX_FRAME_LENGTH]; + + /* Input/output buffering */ + final short[] inputBuf = new short[SilkConstants.MAX_FRAME_LENGTH + 2]; + /* Buffer containing input signal */ + int inputBufIx = 0; + int nFramesPerPacket = 0; + int nFramesEncoded = 0; + /* Number of frames analyzed in current packet */ + + int nChannelsAPI = 0; + int nChannelsInternal = 0; + int channelNb = 0; + + /* Parameters For LTP scaling Control */ + int frames_since_onset = 0; + + /* Specifically for entropy coding */ + int ec_prevSignalType = 0; + short ec_prevLagIndex = 0; + + final SilkResamplerState resampler_state = new SilkResamplerState(); + + /* DTX */ + int useDTX = 0; + /* Flag to enable DTX */ + int inDTX = 0; + /* Flag to signal DTX period */ + int noSpeechCounter = 0; + /* Counts concecutive nonactive frames, used by DTX */ + + /* Inband Low Bitrate Redundancy (LBRR) data */ + int useInBandFEC = 0; + /* Saves the API setting for query */ + int LBRR_enabled = 0; + /* Depends on useInBandFRC, bitrate and packet loss rate */ + int LBRR_GainIncreases = 0; + /* Gains increment for coding LBRR frames */ + final SideInfoIndices[] indices_LBRR = new SideInfoIndices[SilkConstants.MAX_FRAMES_PER_PACKET]; + final byte[][] pulses_LBRR = Arrays.InitTwoDimensionalArrayByte(SilkConstants.MAX_FRAMES_PER_PACKET, SilkConstants.MAX_FRAME_LENGTH); + + /* Noise shaping state */ + final SilkShapeState sShape = new SilkShapeState(); + + /* Prefilter State */ + final SilkPrefilterState sPrefilt = new SilkPrefilterState(); + + /* Buffer for find pitch and noise shape analysis */ + final short[] x_buf = new short[2 * SilkConstants.MAX_FRAME_LENGTH + SilkConstants.LA_SHAPE_MAX]; + + /* Normalized correlation from pitch lag estimator */ + int LTPCorr_Q15 = 0; + + SilkChannelEncoder() { + for (int c = 0; c < SilkConstants.MAX_FRAMES_PER_PACKET; c++) { + indices_LBRR[c] = new SideInfoIndices(); + } + } + + void Reset() { + Arrays.MemSet(In_HP_State, 0, 2); + variable_HP_smth1_Q15 = 0; + variable_HP_smth2_Q15 = 0; + sLP.Reset(); + sVAD.Reset(); + sNSQ.Reset(); + Arrays.MemSet(prev_NLSFq_Q15, (short) 0, SilkConstants.MAX_LPC_ORDER); + speech_activity_Q8 = 0; + allow_bandwidth_switch = 0; + LBRRprevLastGainIndex = 0; + prevSignalType = 0; + prevLag = 0; + pitch_LPC_win_length = 0; + max_pitch_lag = 0; + API_fs_Hz = 0; + prev_API_fs_Hz = 0; + maxInternal_fs_Hz = 0; + minInternal_fs_Hz = 0; + desiredInternal_fs_Hz = 0; + fs_kHz = 0; + nb_subfr = 0; + frame_length = 0; + subfr_length = 0; + ltp_mem_length = 0; + la_pitch = 0; + la_shape = 0; + shapeWinLength = 0; + TargetRate_bps = 0; + PacketSize_ms = 0; + PacketLoss_perc = 0; + frameCounter = 0; + Complexity = 0; + nStatesDelayedDecision = 0; + useInterpolatedNLSFs = 0; + shapingLPCOrder = 0; + predictLPCOrder = 0; + pitchEstimationComplexity = 0; + pitchEstimationLPCOrder = 0; + pitchEstimationThreshold_Q16 = 0; + LTPQuantLowComplexity = 0; + mu_LTP_Q9 = 0; + sum_log_gain_Q7 = 0; + NLSF_MSVQ_Survivors = 0; + first_frame_after_reset = 0; + controlled_since_last_payload = 0; + warping_Q16 = 0; + useCBR = 0; + prefillFlag = 0; + pitch_lag_low_bits_iCDF = null; + pitch_contour_iCDF = null; + psNLSF_CB = null; + Arrays.MemSet(input_quality_bands_Q15, 0, SilkConstants.VAD_N_BANDS); + input_tilt_Q15 = 0; + SNR_dB_Q7 = 0; + Arrays.MemSet(VAD_flags, (byte) 0, SilkConstants.MAX_FRAMES_PER_PACKET); + LBRR_flag = 0; + Arrays.MemSet(LBRR_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET); + indices.Reset(); + Arrays.MemSet(pulses, (byte) 0, SilkConstants.MAX_FRAME_LENGTH); + Arrays.MemSet(inputBuf, (short) 0, SilkConstants.MAX_FRAME_LENGTH + 2); + inputBufIx = 0; + nFramesPerPacket = 0; + nFramesEncoded = 0; + nChannelsAPI = 0; + nChannelsInternal = 0; + channelNb = 0; + frames_since_onset = 0; + ec_prevSignalType = 0; + ec_prevLagIndex = 0; + resampler_state.Reset(); + useDTX = 0; + inDTX = 0; + noSpeechCounter = 0; + useInBandFEC = 0; + LBRR_enabled = 0; + LBRR_GainIncreases = 0; + for (int c = 0; c < SilkConstants.MAX_FRAMES_PER_PACKET; c++) { + indices_LBRR[c].Reset(); + Arrays.MemSet(pulses_LBRR[c], (byte) 0, SilkConstants.MAX_FRAME_LENGTH); + } + sShape.Reset(); + sPrefilt.Reset(); + Arrays.MemSet(x_buf, (short) 0, 2 * SilkConstants.MAX_FRAME_LENGTH + SilkConstants.LA_SHAPE_MAX); + LTPCorr_Q15 = 0; + } + + /// + /// Control encoder + /// + /// I/O Pointer to Silk encoder state + /// I Control structure + /// I Target max bitrate (bps) + /// I Flag to allow switching audio bandwidth + /// I Channel number + /// + /// + int silk_control_encoder( + EncControlState encControl, + int TargetRate_bps, + int allow_bw_switch, + int channelNb, + int force_fs_kHz) { + int fs_kHz; + int ret = SilkError.SILK_NO_ERROR; + + this.useDTX = encControl.useDTX; + this.useCBR = encControl.useCBR; + this.API_fs_Hz = encControl.API_sampleRate; + this.maxInternal_fs_Hz = encControl.maxInternalSampleRate; + this.minInternal_fs_Hz = encControl.minInternalSampleRate; + this.desiredInternal_fs_Hz = encControl.desiredInternalSampleRate; + this.useInBandFEC = encControl.useInBandFEC; + this.nChannelsAPI = encControl.nChannelsAPI; + this.nChannelsInternal = encControl.nChannelsInternal; + this.allow_bandwidth_switch = allow_bw_switch; + this.channelNb = channelNb; + + if (this.controlled_since_last_payload != 0 && this.prefillFlag == 0) { + if (this.API_fs_Hz != this.prev_API_fs_Hz && this.fs_kHz > 0) { + /* Change in API sampling rate in the middle of encoding a packet */ + ret = silk_setup_resamplers(this.fs_kHz); + } + return ret; + } + + /* Beyond this point we know that there are no previously coded frames in the payload buffer */ + /** + * ***************************************** + */ + /* Determine sampling rate */ + /** + * ***************************************** + */ + fs_kHz = silk_control_audio_bandwidth(encControl); + if (force_fs_kHz != 0) { + fs_kHz = force_fs_kHz; + } + /** + * ***************************************** + */ + /* Prepare resampler and buffered data */ + /** + * ***************************************** + */ + ret = silk_setup_resamplers(fs_kHz); + + /** + * ***************************************** + */ + /* Set sampling frequency */ + /** + * ***************************************** + */ + ret = silk_setup_fs(fs_kHz, encControl.payloadSize_ms); + + /** + * ***************************************** + */ + /* Set encoding complexity */ + /** + * ***************************************** + */ + ret = silk_setup_complexity(encControl.complexity); + + /** + * ***************************************** + */ + /* Set packet loss rate measured by farend */ + /** + * ***************************************** + */ + this.PacketLoss_perc = encControl.packetLossPercentage; + + /** + * ***************************************** + */ + /* Set LBRR usage */ + /** + * ***************************************** + */ + ret = silk_setup_LBRR(TargetRate_bps); + + this.controlled_since_last_payload = 1; + + return ret; + } + + /// + /// + /// + /// I/O + /// I + /// + private int silk_setup_resamplers(int fs_kHz) { + int ret = 0; + + if (this.fs_kHz != fs_kHz || this.prev_API_fs_Hz != this.API_fs_Hz) { + if (this.fs_kHz == 0) { + /* Initialize the resampler for enc_API.c preparing resampling from API_fs_Hz to fs_kHz */ + ret += Resampler.silk_resampler_init(this.resampler_state, this.API_fs_Hz, fs_kHz * 1000, 1); + } else { + short[] x_buf_API_fs_Hz; + SilkResamplerState temp_resampler_state = null; + + int api_buf_samples; + int old_buf_samples; + int buf_length_ms; + + buf_length_ms = Inlines.silk_LSHIFT(this.nb_subfr * 5, 1) + SilkConstants.LA_SHAPE_MS; + old_buf_samples = buf_length_ms * this.fs_kHz; + + /* Initialize resampler for temporary resampling of x_buf data to API_fs_Hz */ + temp_resampler_state = new SilkResamplerState(); + ret += Resampler.silk_resampler_init(temp_resampler_state, Inlines.silk_SMULBB(this.fs_kHz, 1000), this.API_fs_Hz, 0); + + /* Calculate number of samples to temporarily upsample */ + api_buf_samples = buf_length_ms * Inlines.silk_DIV32_16(this.API_fs_Hz, 1000); + + /* Temporary resampling of x_buf data to API_fs_Hz */ + x_buf_API_fs_Hz = new short[api_buf_samples]; + ret += Resampler.silk_resampler(temp_resampler_state, x_buf_API_fs_Hz, 0, this.x_buf, 0, old_buf_samples); + + /* Initialize the resampler for enc_API.c preparing resampling from API_fs_Hz to fs_kHz */ + ret += Resampler.silk_resampler_init(this.resampler_state, this.API_fs_Hz, Inlines.silk_SMULBB(fs_kHz, 1000), 1); + + /* Correct resampler state by resampling buffered data from API_fs_Hz to fs_kHz */ + ret += Resampler.silk_resampler(this.resampler_state, this.x_buf, 0, x_buf_API_fs_Hz, 0, api_buf_samples); + } + } + + this.prev_API_fs_Hz = this.API_fs_Hz; + + return ret; + } + + /// + /// + /// + /// I/O + /// I + /// I + /// + private int silk_setup_fs( + int fs_kHz, + int PacketSize_ms) { + int ret = SilkError.SILK_NO_ERROR; + + /* Set packet size */ + if (PacketSize_ms != this.PacketSize_ms) { + if ((PacketSize_ms != 10) + && (PacketSize_ms != 20) + && (PacketSize_ms != 40) + && (PacketSize_ms != 60)) { + ret = SilkError.SILK_ENC_PACKET_SIZE_NOT_SUPPORTED; + } + if (PacketSize_ms <= 10) { + this.nFramesPerPacket = 1; + this.nb_subfr = PacketSize_ms == 10 ? 2 : 1; + this.frame_length = Inlines.silk_SMULBB(PacketSize_ms, fs_kHz); + this.pitch_LPC_win_length = Inlines.silk_SMULBB(SilkConstants.FIND_PITCH_LPC_WIN_MS_2_SF, fs_kHz); + if (this.fs_kHz == 8) { + this.pitch_contour_iCDF = SilkTables.silk_pitch_contour_10_ms_NB_iCDF; + } else { + this.pitch_contour_iCDF = SilkTables.silk_pitch_contour_10_ms_iCDF; + } + } else { + this.nFramesPerPacket = Inlines.silk_DIV32_16(PacketSize_ms, SilkConstants.MAX_FRAME_LENGTH_MS); + this.nb_subfr = SilkConstants.MAX_NB_SUBFR; + this.frame_length = Inlines.silk_SMULBB(20, fs_kHz); + this.pitch_LPC_win_length = Inlines.silk_SMULBB(SilkConstants.FIND_PITCH_LPC_WIN_MS, fs_kHz); + if (this.fs_kHz == 8) { + this.pitch_contour_iCDF = SilkTables.silk_pitch_contour_NB_iCDF; + } else { + this.pitch_contour_iCDF = SilkTables.silk_pitch_contour_iCDF; + } + } + this.PacketSize_ms = PacketSize_ms; + this.TargetRate_bps = 0; + /* trigger new SNR computation */ + } + + /* Set sampling frequency */ + Inlines.OpusAssert(fs_kHz == 8 || fs_kHz == 12 || fs_kHz == 16); + Inlines.OpusAssert(this.nb_subfr == 2 || this.nb_subfr == 4); + if (this.fs_kHz != fs_kHz) { + /* reset part of the state */ + this.sShape.Reset(); + this.sPrefilt.Reset(); + this.sNSQ.Reset(); + Arrays.MemSet(this.prev_NLSFq_Q15, (short) 0, SilkConstants.MAX_LPC_ORDER); + Arrays.MemSet(this.sLP.In_LP_State, 0, 2); + this.inputBufIx = 0; + this.nFramesEncoded = 0; + this.TargetRate_bps = 0; + /* trigger new SNR computation */ + + /* Initialize non-zero parameters */ + this.prevLag = 100; + this.first_frame_after_reset = 1; + this.sPrefilt.lagPrev = 100; + this.sShape.LastGainIndex = 10; + this.sNSQ.lagPrev = 100; + this.sNSQ.prev_gain_Q16 = 65536; + this.prevSignalType = SilkConstants.TYPE_NO_VOICE_ACTIVITY; + + this.fs_kHz = fs_kHz; + if (this.fs_kHz == 8) { + if (this.nb_subfr == SilkConstants.MAX_NB_SUBFR) { + this.pitch_contour_iCDF = SilkTables.silk_pitch_contour_NB_iCDF; + } else { + this.pitch_contour_iCDF = SilkTables.silk_pitch_contour_10_ms_NB_iCDF; + } + } else if (this.nb_subfr == SilkConstants.MAX_NB_SUBFR) { + this.pitch_contour_iCDF = SilkTables.silk_pitch_contour_iCDF; + } else { + this.pitch_contour_iCDF = SilkTables.silk_pitch_contour_10_ms_iCDF; + } + + if (this.fs_kHz == 8 || this.fs_kHz == 12) { + this.predictLPCOrder = SilkConstants.MIN_LPC_ORDER; + this.psNLSF_CB = SilkTables.silk_NLSF_CB_NB_MB; + } else { + this.predictLPCOrder = SilkConstants.MAX_LPC_ORDER; + this.psNLSF_CB = SilkTables.silk_NLSF_CB_WB; + } + + this.subfr_length = SilkConstants.SUB_FRAME_LENGTH_MS * fs_kHz; + this.frame_length = Inlines.silk_SMULBB(this.subfr_length, this.nb_subfr); + this.ltp_mem_length = Inlines.silk_SMULBB(SilkConstants.LTP_MEM_LENGTH_MS, fs_kHz); + this.la_pitch = Inlines.silk_SMULBB(SilkConstants.LA_PITCH_MS, fs_kHz); + this.max_pitch_lag = Inlines.silk_SMULBB(18, fs_kHz); + + if (this.nb_subfr == SilkConstants.MAX_NB_SUBFR) { + this.pitch_LPC_win_length = Inlines.silk_SMULBB(SilkConstants.FIND_PITCH_LPC_WIN_MS, fs_kHz); + } else { + this.pitch_LPC_win_length = Inlines.silk_SMULBB(SilkConstants.FIND_PITCH_LPC_WIN_MS_2_SF, fs_kHz); + } + + if (this.fs_kHz == 16) { + this.mu_LTP_Q9 = ((int) ((TuningParameters.MU_LTP_QUANT_WB) * ((long) 1 << (9)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.MU_LTP_QUANT_WB, 9)*/; + this.pitch_lag_low_bits_iCDF = SilkTables.silk_uniform8_iCDF; + } else if (this.fs_kHz == 12) { + this.mu_LTP_Q9 = ((int) ((TuningParameters.MU_LTP_QUANT_MB) * ((long) 1 << (9)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.MU_LTP_QUANT_MB, 9)*/; + this.pitch_lag_low_bits_iCDF = SilkTables.silk_uniform6_iCDF; + } else { + this.mu_LTP_Q9 = ((int) ((TuningParameters.MU_LTP_QUANT_NB) * ((long) 1 << (9)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.MU_LTP_QUANT_NB, 9)*/; + this.pitch_lag_low_bits_iCDF = SilkTables.silk_uniform4_iCDF; + } + } + + /* Check that settings are valid */ + Inlines.OpusAssert((this.subfr_length * this.nb_subfr) == this.frame_length); + + return ret; + } + + /// + /// + /// + /// I/O + /// O + /// + private int silk_setup_complexity(int Complexity) { + int ret = 0; + + /* Set encoding complexity */ + Inlines.OpusAssert(Complexity >= 0 && Complexity <= 10); + if (Complexity < 2) { + this.pitchEstimationComplexity = SilkConstants.SILK_PE_MIN_COMPLEX; + this.pitchEstimationThreshold_Q16 = ((int) ((0.8f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.8f, 16)*/; + this.pitchEstimationLPCOrder = 6; + this.shapingLPCOrder = 8; + this.la_shape = 3 * this.fs_kHz; + this.nStatesDelayedDecision = 1; + this.useInterpolatedNLSFs = 0; + this.LTPQuantLowComplexity = 1; + this.NLSF_MSVQ_Survivors = 2; + this.warping_Q16 = 0; + } else if (Complexity < 4) { + this.pitchEstimationComplexity = SilkConstants.SILK_PE_MID_COMPLEX; + this.pitchEstimationThreshold_Q16 = ((int) ((0.76f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.76f, 16)*/; + this.pitchEstimationLPCOrder = 8; + this.shapingLPCOrder = 10; + this.la_shape = 5 * this.fs_kHz; + this.nStatesDelayedDecision = 1; + this.useInterpolatedNLSFs = 0; + this.LTPQuantLowComplexity = 0; + this.NLSF_MSVQ_Survivors = 4; + this.warping_Q16 = 0; + } else if (Complexity < 6) { + this.pitchEstimationComplexity = SilkConstants.SILK_PE_MID_COMPLEX; + this.pitchEstimationThreshold_Q16 = ((int) ((0.74f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.74f, 16)*/; + this.pitchEstimationLPCOrder = 10; + this.shapingLPCOrder = 12; + this.la_shape = 5 * this.fs_kHz; + this.nStatesDelayedDecision = 2; + this.useInterpolatedNLSFs = 1; + this.LTPQuantLowComplexity = 0; + this.NLSF_MSVQ_Survivors = 8; + this.warping_Q16 = this.fs_kHz * ((int) ((TuningParameters.WARPING_MULTIPLIER) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.WARPING_MULTIPLIER, 16)*/; + } else if (Complexity < 8) { + this.pitchEstimationComplexity = SilkConstants.SILK_PE_MID_COMPLEX; + this.pitchEstimationThreshold_Q16 = ((int) ((0.72f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.72f, 16)*/; + this.pitchEstimationLPCOrder = 12; + this.shapingLPCOrder = 14; + this.la_shape = 5 * this.fs_kHz; + this.nStatesDelayedDecision = 3; + this.useInterpolatedNLSFs = 1; + this.LTPQuantLowComplexity = 0; + this.NLSF_MSVQ_Survivors = 16; + this.warping_Q16 = this.fs_kHz * ((int) ((TuningParameters.WARPING_MULTIPLIER) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.WARPING_MULTIPLIER, 16)*/; + } else { + this.pitchEstimationComplexity = SilkConstants.SILK_PE_MAX_COMPLEX; + this.pitchEstimationThreshold_Q16 = ((int) ((0.7f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.7f, 16)*/; + this.pitchEstimationLPCOrder = 16; + this.shapingLPCOrder = 16; + this.la_shape = 5 * this.fs_kHz; + this.nStatesDelayedDecision = SilkConstants.MAX_DEL_DEC_STATES; + this.useInterpolatedNLSFs = 1; + this.LTPQuantLowComplexity = 0; + this.NLSF_MSVQ_Survivors = 32; + this.warping_Q16 = this.fs_kHz * ((int) ((TuningParameters.WARPING_MULTIPLIER) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.WARPING_MULTIPLIER, 16)*/; + } + + /* Do not allow higher pitch estimation LPC order than predict LPC order */ + this.pitchEstimationLPCOrder = Inlines.silk_min_int(this.pitchEstimationLPCOrder, this.predictLPCOrder); + this.shapeWinLength = SilkConstants.SUB_FRAME_LENGTH_MS * this.fs_kHz + 2 * this.la_shape; + this.Complexity = Complexity; + + Inlines.OpusAssert(this.pitchEstimationLPCOrder <= SilkConstants.MAX_FIND_PITCH_LPC_ORDER); + Inlines.OpusAssert(this.shapingLPCOrder <= SilkConstants.MAX_SHAPE_LPC_ORDER); + Inlines.OpusAssert(this.nStatesDelayedDecision <= SilkConstants.MAX_DEL_DEC_STATES); + Inlines.OpusAssert(this.warping_Q16 <= 32767); + Inlines.OpusAssert(this.la_shape <= SilkConstants.LA_SHAPE_MAX); + Inlines.OpusAssert(this.shapeWinLength <= SilkConstants.SHAPE_LPC_WIN_MAX); + Inlines.OpusAssert(this.NLSF_MSVQ_Survivors <= SilkConstants.NLSF_VQ_MAX_SURVIVORS); + + return ret; + } + + /// + /// + /// + /// I/O + /// I + /// + private int silk_setup_LBRR(int TargetRate_bps) { + int LBRR_in_previous_packet; + int ret = SilkError.SILK_NO_ERROR; + int LBRR_rate_thres_bps; + + LBRR_in_previous_packet = this.LBRR_enabled; + this.LBRR_enabled = 0; + if (this.useInBandFEC != 0 && this.PacketLoss_perc > 0) { + if (this.fs_kHz == 8) { + LBRR_rate_thres_bps = SilkConstants.LBRR_NB_MIN_RATE_BPS; + } else if (this.fs_kHz == 12) { + LBRR_rate_thres_bps = SilkConstants.LBRR_MB_MIN_RATE_BPS; + } else { + LBRR_rate_thres_bps = SilkConstants.LBRR_WB_MIN_RATE_BPS; + } + + LBRR_rate_thres_bps = Inlines.silk_SMULWB(Inlines.silk_MUL(LBRR_rate_thres_bps, 125 - Inlines.silk_min(this.PacketLoss_perc, 25)), ((int) ((0.01f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.01f, 16)*/); + + if (TargetRate_bps > LBRR_rate_thres_bps) { + /* Set gain increase for coding LBRR excitation */ + if (LBRR_in_previous_packet == 0) { + /* Previous packet did not have LBRR, and was therefore coded at a higher bitrate */ + this.LBRR_GainIncreases = 7; + } else { + this.LBRR_GainIncreases = Inlines.silk_max_int(7 - Inlines.silk_SMULWB((int) this.PacketLoss_perc, ((int) ((0.4f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.4f, 16)*/), 2); + } + this.LBRR_enabled = 1; + } + } + + return ret; + } + + /// + /// Control sampling rate + /// + /// I/O Pointer to Silk encoder state + /// I Control structure + /// + int silk_control_audio_bandwidth(EncControlState encControl) { + int fs_kHz; + int fs_Hz; + + fs_kHz = this.fs_kHz; + fs_Hz = Inlines.silk_SMULBB(fs_kHz, 1000); + + if (fs_Hz == 0) { + /* Encoder has just been initialized */ + fs_Hz = Inlines.silk_min(this.desiredInternal_fs_Hz, this.API_fs_Hz); + fs_kHz = Inlines.silk_DIV32_16(fs_Hz, 1000); + } else if (fs_Hz > this.API_fs_Hz || fs_Hz > this.maxInternal_fs_Hz || fs_Hz < this.minInternal_fs_Hz) { + /* Make sure rate is not higher than external rate or maximum allowed, or lower than minimum allowed */ + fs_Hz = this.API_fs_Hz; + fs_Hz = Inlines.silk_min(fs_Hz, this.maxInternal_fs_Hz); + fs_Hz = Inlines.silk_max(fs_Hz, this.minInternal_fs_Hz); + fs_kHz = Inlines.silk_DIV32_16(fs_Hz, 1000); + } else { + /* State machine for the sampling rate switching */ + if (this.sLP.transition_frame_no >= SilkConstants.TRANSITION_FRAMES) { + /* Stop transition phase */ + this.sLP.mode = 0; + } + + if (this.allow_bandwidth_switch != 0 || encControl.opusCanSwitch != 0) { + /* Check if we should switch down */ + if (Inlines.silk_SMULBB(this.fs_kHz, 1000) > this.desiredInternal_fs_Hz) { + /* Switch down */ + if (this.sLP.mode == 0) { + /* New transition */ + this.sLP.transition_frame_no = SilkConstants.TRANSITION_FRAMES; + + /* Reset transition filter state */ + Arrays.MemSet(this.sLP.In_LP_State, 0, 2); + } + + if (encControl.opusCanSwitch != 0) { + /* Stop transition phase */ + this.sLP.mode = 0; + + /* Switch to a lower sample frequency */ + fs_kHz = this.fs_kHz == 16 ? 12 : 8; + } else if (this.sLP.transition_frame_no <= 0) { + encControl.switchReady = 1; + /* Make room for redundancy */ + encControl.maxBits -= encControl.maxBits * 5 / (encControl.payloadSize_ms + 5); + } else { + /* Direction: down (at double speed) */ + this.sLP.mode = -2; + } + } else /* Check if we should switch up */ if (Inlines.silk_SMULBB(this.fs_kHz, 1000) < this.desiredInternal_fs_Hz) { + /* Switch up */ + if (encControl.opusCanSwitch != 0) { + /* Switch to a higher sample frequency */ + fs_kHz = this.fs_kHz == 8 ? 12 : 16; + + /* New transition */ + this.sLP.transition_frame_no = 0; + + /* Reset transition filter state */ + Arrays.MemSet(this.sLP.In_LP_State, 0, 2); + + /* Direction: up */ + this.sLP.mode = 1; + } else if (this.sLP.mode == 0) { + encControl.switchReady = 1; + /* Make room for redundancy */ + encControl.maxBits -= encControl.maxBits * 5 / (encControl.payloadSize_ms + 5); + } else { + /* Direction: up */ + this.sLP.mode = 1; + } + } else if (this.sLP.mode < 0) { + this.sLP.mode = 1; + } + } + } + + return fs_kHz; + } + + /* Control SNR of residual quantizer */ + int silk_control_SNR( + int TargetRate_bps /* I Target max bitrate (bps) */ + ) { + int k, ret = SilkError.SILK_NO_ERROR; + int frac_Q6; + int[] rateTable; + + /* Set bitrate/coding quality */ + TargetRate_bps = Inlines.silk_LIMIT(TargetRate_bps, SilkConstants.MIN_TARGET_RATE_BPS, SilkConstants.MAX_TARGET_RATE_BPS); + if (TargetRate_bps != this.TargetRate_bps) { + this.TargetRate_bps = TargetRate_bps; + + /* If new TargetRate_bps, translate to SNR_dB value */ + if (this.fs_kHz == 8) { + rateTable = SilkTables.silk_TargetRate_table_NB; + } else if (this.fs_kHz == 12) { + rateTable = SilkTables.silk_TargetRate_table_MB; + } else { + rateTable = SilkTables.silk_TargetRate_table_WB; + } + + /* Reduce bitrate for 10 ms modes in these calculations */ + if (this.nb_subfr == 2) { + TargetRate_bps -= TuningParameters.REDUCE_BITRATE_10_MS_BPS; + } + + /* Find bitrate interval in table and interpolate */ + for (k = 1; k < SilkConstants.TARGET_RATE_TAB_SZ; k++) { + if (TargetRate_bps <= rateTable[k]) { + frac_Q6 = Inlines.silk_DIV32(Inlines.silk_LSHIFT(TargetRate_bps - rateTable[k - 1], 6), + rateTable[k] - rateTable[k - 1]); + this.SNR_dB_Q7 = Inlines.silk_LSHIFT(SilkTables.silk_SNR_table_Q1[k - 1], 6) + Inlines.silk_MUL(frac_Q6, SilkTables.silk_SNR_table_Q1[k] - SilkTables.silk_SNR_table_Q1[k - 1]); + break; + } + } + } + + return ret; + } + + void silk_encode_do_VAD() { + /** + * ************************* + */ + /* Voice Activity Detection */ + /** + * ************************* + */ + VoiceActivityDetection.silk_VAD_GetSA_Q8(this, this.inputBuf, 1); + + /** + * *********************************************** + */ + /* Convert speech activity into VAD and DTX flags */ + /** + * *********************************************** + */ + if (this.speech_activity_Q8 < ((int) ((TuningParameters.SPEECH_ACTIVITY_DTX_THRES) * ((long) 1 << (8)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SPEECH_ACTIVITY_DTX_THRES, 8)*/) { + this.indices.signalType = SilkConstants.TYPE_NO_VOICE_ACTIVITY; + this.noSpeechCounter++; + if (this.noSpeechCounter < SilkConstants.NB_SPEECH_FRAMES_BEFORE_DTX) { + this.inDTX = 0; + } else if (this.noSpeechCounter > SilkConstants.MAX_CONSECUTIVE_DTX + SilkConstants.NB_SPEECH_FRAMES_BEFORE_DTX) { + this.noSpeechCounter = SilkConstants.NB_SPEECH_FRAMES_BEFORE_DTX; + this.inDTX = 0; + } + this.VAD_flags[this.nFramesEncoded] = 0; + } else { + this.noSpeechCounter = 0; + this.inDTX = 0; + this.indices.signalType = SilkConstants.TYPE_UNVOICED; + this.VAD_flags[this.nFramesEncoded] = 1; + } + } + + /** + * ************* + */ + /* Encode frame */ + /** + * ************* + */ + int silk_encode_frame( + BoxedValueInt pnBytesOut, /* O Pointer to number of payload bytes; */ + EntropyCoder psRangeEnc, /* I/O compressor data structure */ + int condCoding, /* I The type of conditional coding to use */ + int maxBits, /* I If > 0: maximum number of output bits */ + int useCBR /* I Flag to force constant-bitrate operation */ + ) { + SilkEncoderControl sEncCtrl = new SilkEncoderControl(); + int i, iter, maxIter, found_upper, found_lower, ret = 0; + int x_frame; + EntropyCoder sRangeEnc_copy = new EntropyCoder(); + EntropyCoder sRangeEnc_copy2 = new EntropyCoder(); + SilkNSQState sNSQ_copy = new SilkNSQState(); + SilkNSQState sNSQ_copy2 = new SilkNSQState(); + int nBits, nBits_lower, nBits_upper, gainMult_lower, gainMult_upper; + int gainsID, gainsID_lower, gainsID_upper; + short gainMult_Q8; + short ec_prevLagIndex_copy; + int ec_prevSignalType_copy; + byte LastGainIndex_copy2; + byte seed_copy; + + /* This is totally unnecessary but many compilers (including gcc) are too dumb to realise it */ + LastGainIndex_copy2 = 0; + nBits_lower = nBits_upper = gainMult_lower = gainMult_upper = 0; + + this.indices.Seed = (byte) (this.frameCounter++ & 3); + + /** + * *********************************************************** + */ + /* Set up Input Pointers, and insert frame in input buffer */ + /** + * ********************************************************** + */ + /* start of frame to encode */ + x_frame = this.ltp_mem_length; + + /** + * ************************************ + */ + /* Ensure smooth bandwidth transitions */ + /** + * ************************************ + */ + this.sLP.silk_LP_variable_cutoff(this.inputBuf, 1, this.frame_length); + + /** + * **************************************** + */ + /* Copy new frame to front of input buffer */ + /** + * **************************************** + */ + System.arraycopy(this.inputBuf, 1, this.x_buf, x_frame + SilkConstants.LA_SHAPE_MS * this.fs_kHz, this.frame_length); + + if (this.prefillFlag == 0) { + int[] xfw_Q3; + short[] res_pitch; + byte[] ec_buf_copy; + int res_pitch_frame; + + res_pitch = new short[this.la_pitch + this.frame_length + this.ltp_mem_length]; + /* start of pitch LPC residual frame */ + res_pitch_frame = this.ltp_mem_length; + + /** + * ************************************** + */ + /* Find pitch lags, initial LPC analysis */ + /** + * ************************************** + */ + FindPitchLags.silk_find_pitch_lags(this, sEncCtrl, res_pitch, this.x_buf, x_frame); + + /** + * ********************* + */ + /* Noise shape analysis */ + /** + * ********************* + */ + NoiseShapeAnalysis.silk_noise_shape_analysis(this, sEncCtrl, res_pitch, res_pitch_frame, this.x_buf, x_frame); + + /** + * ************************************************ + */ + /* Find linear prediction coefficients (LPC + LTP) */ + /** + * ************************************************ + */ + FindPredCoefs.silk_find_pred_coefs(this, sEncCtrl, res_pitch, this.x_buf, x_frame, condCoding); + + /** + * ************************************* + */ + /* Process gains */ + /** + * ************************************* + */ + ProcessGains.silk_process_gains(this, sEncCtrl, condCoding); + + /** + * ************************************** + */ + /* Prefiltering for noise shaper */ + /** + * ************************************** + */ + xfw_Q3 = new int[this.frame_length]; + Filters.silk_prefilter(this, sEncCtrl, xfw_Q3, this.x_buf, x_frame); + + /** + * ************************************* + */ + /* Low Bitrate Redundant Encoding */ + /** + * ************************************* + */ + silk_LBRR_encode(sEncCtrl, xfw_Q3, condCoding); + + /* Loop over quantizer and entropy coding to control bitrate */ + maxIter = 6; + gainMult_Q8 = (short) (((int) ((1) * ((long) 1 << (8)) + 0.5))/*Inlines.SILK_CONST(1, 8)*/); + found_lower = 0; + found_upper = 0; + gainsID = GainQuantization.silk_gains_ID(this.indices.GainsIndices, this.nb_subfr); + gainsID_lower = -1; + gainsID_upper = -1; + /* Copy part of the input state */ + sRangeEnc_copy.Assign(psRangeEnc); + sNSQ_copy.Assign(this.sNSQ); + seed_copy = this.indices.Seed; + ec_prevLagIndex_copy = this.ec_prevLagIndex; + ec_prevSignalType_copy = this.ec_prevSignalType; + ec_buf_copy = new byte[1275]; // fixme: this size might be optimized to the actual size + for (iter = 0;; iter++) { + if (gainsID == gainsID_lower) { + nBits = nBits_lower; + } else if (gainsID == gainsID_upper) { + nBits = nBits_upper; + } else { + /* Restore part of the input state */ + if (iter > 0) { + psRangeEnc.Assign(sRangeEnc_copy); + this.sNSQ.Assign(sNSQ_copy); + this.indices.Seed = seed_copy; + this.ec_prevLagIndex = ec_prevLagIndex_copy; + this.ec_prevSignalType = ec_prevSignalType_copy; + } + + /** + * ************************************** + */ + /* Noise shaping quantization */ + /** + * ************************************** + */ + if (this.nStatesDelayedDecision > 1 || this.warping_Q16 > 0) { + sNSQ.silk_NSQ_del_dec( + this, + this.indices, + xfw_Q3, + pulses, + sEncCtrl.PredCoef_Q12, + sEncCtrl.LTPCoef_Q14, + sEncCtrl.AR2_Q13, + sEncCtrl.HarmShapeGain_Q14, + sEncCtrl.Tilt_Q14, + sEncCtrl.LF_shp_Q14, + sEncCtrl.Gains_Q16, + sEncCtrl.pitchL, + sEncCtrl.Lambda_Q10, + sEncCtrl.LTP_scale_Q14); + } else { + sNSQ.silk_NSQ( + this, + this.indices, + xfw_Q3, + pulses, + sEncCtrl.PredCoef_Q12, + sEncCtrl.LTPCoef_Q14, + sEncCtrl.AR2_Q13, + sEncCtrl.HarmShapeGain_Q14, + sEncCtrl.Tilt_Q14, + sEncCtrl.LF_shp_Q14, + sEncCtrl.Gains_Q16, + sEncCtrl.pitchL, + sEncCtrl.Lambda_Q10, + sEncCtrl.LTP_scale_Q14); + } + + /** + * ************************************* + */ + /* Encode Parameters */ + /** + * ************************************* + */ + EncodeIndices.silk_encode_indices(this, psRangeEnc, this.nFramesEncoded, 0, condCoding); + + /** + * ************************************* + */ + /* Encode Excitation Signal */ + /** + * ************************************* + */ + EncodePulses.silk_encode_pulses(psRangeEnc, this.indices.signalType, this.indices.quantOffsetType, + this.pulses, this.frame_length); + + nBits = psRangeEnc.tell(); + + if (useCBR == 0 && iter == 0 && nBits <= maxBits) { + break; + } + } + + if (iter == maxIter) { + if (found_lower != 0 && (gainsID == gainsID_lower || nBits > maxBits)) { + /* Restore output state from earlier iteration that did meet the bitrate budget */ + psRangeEnc.Assign(sRangeEnc_copy2); + Inlines.OpusAssert(sRangeEnc_copy2.offs <= 1275); + psRangeEnc.write_buffer(ec_buf_copy, 0, 0, (int) sRangeEnc_copy2.offs); + this.sNSQ.Assign(sNSQ_copy2); + this.sShape.LastGainIndex = LastGainIndex_copy2; + } + break; + } + + if (nBits > maxBits) { + if (found_lower == 0 && iter >= 2) { + /* Adjust the quantizer's rate/distortion tradeoff and discard previous "upper" results */ + sEncCtrl.Lambda_Q10 = Inlines.silk_ADD_RSHIFT32(sEncCtrl.Lambda_Q10, sEncCtrl.Lambda_Q10, 1); + found_upper = 0; + gainsID_upper = -1; + } else { + found_upper = 1; + nBits_upper = nBits; + gainMult_upper = gainMult_Q8; + gainsID_upper = gainsID; + } + } else if (nBits < maxBits - 5) { + found_lower = 1; + nBits_lower = nBits; + gainMult_lower = gainMult_Q8; + if (gainsID != gainsID_lower) { + gainsID_lower = gainsID; + /* Copy part of the output state */ + sRangeEnc_copy2.Assign(psRangeEnc); + Inlines.OpusAssert(psRangeEnc.offs <= 1275); + System.arraycopy(psRangeEnc.get_buffer(), 0, ec_buf_copy, 0, (int) psRangeEnc.offs); + sNSQ_copy2.Assign(this.sNSQ); + LastGainIndex_copy2 = this.sShape.LastGainIndex; + } + } else { + /* Within 5 bits of budget: close enough */ + break; + } + + if ((found_lower & found_upper) == 0) { + /* Adjust gain according to high-rate rate/distortion curve */ + int gain_factor_Q16; + gain_factor_Q16 = Inlines.silk_log2lin(Inlines.silk_LSHIFT(nBits - maxBits, 7) / this.frame_length + ((int) ((16) * ((long) 1 << (7)) + 0.5))/*Inlines.SILK_CONST(16, 7)*/); + gain_factor_Q16 = Inlines.silk_min_32(gain_factor_Q16, ((int) ((2) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(2, 16)*/); + if (nBits > maxBits) { + gain_factor_Q16 = Inlines.silk_max_32(gain_factor_Q16, ((int) ((1.3f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.3f, 16)*/); + } + + gainMult_Q8 = (short) (Inlines.silk_SMULWB(gain_factor_Q16, (int) gainMult_Q8)); + } else { + /* Adjust gain by interpolating */ + gainMult_Q8 = (short) (gainMult_lower + Inlines.silk_DIV32_16(Inlines.silk_MUL(gainMult_upper - gainMult_lower, maxBits - nBits_lower), nBits_upper - nBits_lower)); + /* New gain multplier must be between 25% and 75% of old range (note that gainMult_upper < gainMult_lower) */ + if (gainMult_Q8 > Inlines.silk_ADD_RSHIFT32(gainMult_lower, gainMult_upper - gainMult_lower, 2)) { + gainMult_Q8 = (short) (Inlines.silk_ADD_RSHIFT32(gainMult_lower, gainMult_upper - gainMult_lower, 2)); + } else if (gainMult_Q8 < Inlines.silk_SUB_RSHIFT32(gainMult_upper, gainMult_upper - gainMult_lower, 2)) { + gainMult_Q8 = (short) (Inlines.silk_SUB_RSHIFT32(gainMult_upper, gainMult_upper - gainMult_lower, 2)); + } + } + + for (i = 0; i < this.nb_subfr; i++) { + sEncCtrl.Gains_Q16[i] = Inlines.silk_LSHIFT_SAT32(Inlines.silk_SMULWB(sEncCtrl.GainsUnq_Q16[i], gainMult_Q8), 8); + } + + /* Quantize gains */ + this.sShape.LastGainIndex = sEncCtrl.lastGainIndexPrev; + BoxedValueByte boxed_gainIndex = new BoxedValueByte(this.sShape.LastGainIndex); + GainQuantization.silk_gains_quant(this.indices.GainsIndices, sEncCtrl.Gains_Q16, + boxed_gainIndex, condCoding == SilkConstants.CODE_CONDITIONALLY ? 1 : 0, this.nb_subfr); + this.sShape.LastGainIndex = boxed_gainIndex.Val; + + /* Unique identifier of gains vector */ + gainsID = GainQuantization.silk_gains_ID(this.indices.GainsIndices, this.nb_subfr); + } + } + + /* Update input buffer */ + Arrays.MemMove(this.x_buf, this.frame_length, 0, this.ltp_mem_length + SilkConstants.LA_SHAPE_MS * this.fs_kHz); + + /* Exit without entropy coding */ + if (this.prefillFlag != 0) { + /* No payload */ + pnBytesOut.Val = 0; + + return ret; + } + + /* Parameters needed for next frame */ + this.prevLag = sEncCtrl.pitchL[this.nb_subfr - 1]; + this.prevSignalType = this.indices.signalType; + + /** + * ************************************* + */ + /* Finalize payload */ + /** + * ************************************* + */ + this.first_frame_after_reset = 0; + /* Payload size */ + pnBytesOut.Val = Inlines.silk_RSHIFT(psRangeEnc.tell() + 7, 3); + + return ret; + } + + /* Low-Bitrate Redundancy (LBRR) encoding. Reuse all parameters but encode excitation at lower bitrate */ + void silk_LBRR_encode( + SilkEncoderControl thisCtrl, /* I/O Pointer to Silk FIX encoder control struct */ + int[] xfw_Q3, /* I Input signal */ + int condCoding /* I The type of conditional coding used so far for this frame */ + ) { + int[] TempGains_Q16 = new int[/*SilkConstants.MAX_NB_SUBFR*/this.nb_subfr]; + SideInfoIndices psIndices_LBRR = this.indices_LBRR[this.nFramesEncoded]; + SilkNSQState sNSQ_LBRR = new SilkNSQState(); + + /** + * **************************************** + */ + /* Control use of inband LBRR */ + /** + * **************************************** + */ + if (this.LBRR_enabled != 0 && this.speech_activity_Q8 > ((int) ((TuningParameters.LBRR_SPEECH_ACTIVITY_THRES) * ((long) 1 << (8)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LBRR_SPEECH_ACTIVITY_THRES, 8)*/) { + this.LBRR_flags[this.nFramesEncoded] = 1; + + /* Copy noise shaping quantizer state and quantization indices from regular encoding */ + sNSQ_LBRR.Assign(this.sNSQ); + psIndices_LBRR.Assign(this.indices); + + /* Save original gains */ + System.arraycopy(thisCtrl.Gains_Q16, 0, TempGains_Q16, 0, this.nb_subfr); + + if (this.nFramesEncoded == 0 || this.LBRR_flags[this.nFramesEncoded - 1] == 0) { + /* First frame in packet or previous frame not LBRR coded */ + this.LBRRprevLastGainIndex = this.sShape.LastGainIndex; + + /* Increase Gains to get target LBRR rate */ + psIndices_LBRR.GainsIndices[0] = (byte) (psIndices_LBRR.GainsIndices[0] + this.LBRR_GainIncreases); + psIndices_LBRR.GainsIndices[0] = (byte) (Inlines.silk_min_int(psIndices_LBRR.GainsIndices[0], SilkConstants.N_LEVELS_QGAIN - 1)); + } + + /* Decode to get gains in sync with decoder */ + /* Overwrite unquantized gains with quantized gains */ + BoxedValueByte boxed_gainIndex = new BoxedValueByte(this.LBRRprevLastGainIndex); + GainQuantization.silk_gains_dequant(thisCtrl.Gains_Q16, psIndices_LBRR.GainsIndices, + boxed_gainIndex, condCoding == SilkConstants.CODE_CONDITIONALLY ? 1 : 0, this.nb_subfr); + this.LBRRprevLastGainIndex = boxed_gainIndex.Val; + + /** + * ************************************** + */ + /* Noise shaping quantization */ + /** + * ************************************** + */ + if (this.nStatesDelayedDecision > 1 || this.warping_Q16 > 0) { + sNSQ_LBRR.silk_NSQ_del_dec(this, + psIndices_LBRR, + xfw_Q3, + this.pulses_LBRR[this.nFramesEncoded], + thisCtrl.PredCoef_Q12, + thisCtrl.LTPCoef_Q14, + thisCtrl.AR2_Q13, + thisCtrl.HarmShapeGain_Q14, + thisCtrl.Tilt_Q14, + thisCtrl.LF_shp_Q14, + thisCtrl.Gains_Q16, + thisCtrl.pitchL, + thisCtrl.Lambda_Q10, + thisCtrl.LTP_scale_Q14); + } else { + sNSQ_LBRR.silk_NSQ(this, + psIndices_LBRR, + xfw_Q3, + this.pulses_LBRR[this.nFramesEncoded], + thisCtrl.PredCoef_Q12, + thisCtrl.LTPCoef_Q14, + thisCtrl.AR2_Q13, + thisCtrl.HarmShapeGain_Q14, + thisCtrl.Tilt_Q14, + thisCtrl.LF_shp_Q14, + thisCtrl.Gains_Q16, + thisCtrl.pitchL, + thisCtrl.Lambda_Q10, + thisCtrl.LTP_scale_Q14); + } + + /* Restore original gains */ + System.arraycopy(TempGains_Q16, 0, thisCtrl.Gains_Q16, 0, this.nb_subfr); + } + } +} diff --git a/app/src/main/java/org/concentus/SilkConstants.java b/app/src/main/java/org/concentus/SilkConstants.java new file mode 100644 index 0000000000..fa46137379 --- /dev/null +++ b/app/src/main/java/org/concentus/SilkConstants.java @@ -0,0 +1,339 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class SilkConstants { + + /* Max number of encoder channels (1/2) */ + public static final int ENCODER_NUM_CHANNELS = 2; + /* Number of decoder channels (1/2) */ + public static final int DECODER_NUM_CHANNELS = 2; + + public static final int MAX_FRAMES_PER_PACKET = 3; + + /* Limits on bitrate */ + public static final int MIN_TARGET_RATE_BPS = 5000; + public static final int MAX_TARGET_RATE_BPS = 80000; + public static final int TARGET_RATE_TAB_SZ = 8; + + /* LBRR thresholds */ + public static final int LBRR_NB_MIN_RATE_BPS = 12000; + public static final int LBRR_MB_MIN_RATE_BPS = 14000; + public static final int LBRR_WB_MIN_RATE_BPS = 16000; + + /* DTX settings */ + public static final int NB_SPEECH_FRAMES_BEFORE_DTX = 10; + /* eq 200 ms */ + public static final int MAX_CONSECUTIVE_DTX = 20; + /* eq 400 ms */ + + /* Maximum sampling frequency */ + public static final int MAX_FS_KHZ = 16; + public static final int MAX_API_FS_KHZ = 48; + + // FIXME can use enums here + /* Signal types */ + public static final int TYPE_NO_VOICE_ACTIVITY = 0; + public static final int TYPE_UNVOICED = 1; + public static final int TYPE_VOICED = 2; + + /* Conditional coding types */ + public static final int CODE_INDEPENDENTLY = 0; + public static final int CODE_INDEPENDENTLY_NO_LTP_SCALING = 1; + public static final int CODE_CONDITIONALLY = 2; + + /* Settings for stereo processing */ + public static final int STEREO_QUANT_TAB_SIZE = 16; + public static final int STEREO_QUANT_SUB_STEPS = 5; + public static final int STEREO_INTERP_LEN_MS = 8; + /* must be even */ + public static final float STEREO_RATIO_SMOOTH_COEF = 0.01f; + /* smoothing coef for signal norms and stereo width */ + + /* Range of pitch lag estimates */ + public static final int PITCH_EST_MIN_LAG_MS = 2; + /* 2 ms . 500 Hz */ + public static final int PITCH_EST_MAX_LAG_MS = 18; + /* 18 ms . 56 Hz */ + + /* Maximum number of subframes */ + public static final int MAX_NB_SUBFR = 4; + + /* Number of samples per frame */ + public static final int LTP_MEM_LENGTH_MS = 20; + public static final int SUB_FRAME_LENGTH_MS = 5; + public static final int MAX_SUB_FRAME_LENGTH = (SUB_FRAME_LENGTH_MS * MAX_FS_KHZ); + public static final int MAX_FRAME_LENGTH_MS = (SUB_FRAME_LENGTH_MS * MAX_NB_SUBFR); + public static final int MAX_FRAME_LENGTH = (MAX_FRAME_LENGTH_MS * MAX_FS_KHZ); + + /* Milliseconds of lookahead for pitch analysis */ + public static final int LA_PITCH_MS = 2; + public static final int LA_PITCH_MAX = (LA_PITCH_MS * MAX_FS_KHZ); + + /* Order of LPC used in find pitch */ + public static final int MAX_FIND_PITCH_LPC_ORDER = 16; + + /* Length of LPC window used in find pitch */ + public static final int FIND_PITCH_LPC_WIN_MS = (20 + (LA_PITCH_MS << 1)); + public static final int FIND_PITCH_LPC_WIN_MS_2_SF = (10 + (LA_PITCH_MS << 1)); + public static final int FIND_PITCH_LPC_WIN_MAX = (FIND_PITCH_LPC_WIN_MS * MAX_FS_KHZ); + + /* Milliseconds of lookahead for noise shape analysis */ + public static final int LA_SHAPE_MS = 5; + public static final int LA_SHAPE_MAX = (LA_SHAPE_MS * MAX_FS_KHZ); + + /* Maximum length of LPC window used in noise shape analysis */ + public static final int SHAPE_LPC_WIN_MAX = (15 * MAX_FS_KHZ); + + /* dB level of lowest gain quantization level */ + public static final int MIN_QGAIN_DB = 2; + /* dB level of highest gain quantization level */ + public static final int MAX_QGAIN_DB = 88; + /* Number of gain quantization levels */ + public static final int N_LEVELS_QGAIN = 64; + /* Max increase in gain quantization index */ + public static final int MAX_DELTA_GAIN_QUANT = 36; + /* Max decrease in gain quantization index */ + public static final int MIN_DELTA_GAIN_QUANT = -4; + + /* Quantization offsets (multiples of 4) */ + public static final short OFFSET_VL_Q10 = 32; + public static final short OFFSET_VH_Q10 = 100; + public static final short OFFSET_UVL_Q10 = 100; + public static final short OFFSET_UVH_Q10 = 240; + + public static final int QUANT_LEVEL_ADJUST_Q10 = 80; + + /* Maximum numbers of iterations used to stabilize an LPC vector */ + public static final int MAX_LPC_STABILIZE_ITERATIONS = 16; + public static final float MAX_PREDICTION_POWER_GAIN = 1e4f; + public static final float MAX_PREDICTION_POWER_GAIN_AFTER_RESET = 1e2f; + + public static final int SILK_MAX_ORDER_LPC = 16; + public static final int MAX_LPC_ORDER = 16; + public static final int MIN_LPC_ORDER = 10; + + /* Find Pred Coef defines */ + public static final int LTP_ORDER = 5; + + /* LTP quantization settings */ + public static final int NB_LTP_CBKS = 3; + + /* Flag to use harmonic noise shaping */ + public static final int USE_HARM_SHAPING = 1; + + /* Max LPC order of noise shaping filters */ + public static final int MAX_SHAPE_LPC_ORDER = 16; + + public static final int HARM_SHAPE_FIR_TAPS = 3; + + /* Maximum number of delayed decision states */ + public static final int MAX_DEL_DEC_STATES = 4; + + public static final int LTP_BUF_LENGTH = 512; + public static final int LTP_MASK = (LTP_BUF_LENGTH - 1); + + public static final int DECISION_DELAY = 32; + public static final int DECISION_DELAY_MASK = (DECISION_DELAY - 1); + + /* Number of subframes for excitation entropy coding */ + public static final int SHELL_CODEC_FRAME_LENGTH = 16; + public static final int LOG2_SHELL_CODEC_FRAME_LENGTH = 4; + public static final int MAX_NB_SHELL_BLOCKS = (MAX_FRAME_LENGTH / SHELL_CODEC_FRAME_LENGTH); + + /* Number of rate levels, for entropy coding of excitation */ + public static final int N_RATE_LEVELS = 10; + + /* Maximum sum of pulses per shell coding frame */ + public static final int SILK_MAX_PULSES = 16; + + public static final int MAX_MATRIX_SIZE = MAX_LPC_ORDER; + /* Max of LPC Order and LTP order */ + + static final int NSQ_LPC_BUF_LENGTH = Math.max(MAX_LPC_ORDER, DECISION_DELAY); + + /** + * ************************ + */ + /* Voice activity detector */ + /** + * ************************ + */ + public static final int VAD_N_BANDS = 4; + + public static final int VAD_INTERNAL_SUBFRAMES_LOG2 = 2; + public static final int VAD_INTERNAL_SUBFRAMES = (1 << VAD_INTERNAL_SUBFRAMES_LOG2); + + public static final int VAD_NOISE_LEVEL_SMOOTH_COEF_Q16 = 1024; + /* Must be < 4096 */ + public static final int VAD_NOISE_LEVELS_BIAS = 50; + + /* Sigmoid settings */ + public static final int VAD_NEGATIVE_OFFSET_Q5 = 128; + /* sigmoid is 0 at -128 */ + public static final int VAD_SNR_FACTOR_Q16 = 45000; + + /* smoothing for SNR measurement */ + public static final int VAD_SNR_SMOOTH_COEF_Q18 = 4096; + + /* Size of the piecewise linear cosine approximation table for the LSFs */ + public static final int LSF_COS_TAB_SZ = 128; + + /** + * *************** + */ + /* NLSF quantizer */ + /** + * *************** + */ + public static final int NLSF_W_Q = 2; + public static final int NLSF_VQ_MAX_VECTORS = 32; + public static final int NLSF_VQ_MAX_SURVIVORS = 32; + public static final int NLSF_QUANT_MAX_AMPLITUDE = 4; + public static final int NLSF_QUANT_MAX_AMPLITUDE_EXT = 10; + public static final float NLSF_QUANT_LEVEL_ADJ = 0.1f; + public static final int NLSF_QUANT_DEL_DEC_STATES_LOG2 = 2; + public static final int NLSF_QUANT_DEL_DEC_STATES = (1 << NLSF_QUANT_DEL_DEC_STATES_LOG2); + + /* Transition filtering for mode switching */ + public static final int TRANSITION_TIME_MS = 5120; + /* 5120 = 64 * FRAME_LENGTH_MS * ( TRANSITION_INT_NUM - 1 ) = 64*(20*4)*/ + public static final int TRANSITION_NB = 3; + /* Hardcoded in tables */ + public static final int TRANSITION_NA = 2; + /* Hardcoded in tables */ + public static final int TRANSITION_INT_NUM = 5; + /* Hardcoded in tables */ + public static final int TRANSITION_FRAMES = (TRANSITION_TIME_MS / MAX_FRAME_LENGTH_MS); + public static final int TRANSITION_INT_STEPS = (TRANSITION_FRAMES / (TRANSITION_INT_NUM - 1)); + + /* BWE factors to apply after packet loss */ + public static final int BWE_AFTER_LOSS_Q16 = 63570; + + /* Defines for CN generation */ + public static final int CNG_BUF_MASK_MAX = 255; + /* 2^floor(log2(MAX_FRAME_LENGTH))-1 */ + public static final int CNG_GAIN_SMTH_Q16 = 4634; + /* 0.25^(1/4) */ + public static final int CNG_NLSF_SMTH_Q16 = 16348; + /* 0.25 */ + + /** + * ***************************************************** + */ + /* Definitions for pitch estimator (from pitch_est_defines.h) */ + /** + * ***************************************************** + */ + + public static final int PE_MAX_FS_KHZ = 16; + /* Maximum sampling frequency used */ + + public static final int PE_MAX_NB_SUBFR = 4; + public static final int PE_SUBFR_LENGTH_MS = 5; + /* 5 ms */ + + public static final int PE_LTP_MEM_LENGTH_MS = (4 * PE_SUBFR_LENGTH_MS); + + public static final int PE_MAX_FRAME_LENGTH_MS = (PE_LTP_MEM_LENGTH_MS + PE_MAX_NB_SUBFR * PE_SUBFR_LENGTH_MS); + public static final int PE_MAX_FRAME_LENGTH = (PE_MAX_FRAME_LENGTH_MS * PE_MAX_FS_KHZ); + public static final int PE_MAX_FRAME_LENGTH_ST_1 = (PE_MAX_FRAME_LENGTH >> 2); + public static final int PE_MAX_FRAME_LENGTH_ST_2 = (PE_MAX_FRAME_LENGTH >> 1); + + public static final int PE_MAX_LAG_MS = 18; + /* 18 ms . 56 Hz */ + public static final int PE_MIN_LAG_MS = 2; + /* 2 ms . 500 Hz */ + public static final int PE_MAX_LAG = (PE_MAX_LAG_MS * PE_MAX_FS_KHZ); + public static final int PE_MIN_LAG = (PE_MIN_LAG_MS * PE_MAX_FS_KHZ); + + public static final int PE_D_SRCH_LENGTH = 24; + + public static final int PE_NB_STAGE3_LAGS = 5; + + public static final int PE_NB_CBKS_STAGE2 = 3; + public static final int PE_NB_CBKS_STAGE2_EXT = 11; + + public static final int PE_NB_CBKS_STAGE3_MAX = 34; + public static final int PE_NB_CBKS_STAGE3_MID = 24; + public static final int PE_NB_CBKS_STAGE3_MIN = 16; + + public static final int PE_NB_CBKS_STAGE3_10MS = 12; + public static final int PE_NB_CBKS_STAGE2_10MS = 3; + + public static final float PE_SHORTLAG_BIAS = 0.2f; + /* for logarithmic weighting */ + public static final float PE_PREVLAG_BIAS = 0.2f; + /* for logarithmic weighting */ + public static final float PE_FLATCONTOUR_BIAS = 0.05f; + + public static final int SILK_PE_MIN_COMPLEX = 0; + public static final int SILK_PE_MID_COMPLEX = 1; + public static final int SILK_PE_MAX_COMPLEX = 2; + + // Definitions for PLC (from plc.h) + public static final float BWE_COEF = 0.99f; + public static final int V_PITCH_GAIN_START_MIN_Q14 = 11469; + /* 0.7 in Q14 */ + public static final int V_PITCH_GAIN_START_MAX_Q14 = 15565; + /* 0.95 in Q14 */ + public static final int MAX_PITCH_LAG_MS = 18; + public static final int RAND_BUF_SIZE = 128; + public static final int RAND_BUF_MASK = (RAND_BUF_SIZE - 1); + public static final int LOG2_INV_LPC_GAIN_HIGH_THRES = 3; + /* 2^3 = 8 dB LPC gain */ + public static final int LOG2_INV_LPC_GAIN_LOW_THRES = 8; + /* 2^8 = 24 dB LPC gain */ + public static final int PITCH_DRIFT_FAC_Q16 = 655; + /* 0.01 in Q16 */ + + // Definitions for resampler (from resampler_structs.h) + + public static final int SILK_RESAMPLER_MAX_FIR_ORDER = 36; + public static final int SILK_RESAMPLER_MAX_IIR_ORDER = 6; + + // from resampler_rom.h + public static final int RESAMPLER_DOWN_ORDER_FIR0 = 18; + public static final int RESAMPLER_DOWN_ORDER_FIR1 = 24; + public static final int RESAMPLER_DOWN_ORDER_FIR2 = 36; + public static final int RESAMPLER_ORDER_FIR_12 = 8; + + // from resampler_private.h + + /* Number of input samples to process in the inner loop */ + public static final int RESAMPLER_MAX_BATCH_SIZE_MS = 10; + public static final int RESAMPLER_MAX_FS_KHZ = 48; + public static final int RESAMPLER_MAX_BATCH_SIZE_IN = (RESAMPLER_MAX_BATCH_SIZE_MS * RESAMPLER_MAX_FS_KHZ); + + // from api.h + public static final int SILK_MAX_FRAMES_PER_PACKET = 3; +} diff --git a/app/src/main/java/org/concentus/SilkDecoder.java b/app/src/main/java/org/concentus/SilkDecoder.java new file mode 100644 index 0000000000..d8bb0b3b7a --- /dev/null +++ b/app/src/main/java/org/concentus/SilkDecoder.java @@ -0,0 +1,60 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Decoder super struct +/// +class SilkDecoder { + + final SilkChannelDecoder[] channel_state = new SilkChannelDecoder[SilkConstants.DECODER_NUM_CHANNELS]; + final StereoDecodeState sStereo = new StereoDecodeState(); + int nChannelsAPI = 0; + int nChannelsInternal = 0; + int prev_decode_only_middle = 0; + + SilkDecoder() { + for (int c = 0; c < SilkConstants.DECODER_NUM_CHANNELS; c++) { + channel_state[c] = new SilkChannelDecoder(); + } + } + + void Reset() { + for (int c = 0; c < SilkConstants.DECODER_NUM_CHANNELS; c++) { + channel_state[c].Reset(); + } + sStereo.Reset(); + nChannelsAPI = 0; + nChannelsInternal = 0; + prev_decode_only_middle = 0; + } +} diff --git a/app/src/main/java/org/concentus/SilkDecoderControl.java b/app/src/main/java/org/concentus/SilkDecoderControl.java new file mode 100644 index 0000000000..0c17a842ce --- /dev/null +++ b/app/src/main/java/org/concentus/SilkDecoderControl.java @@ -0,0 +1,56 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Decoder control +/// +class SilkDecoderControl { + + /* Prediction and coding parameters */ + final int[] pitchL = new int[SilkConstants.MAX_NB_SUBFR]; + final int[] Gains_Q16 = new int[SilkConstants.MAX_NB_SUBFR]; + + /* Holds interpolated and final coefficients */ + final short[][] PredCoef_Q12 = Arrays.InitTwoDimensionalArrayShort(2, SilkConstants.MAX_LPC_ORDER); + final short[] LTPCoef_Q14 = new short[SilkConstants.LTP_ORDER * SilkConstants.MAX_NB_SUBFR]; + int LTP_scale_Q14 = 0; + + void Reset() { + Arrays.MemSet(pitchL, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSet(Gains_Q16, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSet(PredCoef_Q12[0], (short) 0, SilkConstants.MAX_LPC_ORDER); + Arrays.MemSet(PredCoef_Q12[1], (short) 0, SilkConstants.MAX_LPC_ORDER); + Arrays.MemSet(LTPCoef_Q14, (short) 0, SilkConstants.LTP_ORDER * SilkConstants.MAX_NB_SUBFR); + LTP_scale_Q14 = 0; + } +} diff --git a/app/src/main/java/org/concentus/SilkEncoder.java b/app/src/main/java/org/concentus/SilkEncoder.java new file mode 100644 index 0000000000..c8c3d21720 --- /dev/null +++ b/app/src/main/java/org/concentus/SilkEncoder.java @@ -0,0 +1,95 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Encoder Super Struct +/// +class SilkEncoder { + + final SilkChannelEncoder[] state_Fxx = new SilkChannelEncoder[SilkConstants.ENCODER_NUM_CHANNELS]; + final StereoEncodeState sStereo = new StereoEncodeState(); + int nBitsUsedLBRR = 0; + int nBitsExceeded = 0; + int nChannelsAPI = 0; + int nChannelsInternal = 0; + int nPrevChannelsInternal = 0; + int timeSinceSwitchAllowed_ms = 0; + int allowBandwidthSwitch = 0; + int prev_decode_only_middle = 0; + + SilkEncoder() { + for (int c = 0; c < SilkConstants.ENCODER_NUM_CHANNELS; c++) { + state_Fxx[c] = new SilkChannelEncoder(); + } + } + + void Reset() { + for (int c = 0; c < SilkConstants.ENCODER_NUM_CHANNELS; c++) { + state_Fxx[c].Reset(); + } + + sStereo.Reset(); + nBitsUsedLBRR = 0; + nBitsExceeded = 0; + nChannelsAPI = 0; + nChannelsInternal = 0; + nPrevChannelsInternal = 0; + timeSinceSwitchAllowed_ms = 0; + allowBandwidthSwitch = 0; + prev_decode_only_middle = 0; + } + + /// + /// Initialize Silk Encoder state + /// + /// I/O Pointer to Silk FIX encoder state + /// I Run-time architecture + /// + static int silk_init_encoder(SilkChannelEncoder psEnc) { + int ret = 0; + + // Clear the entire encoder state + psEnc.Reset(); + + psEnc.variable_HP_smth1_Q15 = Inlines.silk_LSHIFT(Inlines.silk_lin2log(((int) ((TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ, 16)*/) - (16 << 7), 8); + psEnc.variable_HP_smth2_Q15 = psEnc.variable_HP_smth1_Q15; + + // Used to deactivate LSF interpolation, pitch prediction + psEnc.first_frame_after_reset = 1; + + // Initialize Silk VAD + ret += VoiceActivityDetection.silk_VAD_Init(psEnc.sVAD); + + return ret; + } +} diff --git a/app/src/main/java/org/concentus/SilkEncoderControl.java b/app/src/main/java/org/concentus/SilkEncoderControl.java new file mode 100644 index 0000000000..ba272de029 --- /dev/null +++ b/app/src/main/java/org/concentus/SilkEncoderControl.java @@ -0,0 +1,104 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/** + * ********************* + */ +/* Encoder control FIX */ +/** + * ********************* + */ +class SilkEncoderControl { + + /* Prediction and coding parameters */ + final int[] Gains_Q16 = new int[SilkConstants.MAX_NB_SUBFR]; + final short[][] PredCoef_Q12 = Arrays.InitTwoDimensionalArrayShort(2, SilkConstants.MAX_LPC_ORDER); + /* holds interpolated and final coefficients */ + final short[] LTPCoef_Q14 = new short[SilkConstants.LTP_ORDER * SilkConstants.MAX_NB_SUBFR]; + int LTP_scale_Q14 = 0; + final int[] pitchL = new int[SilkConstants.MAX_NB_SUBFR]; + + /* Noise shaping parameters */ + final short[] AR1_Q13 = new short[SilkConstants.MAX_NB_SUBFR * SilkConstants.MAX_SHAPE_LPC_ORDER]; + final short[] AR2_Q13 = new short[SilkConstants.MAX_NB_SUBFR * SilkConstants.MAX_SHAPE_LPC_ORDER]; + final int[] LF_shp_Q14 = new int[SilkConstants.MAX_NB_SUBFR]; + /* Packs two int16 coefficients per int32 value */ + final int[] GainsPre_Q14 = new int[SilkConstants.MAX_NB_SUBFR]; + final int[] HarmBoost_Q14 = new int[SilkConstants.MAX_NB_SUBFR]; + final int[] Tilt_Q14 = new int[SilkConstants.MAX_NB_SUBFR]; + final int[] HarmShapeGain_Q14 = new int[SilkConstants.MAX_NB_SUBFR]; + int Lambda_Q10 = 0; + int input_quality_Q14 = 0; + int coding_quality_Q14 = 0; + + /* Measures */ + int sparseness_Q8 = 0; + int predGain_Q16 = 0; + int LTPredCodGain_Q7 = 0; + + /* Residual energy per subframe */ + final int[] ResNrg = new int[SilkConstants.MAX_NB_SUBFR]; + + /* Q domain for the residual energy > 0 */ + final int[] ResNrgQ = new int[SilkConstants.MAX_NB_SUBFR]; + + /* Parameters for CBR mode */ + final int[] GainsUnq_Q16 = new int[SilkConstants.MAX_NB_SUBFR]; + byte lastGainIndexPrev = 0; + + void Reset() { + Arrays.MemSet(Gains_Q16, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSet(PredCoef_Q12[0], (short) 0, SilkConstants.MAX_LPC_ORDER); + Arrays.MemSet(PredCoef_Q12[1], (short) 0, SilkConstants.MAX_LPC_ORDER); + Arrays.MemSet(LTPCoef_Q14, (short) 0, SilkConstants.LTP_ORDER * SilkConstants.MAX_NB_SUBFR); + LTP_scale_Q14 = 0; + Arrays.MemSet(pitchL, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSet(AR1_Q13, (short) 0, SilkConstants.MAX_NB_SUBFR * SilkConstants.MAX_SHAPE_LPC_ORDER); + Arrays.MemSet(AR2_Q13, (short) 0, SilkConstants.MAX_NB_SUBFR * SilkConstants.MAX_SHAPE_LPC_ORDER); + Arrays.MemSet(LF_shp_Q14, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSet(GainsPre_Q14, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSet(HarmBoost_Q14, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSet(Tilt_Q14, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSet(HarmShapeGain_Q14, 0, SilkConstants.MAX_NB_SUBFR); + Lambda_Q10 = 0; + input_quality_Q14 = 0; + coding_quality_Q14 = 0; + sparseness_Q8 = 0; + predGain_Q16 = 0; + LTPredCodGain_Q7 = 0; + Arrays.MemSet(ResNrg, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSet(ResNrgQ, 0, SilkConstants.MAX_NB_SUBFR); + Arrays.MemSet(GainsUnq_Q16, 0, SilkConstants.MAX_NB_SUBFR); + lastGainIndexPrev = 0; + } +} diff --git a/app/src/main/java/org/concentus/SilkError.java b/app/src/main/java/org/concentus/SilkError.java new file mode 100644 index 0000000000..721575f95e --- /dev/null +++ b/app/src/main/java/org/concentus/SilkError.java @@ -0,0 +1,89 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Represents error messages from a silk encoder/decoder +/// +class SilkError { + + static int SILK_NO_ERROR = 0; + + // Encoder error messages + + /* Input length is not a multiple of 10 ms, or length is longer than the packet length */ + static int SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES = -101; + + /* Sampling frequency not 8000, 12000 or 16000 Hertz */ + static int SILK_ENC_FS_NOT_SUPPORTED = -102; + + /* Packet size not 10, 20, 40, or 60 ms */ + static int SILK_ENC_PACKET_SIZE_NOT_SUPPORTED = -103; + + /* Allocated payload buffer too short */ + static int SILK_ENC_PAYLOAD_BUF_TOO_SHORT = -104; + + /* Loss rate not between 0 and 100 percent */ + static int SILK_ENC_INVALID_LOSS_RATE = -105; + + /* Complexity setting not valid, use 0...10 */ + static int SILK_ENC_INVALID_COMPLEXITY_SETTING = -106; + + /* Inband FEC setting not valid, use 0 or 1 */ + static int SILK_ENC_INVALID_INBAND_FEC_SETTING = -107; + + /* DTX setting not valid, use 0 or 1 */ + static int SILK_ENC_INVALID_DTX_SETTING = -108; + + /* CBR setting not valid, use 0 or 1 */ + static int SILK_ENC_INVALID_CBR_SETTING = -109; + + /* Internal encoder error */ + static int SILK_ENC_INTERNAL_ERROR = -110; + + /* Internal encoder error */ + static int SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR = -111; + + // Decoder error messages + + /* Output sampling frequency lower than internal decoded sampling frequency */ + static int SILK_DEC_INVALID_SAMPLING_FREQUENCY = -200; + + /* Payload size exceeded the maximum allowed 1024 bytes */ + static int SILK_DEC_PAYLOAD_TOO_LARGE = -201; + + /* Payload has bit errors */ + static int SILK_DEC_PAYLOAD_ERROR = -202; + + /* Payload has bit errors */ + static int SILK_DEC_INVALID_FRAME_SIZE = -203; +} diff --git a/app/src/main/java/org/concentus/SilkLPState.java b/app/src/main/java/org/concentus/SilkLPState.java new file mode 100644 index 0000000000..b45b4c74de --- /dev/null +++ b/app/src/main/java/org/concentus/SilkLPState.java @@ -0,0 +1,99 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Variable cut-off low-pass filter state +/// +class SilkLPState { + + /// + /// Low pass filter state + /// + final int[] In_LP_State = new int[2]; + + /// + /// Counter which is mapped to a cut-off frequency + /// + int transition_frame_no = 0; + + /// + /// Operating mode, <0: switch down, >0: switch up; 0: do nothing + /// + int mode = 0; + + void Reset() { + In_LP_State[0] = 0; + In_LP_State[1] = 0; + transition_frame_no = 0; + mode = 0; + } + + /* Low-pass filter with variable cutoff frequency based on */ + /* piece-wise linear interpolation between elliptic filters */ + /* Start by setting psEncC.mode <> 0; */ + /* Deactivate by setting psEncC.mode = 0; */ + void silk_LP_variable_cutoff( + short[] frame, /* I/O Low-pass filtered output signal */ + int frame_ptr, + int frame_length /* I Frame length */ + ) { + int[] B_Q28 = new int[SilkConstants.TRANSITION_NB]; + int[] A_Q28 = new int[SilkConstants.TRANSITION_NA]; + int fac_Q16 = 0; + int ind = 0; + + Inlines.OpusAssert(this.transition_frame_no >= 0 && this.transition_frame_no <= SilkConstants.TRANSITION_FRAMES); + + /* Run filter if needed */ + if (this.mode != 0) { + /* Calculate index and interpolation factor for interpolation */ + fac_Q16 = Inlines.silk_LSHIFT(SilkConstants.TRANSITION_FRAMES - this.transition_frame_no, 16 - 6); + + ind = Inlines.silk_RSHIFT(fac_Q16, 16); + fac_Q16 -= Inlines.silk_LSHIFT(ind, 16); + + Inlines.OpusAssert(ind >= 0); + Inlines.OpusAssert(ind < SilkConstants.TRANSITION_INT_NUM); + + /* Interpolate filter coefficients */ + Filters.silk_LP_interpolate_filter_taps(B_Q28, A_Q28, ind, fac_Q16); + + /* Update transition frame number for next frame */ + this.transition_frame_no = Inlines.silk_LIMIT(this.transition_frame_no + this.mode, 0, SilkConstants.TRANSITION_FRAMES); + + /* ARMA low-pass filtering */ + Inlines.OpusAssert(SilkConstants.TRANSITION_NB == 3 && SilkConstants.TRANSITION_NA == 2); + Filters.silk_biquad_alt(frame, frame_ptr, B_Q28, A_Q28, this.In_LP_State, frame, frame_ptr, frame_length, 1); + } + } +} diff --git a/app/src/main/java/org/concentus/SilkNSQState.java b/app/src/main/java/org/concentus/SilkNSQState.java new file mode 100644 index 0000000000..f9095b23aa --- /dev/null +++ b/app/src/main/java/org/concentus/SilkNSQState.java @@ -0,0 +1,1229 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Noise shaping quantization state +/// +class SilkNSQState { + + /// + /// Buffer for quantized output signal + /// + final short[] xq = new short[2 * SilkConstants.MAX_FRAME_LENGTH]; //opt: make these arrays variable-sized since construction cost is significant here + final int[] sLTP_shp_Q14 = new int[2 * SilkConstants.MAX_FRAME_LENGTH]; + final int[] sLPC_Q14 = new int[SilkConstants.MAX_SUB_FRAME_LENGTH + SilkConstants.NSQ_LPC_BUF_LENGTH]; + final int[] sAR2_Q14 = new int[SilkConstants.MAX_SHAPE_LPC_ORDER]; + int sLF_AR_shp_Q14 = 0; + int lagPrev = 0; + int sLTP_buf_idx = 0; + int sLTP_shp_buf_idx = 0; + int rand_seed = 0; + int prev_gain_Q16 = 0; + int rewhite_flag = 0; + + void Reset() { + Arrays.MemSet(xq, (short) 0, 2 * SilkConstants.MAX_FRAME_LENGTH); + Arrays.MemSet(sLTP_shp_Q14, 0, 2 * SilkConstants.MAX_FRAME_LENGTH); + Arrays.MemSet(sLPC_Q14, 0, SilkConstants.MAX_SUB_FRAME_LENGTH + SilkConstants.NSQ_LPC_BUF_LENGTH); + Arrays.MemSet(sAR2_Q14, 0, SilkConstants.MAX_SHAPE_LPC_ORDER); + sLF_AR_shp_Q14 = 0; + lagPrev = 0; + sLTP_buf_idx = 0; + sLTP_shp_buf_idx = 0; + rand_seed = 0; + prev_gain_Q16 = 0; + rewhite_flag = 0; + } + + // Copies another nsq state to this one + void Assign(SilkNSQState other) { + this.sLF_AR_shp_Q14 = other.sLF_AR_shp_Q14; + this.lagPrev = other.lagPrev; + this.sLTP_buf_idx = other.sLTP_buf_idx; + this.sLTP_shp_buf_idx = other.sLTP_shp_buf_idx; + this.rand_seed = other.rand_seed; + this.prev_gain_Q16 = other.prev_gain_Q16; + this.rewhite_flag = other.rewhite_flag; + System.arraycopy(other.xq, 0, this.xq, 0, 2 * SilkConstants.MAX_FRAME_LENGTH); + System.arraycopy(other.sLTP_shp_Q14, 0, this.sLTP_shp_Q14, 0, 2 * SilkConstants.MAX_FRAME_LENGTH); + System.arraycopy(other.sLPC_Q14, 0, this.sLPC_Q14, 0, SilkConstants.MAX_SUB_FRAME_LENGTH + SilkConstants.NSQ_LPC_BUF_LENGTH); + System.arraycopy(other.sAR2_Q14, 0, this.sAR2_Q14, 0, SilkConstants.MAX_SHAPE_LPC_ORDER); + } + + private class NSQ_del_dec_struct { + + final int[] sLPC_Q14 = new int[SilkConstants.MAX_SUB_FRAME_LENGTH + SilkConstants.NSQ_LPC_BUF_LENGTH]; + final int[] RandState = new int[SilkConstants.DECISION_DELAY]; + final int[] Q_Q10 = new int[SilkConstants.DECISION_DELAY]; + final int[] Xq_Q14 = new int[SilkConstants.DECISION_DELAY]; + final int[] Pred_Q15 = new int[SilkConstants.DECISION_DELAY]; + final int[] Shape_Q14 = new int[SilkConstants.DECISION_DELAY]; + int[] sAR2_Q14; + int LF_AR_Q14 = 0; + int Seed = 0; + int SeedInit = 0; + int RD_Q10 = 0; + + NSQ_del_dec_struct(int shapingOrder) { + sAR2_Q14 = new int[shapingOrder]; + } + + void PartialCopyFrom(NSQ_del_dec_struct other, int q14Offset) { + System.arraycopy(other.sLPC_Q14, q14Offset, sLPC_Q14, q14Offset, (SilkConstants.MAX_SUB_FRAME_LENGTH + SilkConstants.NSQ_LPC_BUF_LENGTH - q14Offset)); + System.arraycopy(other.RandState, 0, RandState, 0, SilkConstants.DECISION_DELAY); + System.arraycopy(other.Q_Q10, 0, Q_Q10, 0, SilkConstants.DECISION_DELAY); + System.arraycopy(other.Xq_Q14, 0, Xq_Q14, 0, SilkConstants.DECISION_DELAY); + System.arraycopy(other.Pred_Q15, 0, Pred_Q15, 0, SilkConstants.DECISION_DELAY); + System.arraycopy(other.Shape_Q14, 0, Shape_Q14, 0, SilkConstants.DECISION_DELAY); + System.arraycopy(other.sAR2_Q14, 0, sAR2_Q14, 0, sAR2_Q14.length); + LF_AR_Q14 = other.LF_AR_Q14; + Seed = other.Seed; + SeedInit = other.SeedInit; + RD_Q10 = other.RD_Q10; + } + + void Assign(NSQ_del_dec_struct other) { + this.PartialCopyFrom(other, 0); + } + } + + private class NSQ_sample_struct { + + int Q_Q10; + int RD_Q10; + int xq_Q14; + int LF_AR_Q14; + int sLTP_shp_Q14; + int LPC_exc_Q14; + + void Assign(NSQ_sample_struct other) { + this.Q_Q10 = other.Q_Q10; + this.RD_Q10 = other.RD_Q10; + this.xq_Q14 = other.xq_Q14; + this.LF_AR_Q14 = other.LF_AR_Q14; + this.sLTP_shp_Q14 = other.sLTP_shp_Q14; + this.LPC_exc_Q14 = other.LPC_exc_Q14; + } + } + + void silk_NSQ( + SilkChannelEncoder psEncC, /* I/O Encoder State */ + SideInfoIndices psIndices, /* I/O Quantization Indices */ + int[] x_Q3, /* I Prefiltered input signal */ + byte[] pulses, /* O Quantized pulse signal */ + short[][] PredCoef_Q12, /* I Short term prediction coefs [2][SilkConstants.MAX_LPC_ORDER] */ + short[] LTPCoef_Q14, /* I Long term prediction coefs [SilkConstants.LTP_ORDER * MAX_NB_SUBFR] */ + short[] AR2_Q13, /* I Noise shaping coefs [MAX_NB_SUBFR * SilkConstants.MAX_SHAPE_LPC_ORDER] */ + int[] HarmShapeGain_Q14, /* I Long term shaping coefs [MAX_NB_SUBFR] */ + int[] Tilt_Q14, /* I Spectral tilt [MAX_NB_SUBFR] */ + int[] LF_shp_Q14, /* I Low frequency shaping coefs [MAX_NB_SUBFR] */ + int[] Gains_Q16, /* I Quantization step sizes [MAX_NB_SUBFR] */ + int[] pitchL, /* I Pitch lags [MAX_NB_SUBFR] */ + int Lambda_Q10, /* I Rate/distortion tradeoff */ + int LTP_scale_Q14 /* I LTP state scaling */ + ) { + int k, lag, start_idx, LSF_interpolation_flag; + int A_Q12, B_Q14, AR_shp_Q13; + int pxq; + int[] sLTP_Q15; + short[] sLTP; + int HarmShapeFIRPacked_Q14; + int offset_Q10; + int[] x_sc_Q10; + int pulses_ptr = 0; + int x_Q3_ptr = 0; + + this.rand_seed = psIndices.Seed; + + /* Set unvoiced lag to the previous one, overwrite later for voiced */ + lag = this.lagPrev; + + Inlines.OpusAssert(this.prev_gain_Q16 != 0); + + offset_Q10 = SilkTables.silk_Quantization_Offsets_Q10[psIndices.signalType >> 1][psIndices.quantOffsetType]; + + if (psIndices.NLSFInterpCoef_Q2 == 4) { + LSF_interpolation_flag = 0; + } else { + LSF_interpolation_flag = 1; + } + + sLTP_Q15 = new int[psEncC.ltp_mem_length + psEncC.frame_length]; + sLTP = new short[psEncC.ltp_mem_length + psEncC.frame_length]; + x_sc_Q10 = new int[psEncC.subfr_length]; + /* Set up pointers to start of sub frame */ + this.sLTP_shp_buf_idx = psEncC.ltp_mem_length; + this.sLTP_buf_idx = psEncC.ltp_mem_length; + pxq = psEncC.ltp_mem_length; + for (k = 0; k < psEncC.nb_subfr; k++) { + A_Q12 = (((k >> 1) | (1 - LSF_interpolation_flag))); + B_Q14 = (k * SilkConstants.LTP_ORDER); // opt: does this indicate a partitioned array? + AR_shp_Q13 = (k * SilkConstants.MAX_SHAPE_LPC_ORDER); // opt: same here + + /* Noise shape parameters */ + Inlines.OpusAssert(HarmShapeGain_Q14[k] >= 0); + HarmShapeFIRPacked_Q14 = Inlines.silk_RSHIFT(HarmShapeGain_Q14[k], 2); + HarmShapeFIRPacked_Q14 |= Inlines.silk_LSHIFT((int) Inlines.silk_RSHIFT(HarmShapeGain_Q14[k], 1), 16); + + this.rewhite_flag = 0; + if (psIndices.signalType == SilkConstants.TYPE_VOICED) { + /* Voiced */ + lag = pitchL[k]; + + /* Re-whitening */ + if ((k & (3 - Inlines.silk_LSHIFT(LSF_interpolation_flag, 1))) == 0) { + /* Rewhiten with new A coefs */ + start_idx = psEncC.ltp_mem_length - lag - psEncC.predictLPCOrder - SilkConstants.LTP_ORDER / 2; + Inlines.OpusAssert(start_idx > 0); + + Filters.silk_LPC_analysis_filter(sLTP, start_idx, this.xq, start_idx + k * psEncC.subfr_length, + PredCoef_Q12[A_Q12], 0, psEncC.ltp_mem_length - start_idx, psEncC.predictLPCOrder); + + this.rewhite_flag = 1; + this.sLTP_buf_idx = psEncC.ltp_mem_length; + } + } + + silk_nsq_scale_states(psEncC, x_Q3, x_Q3_ptr, x_sc_Q10, sLTP, sLTP_Q15, k, LTP_scale_Q14, Gains_Q16, pitchL, psIndices.signalType); + + silk_noise_shape_quantizer( + psIndices.signalType, + x_sc_Q10, + pulses, + pulses_ptr, + this.xq, + pxq, + sLTP_Q15, + PredCoef_Q12[A_Q12], + LTPCoef_Q14, + B_Q14, + AR2_Q13, + AR_shp_Q13, + lag, + HarmShapeFIRPacked_Q14, + Tilt_Q14[k], + LF_shp_Q14[k], + Gains_Q16[k], + Lambda_Q10, + offset_Q10, + psEncC.subfr_length, + psEncC.shapingLPCOrder, + psEncC.predictLPCOrder); + + x_Q3_ptr += psEncC.subfr_length; + pulses_ptr += psEncC.subfr_length; + pxq += psEncC.subfr_length; + } + + /* Update lagPrev for next frame */ + this.lagPrev = pitchL[psEncC.nb_subfr - 1]; + + /* Save quantized speech and noise shaping signals */ + Arrays.MemMove(this.xq, psEncC.frame_length, 0, psEncC.ltp_mem_length); + Arrays.MemMove(this.sLTP_shp_Q14, psEncC.frame_length, 0, psEncC.ltp_mem_length); + } + + /** + * ******************************** + */ + /* silk_noise_shape_quantizer */ + /** + * ******************************** + */ + private void silk_noise_shape_quantizer( + int signalType, /* I Signal type */ + int[] x_sc_Q10, /* I [length] */ + byte[] pulses, /* O [length] */ + int pulses_ptr, + short[] xq, /* O [length] */ + int xq_ptr, + int[] sLTP_Q15, /* I/O LTP state */ + short[] a_Q12, /* I Short term prediction coefs */ + short[] b_Q14, /* I Long term prediction coefs */ + int b_Q14_ptr, + short[] AR_shp_Q13, /* I Noise shaping AR coefs */ + int AR_shp_Q13_ptr, + int lag, /* I Pitch lag */ + int HarmShapeFIRPacked_Q14, /* I */ + int Tilt_Q14, /* I Spectral tilt */ + int LF_shp_Q14, /* I */ + int Gain_Q16, /* I */ + int Lambda_Q10, /* I */ + int offset_Q10, /* I */ + int length, /* I Input length */ + int shapingLPCOrder, /* I Noise shaping AR filter order */ + int predictLPCOrder /* I Prediction filter order */ + ) { + int i, j; + int LTP_pred_Q13, LPC_pred_Q10, n_AR_Q12, n_LTP_Q13; + int n_LF_Q12, r_Q10, rr_Q10, q1_Q0, q1_Q10, q2_Q10, rd1_Q20, rd2_Q20; + int exc_Q14, LPC_exc_Q14, xq_Q14, Gain_Q10; + int tmp1, tmp2, sLF_AR_shp_Q14; + int psLPC_Q14; + int shp_lag_ptr; + int pred_lag_ptr; + + shp_lag_ptr = this.sLTP_shp_buf_idx - lag + SilkConstants.HARM_SHAPE_FIR_TAPS / 2; + pred_lag_ptr = this.sLTP_buf_idx - lag + SilkConstants.LTP_ORDER / 2; + Gain_Q10 = Inlines.silk_RSHIFT(Gain_Q16, 6); + + /* Set up short term AR state */ + psLPC_Q14 = SilkConstants.NSQ_LPC_BUF_LENGTH - 1; + + for (i = 0; i < length; i++) { + /* Generate dither */ + this.rand_seed = Inlines.silk_RAND(this.rand_seed); + + /* Short-term prediction */ + Inlines.OpusAssert(predictLPCOrder == 10 || predictLPCOrder == 16); + /* Avoids introducing a bias because Inlines.silk_SMLAWB() always rounds to -inf */ + LPC_pred_Q10 = Inlines.silk_RSHIFT(predictLPCOrder, 1); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 0], a_Q12[0]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 1], a_Q12[1]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 2], a_Q12[2]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 3], a_Q12[3]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 4], a_Q12[4]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 5], a_Q12[5]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 6], a_Q12[6]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 7], a_Q12[7]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 8], a_Q12[8]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 9], a_Q12[9]); + if (predictLPCOrder == 16) { + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 10], a_Q12[10]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 11], a_Q12[11]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 12], a_Q12[12]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 13], a_Q12[13]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 14], a_Q12[14]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 15], a_Q12[15]); + } + + /* Long-term prediction */ + if (signalType == SilkConstants.TYPE_VOICED) { + /* Unrolled loop */ + /* Avoids introducing a bias because Inlines.silk_SMLAWB() always rounds to -inf */ + LTP_pred_Q13 = 2; + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr], b_Q14[b_Q14_ptr]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 1], b_Q14[b_Q14_ptr + 1]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 2], b_Q14[b_Q14_ptr + 2]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 3], b_Q14[b_Q14_ptr + 3]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 4], b_Q14[b_Q14_ptr + 4]); + pred_lag_ptr += 1; + } else { + LTP_pred_Q13 = 0; + } + + /* Noise shape feedback */ + Inlines.OpusAssert((shapingLPCOrder & 1) == 0); + /* check that order is even */ + tmp2 = this.sLPC_Q14[psLPC_Q14]; + tmp1 = this.sAR2_Q14[0]; + this.sAR2_Q14[0] = tmp2; + n_AR_Q12 = Inlines.silk_RSHIFT(shapingLPCOrder, 1); + n_AR_Q12 = Inlines.silk_SMLAWB(n_AR_Q12, tmp2, AR_shp_Q13[AR_shp_Q13_ptr]); + for (j = 2; j < shapingLPCOrder; j += 2) { + tmp2 = this.sAR2_Q14[j - 1]; + this.sAR2_Q14[j - 1] = tmp1; + n_AR_Q12 = Inlines.silk_SMLAWB(n_AR_Q12, tmp1, AR_shp_Q13[AR_shp_Q13_ptr + j - 1]); + tmp1 = this.sAR2_Q14[j + 0]; + this.sAR2_Q14[j + 0] = tmp2; + n_AR_Q12 = Inlines.silk_SMLAWB(n_AR_Q12, tmp2, AR_shp_Q13[AR_shp_Q13_ptr + j]); + } + this.sAR2_Q14[shapingLPCOrder - 1] = tmp1; + n_AR_Q12 = Inlines.silk_SMLAWB(n_AR_Q12, tmp1, AR_shp_Q13[AR_shp_Q13_ptr + shapingLPCOrder - 1]); + + n_AR_Q12 = Inlines.silk_LSHIFT32(n_AR_Q12, 1); + /* Q11 . Q12 */ + n_AR_Q12 = Inlines.silk_SMLAWB(n_AR_Q12, this.sLF_AR_shp_Q14, Tilt_Q14); + + n_LF_Q12 = Inlines.silk_SMULWB(this.sLTP_shp_Q14[this.sLTP_shp_buf_idx - 1], LF_shp_Q14); + n_LF_Q12 = Inlines.silk_SMLAWT(n_LF_Q12, this.sLF_AR_shp_Q14, LF_shp_Q14); + + Inlines.OpusAssert(lag > 0 || signalType != SilkConstants.TYPE_VOICED); + + /* Combine prediction and noise shaping signals */ + tmp1 = Inlines.silk_SUB32(Inlines.silk_LSHIFT32(LPC_pred_Q10, 2), n_AR_Q12); + /* Q12 */ + tmp1 = Inlines.silk_SUB32(tmp1, n_LF_Q12); + /* Q12 */ + if (lag > 0) { + /* Symmetric, packed FIR coefficients */ + n_LTP_Q13 = Inlines.silk_SMULWB(Inlines.silk_ADD32(this.sLTP_shp_Q14[shp_lag_ptr], this.sLTP_shp_Q14[shp_lag_ptr - 2]), HarmShapeFIRPacked_Q14); + n_LTP_Q13 = Inlines.silk_SMLAWT(n_LTP_Q13, this.sLTP_shp_Q14[shp_lag_ptr - 1], HarmShapeFIRPacked_Q14); + n_LTP_Q13 = Inlines.silk_LSHIFT(n_LTP_Q13, 1); + shp_lag_ptr += 1; + + tmp2 = Inlines.silk_SUB32(LTP_pred_Q13, n_LTP_Q13); + /* Q13 */ + tmp1 = Inlines.silk_ADD_LSHIFT32(tmp2, tmp1, 1); + /* Q13 */ + tmp1 = Inlines.silk_RSHIFT_ROUND(tmp1, 3); + /* Q10 */ + } else { + tmp1 = Inlines.silk_RSHIFT_ROUND(tmp1, 2); + /* Q10 */ + } + + r_Q10 = Inlines.silk_SUB32(x_sc_Q10[i], tmp1); + /* residual error Q10 */ + + /* Flip sign depending on dither */ + if (this.rand_seed < 0) { + r_Q10 = -r_Q10; + } + r_Q10 = Inlines.silk_LIMIT_32(r_Q10, -(31 << 10), 30 << 10); + + /* Find two quantization level candidates and measure their rate-distortion */ + q1_Q10 = Inlines.silk_SUB32(r_Q10, offset_Q10); + q1_Q0 = Inlines.silk_RSHIFT(q1_Q10, 10); + if (q1_Q0 > 0) { + q1_Q10 = Inlines.silk_SUB32(Inlines.silk_LSHIFT(q1_Q0, 10), SilkConstants.QUANT_LEVEL_ADJUST_Q10); + q1_Q10 = Inlines.silk_ADD32(q1_Q10, offset_Q10); + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024); + rd1_Q20 = Inlines.silk_SMULBB(q1_Q10, Lambda_Q10); + rd2_Q20 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } else if (q1_Q0 == 0) { + q1_Q10 = offset_Q10; + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024 - SilkConstants.QUANT_LEVEL_ADJUST_Q10); + rd1_Q20 = Inlines.silk_SMULBB(q1_Q10, Lambda_Q10); + rd2_Q20 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } else if (q1_Q0 == -1) { + q2_Q10 = offset_Q10; + q1_Q10 = Inlines.silk_SUB32(q2_Q10, 1024 - SilkConstants.QUANT_LEVEL_ADJUST_Q10); + rd1_Q20 = Inlines.silk_SMULBB(-q1_Q10, Lambda_Q10); + rd2_Q20 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } else { + /* Q1_Q0 < -1 */ + q1_Q10 = Inlines.silk_ADD32(Inlines.silk_LSHIFT(q1_Q0, 10), SilkConstants.QUANT_LEVEL_ADJUST_Q10); + q1_Q10 = Inlines.silk_ADD32(q1_Q10, offset_Q10); + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024); + rd1_Q20 = Inlines.silk_SMULBB(-q1_Q10, Lambda_Q10); + rd2_Q20 = Inlines.silk_SMULBB(-q2_Q10, Lambda_Q10); + } + rr_Q10 = Inlines.silk_SUB32(r_Q10, q1_Q10); + rd1_Q20 = Inlines.silk_SMLABB(rd1_Q20, rr_Q10, rr_Q10); + rr_Q10 = Inlines.silk_SUB32(r_Q10, q2_Q10); + rd2_Q20 = Inlines.silk_SMLABB(rd2_Q20, rr_Q10, rr_Q10); + + if (rd2_Q20 < rd1_Q20) { + q1_Q10 = q2_Q10; + } + + pulses[pulses_ptr + i] = (byte) Inlines.silk_RSHIFT_ROUND(q1_Q10, 10); + + /* Excitation */ + exc_Q14 = Inlines.silk_LSHIFT(q1_Q10, 4); + if (this.rand_seed < 0) { + exc_Q14 = -exc_Q14; + } + + /* Add predictions */ + LPC_exc_Q14 = Inlines.silk_ADD_LSHIFT32(exc_Q14, LTP_pred_Q13, 1); + xq_Q14 = Inlines.silk_ADD_LSHIFT32(LPC_exc_Q14, LPC_pred_Q10, 4); + + /* Scale XQ back to normal level before saving */ + xq[xq_ptr + i] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(xq_Q14, Gain_Q10), 8)); + + /* Update states */ + psLPC_Q14 += 1; + this.sLPC_Q14[psLPC_Q14] = xq_Q14; + sLF_AR_shp_Q14 = Inlines.silk_SUB_LSHIFT32(xq_Q14, n_AR_Q12, 2); + this.sLF_AR_shp_Q14 = sLF_AR_shp_Q14; + + this.sLTP_shp_Q14[this.sLTP_shp_buf_idx] = Inlines.silk_SUB_LSHIFT32(sLF_AR_shp_Q14, n_LF_Q12, 2); + sLTP_Q15[this.sLTP_buf_idx] = Inlines.silk_LSHIFT(LPC_exc_Q14, 1); + this.sLTP_shp_buf_idx++; + this.sLTP_buf_idx++; + + /* Make dither dependent on quantized signal */ + this.rand_seed = Inlines.silk_ADD32_ovflw(this.rand_seed, pulses[pulses_ptr + i]); + } + + /* Update LPC synth buffer */ + System.arraycopy(this.sLPC_Q14, length, this.sLPC_Q14, 0, SilkConstants.NSQ_LPC_BUF_LENGTH); + } + + private void silk_nsq_scale_states( + SilkChannelEncoder psEncC, /* I Encoder State */ + int[] x_Q3, /* I input in Q3 */ + int x_Q3_ptr, + int[] x_sc_Q10, /* O input scaled with 1/Gain */ + short[] sLTP, /* I re-whitened LTP state in Q0 */ + int[] sLTP_Q15, /* O LTP state matching scaled input */ + int subfr, /* I subframe number */ + int LTP_scale_Q14, /* I */ + int[] Gains_Q16, /* I [MAX_NB_SUBFR] */ + int[] pitchL, /* I Pitch lag [MAX_NB_SUBFR] */ + int signal_type /* I Signal type */ + ) { + int i, lag; + int gain_adj_Q16, inv_gain_Q31, inv_gain_Q23; + + lag = pitchL[subfr]; + inv_gain_Q31 = Inlines.silk_INVERSE32_varQ(Inlines.silk_max(Gains_Q16[subfr], 1), 47); + Inlines.OpusAssert(inv_gain_Q31 != 0); + + /* Calculate gain adjustment factor */ + if (Gains_Q16[subfr] != this.prev_gain_Q16) { + gain_adj_Q16 = Inlines.silk_DIV32_varQ(this.prev_gain_Q16, Gains_Q16[subfr], 16); + } else { + gain_adj_Q16 = (int) 1 << 16; + } + + /* Scale input */ + inv_gain_Q23 = Inlines.silk_RSHIFT_ROUND(inv_gain_Q31, 8); + for (i = 0; i < psEncC.subfr_length; i++) { + x_sc_Q10[i] = Inlines.silk_SMULWW(x_Q3[x_Q3_ptr + i], inv_gain_Q23); + } + + /* Save inverse gain */ + this.prev_gain_Q16 = Gains_Q16[subfr]; + + /* After rewhitening the LTP state is un-scaled, so scale with inv_gain_Q16 */ + if (this.rewhite_flag != 0) { + if (subfr == 0) { + /* Do LTP downscaling */ + inv_gain_Q31 = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(inv_gain_Q31, LTP_scale_Q14), 2); + } + for (i = this.sLTP_buf_idx - lag - SilkConstants.LTP_ORDER / 2; i < this.sLTP_buf_idx; i++) { + Inlines.OpusAssert(i < SilkConstants.MAX_FRAME_LENGTH); + sLTP_Q15[i] = Inlines.silk_SMULWB(inv_gain_Q31, sLTP[i]); + } + } + + /* Adjust for changing gain */ + if (gain_adj_Q16 != (int) 1 << 16) { + /* Scale long-term shaping state */ + for (i = this.sLTP_shp_buf_idx - psEncC.ltp_mem_length; i < this.sLTP_shp_buf_idx; i++) { + this.sLTP_shp_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, this.sLTP_shp_Q14[i]); + } + + /* Scale long-term prediction state */ + if (signal_type == SilkConstants.TYPE_VOICED && this.rewhite_flag == 0) { + for (i = this.sLTP_buf_idx - lag - SilkConstants.LTP_ORDER / 2; i < this.sLTP_buf_idx; i++) { + sLTP_Q15[i] = Inlines.silk_SMULWW(gain_adj_Q16, sLTP_Q15[i]); + } + } + + this.sLF_AR_shp_Q14 = Inlines.silk_SMULWW(gain_adj_Q16, this.sLF_AR_shp_Q14); + + /* Scale short-term prediction and shaping states */ + for (i = 0; i < SilkConstants.NSQ_LPC_BUF_LENGTH; i++) { + this.sLPC_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, this.sLPC_Q14[i]); + } + for (i = 0; i < SilkConstants.MAX_SHAPE_LPC_ORDER; i++) { + this.sAR2_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, this.sAR2_Q14[i]); + } + } + } + + void silk_NSQ_del_dec( + SilkChannelEncoder psEncC, /* I Encoder State */ + SideInfoIndices psIndices, /* I/O Quantization Indices */ + int[] x_Q3, /* I Prefiltered input signal */ + byte[] pulses, /* O Quantized pulse signal */ + short[][] PredCoef_Q12, /* I Short term prediction coefs [2 * MAX_LPC_ORDER] */ + short[] LTPCoef_Q14, /* I Long term prediction coefs LTP_ORDER * MAX_NB_SUBFR] */ + short[] AR2_Q13, /* I Noise shaping coefs [MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER] */ + int[] HarmShapeGain_Q14, /* I Long term shaping coefs [MAX_NB_SUBFR] */ + int[] Tilt_Q14, /* I Spectral tilt [MAX_NB_SUBFR] */ + int[] LF_shp_Q14, /* I Low frequency shaping coefs [MAX_NB_SUBFR] */ + int[] Gains_Q16, /* I Quantization step sizes [MAX_NB_SUBFR] */ + int[] pitchL, /* I Pitch lags [MAX_NB_SUBFR] */ + int Lambda_Q10, /* I Rate/distortion tradeoff */ + int LTP_scale_Q14 /* I LTP state scaling */ + ) { + int i, k, lag, start_idx, LSF_interpolation_flag, Winner_ind, subfr; + int last_smple_idx, smpl_buf_idx, decisionDelay; + int A_Q12; + int pulses_ptr = 0; + int pxq; + int[] sLTP_Q15; + short[] sLTP; + int HarmShapeFIRPacked_Q14; + int offset_Q10; + int RDmin_Q10, Gain_Q10; + int[] x_sc_Q10; + int[] delayedGain_Q10; + int x_Q3_ptr = 0; + NSQ_del_dec_struct[] psDelDec; + NSQ_del_dec_struct psDD; + + /* Set unvoiced lag to the previous one, overwrite later for voiced */ + lag = this.lagPrev; + + Inlines.OpusAssert(this.prev_gain_Q16 != 0); + + /* Initialize delayed decision states */ + psDelDec = new NSQ_del_dec_struct[psEncC.nStatesDelayedDecision]; + for (int c = 0; c < psEncC.nStatesDelayedDecision; c++) { + psDelDec[c] = new NSQ_del_dec_struct(psEncC.shapingLPCOrder); + } + + for (k = 0; k < psEncC.nStatesDelayedDecision; k++) { + psDD = psDelDec[k]; + psDD.Seed = (k + psIndices.Seed) & 3; + psDD.SeedInit = psDD.Seed; + psDD.RD_Q10 = 0; + psDD.LF_AR_Q14 = this.sLF_AR_shp_Q14; + psDD.Shape_Q14[0] = this.sLTP_shp_Q14[psEncC.ltp_mem_length - 1]; + System.arraycopy(this.sLPC_Q14, 0, psDD.sLPC_Q14, 0, SilkConstants.NSQ_LPC_BUF_LENGTH); + System.arraycopy(this.sAR2_Q14, 0, psDD.sAR2_Q14, 0, psEncC.shapingLPCOrder); + } + + offset_Q10 = SilkTables.silk_Quantization_Offsets_Q10[psIndices.signalType >> 1][psIndices.quantOffsetType]; + smpl_buf_idx = 0; + /* index of oldest samples */ + + decisionDelay = Inlines.silk_min_int(SilkConstants.DECISION_DELAY, psEncC.subfr_length); + + /* For voiced frames limit the decision delay to lower than the pitch lag */ + if (psIndices.signalType == SilkConstants.TYPE_VOICED) { + for (k = 0; k < psEncC.nb_subfr; k++) { + decisionDelay = Inlines.silk_min_int(decisionDelay, pitchL[k] - SilkConstants.LTP_ORDER / 2 - 1); + } + } else if (lag > 0) { + decisionDelay = Inlines.silk_min_int(decisionDelay, lag - SilkConstants.LTP_ORDER / 2 - 1); + } + + if (psIndices.NLSFInterpCoef_Q2 == 4) { + LSF_interpolation_flag = 0; + } else { + LSF_interpolation_flag = 1; + } + + sLTP_Q15 = new int[psEncC.ltp_mem_length + psEncC.frame_length]; + sLTP = new short[psEncC.ltp_mem_length + psEncC.frame_length]; + x_sc_Q10 = new int[psEncC.subfr_length]; + delayedGain_Q10 = new int[SilkConstants.DECISION_DELAY]; + + /* Set up pointers to start of sub frame */ + pxq = psEncC.ltp_mem_length; + this.sLTP_shp_buf_idx = psEncC.ltp_mem_length; + this.sLTP_buf_idx = psEncC.ltp_mem_length; + subfr = 0; + for (k = 0; k < psEncC.nb_subfr; k++) { + A_Q12 = (((k >> 1) | (1 - LSF_interpolation_flag))); + + /* Noise shape parameters */ + Inlines.OpusAssert(HarmShapeGain_Q14[k] >= 0); + HarmShapeFIRPacked_Q14 = Inlines.silk_RSHIFT(HarmShapeGain_Q14[k], 2); + HarmShapeFIRPacked_Q14 |= Inlines.silk_LSHIFT((int) Inlines.silk_RSHIFT(HarmShapeGain_Q14[k], 1), 16); + + this.rewhite_flag = 0; + if (psIndices.signalType == SilkConstants.TYPE_VOICED) { + /* Voiced */ + lag = pitchL[k]; + + /* Re-whitening */ + if ((k & (3 - Inlines.silk_LSHIFT(LSF_interpolation_flag, 1))) == 0) { + if (k == 2) { + /* RESET DELAYED DECISIONS */ + /* Find winner */ + RDmin_Q10 = psDelDec[0].RD_Q10; + Winner_ind = 0; + for (i = 1; i < psEncC.nStatesDelayedDecision; i++) { + if (psDelDec[i].RD_Q10 < RDmin_Q10) { + RDmin_Q10 = psDelDec[i].RD_Q10; + Winner_ind = i; + } + } + for (i = 0; i < psEncC.nStatesDelayedDecision; i++) { + if (i != Winner_ind) { + psDelDec[i].RD_Q10 += (Integer.MAX_VALUE >> 4); + Inlines.OpusAssert(psDelDec[i].RD_Q10 >= 0); + } + } + + /* Copy final part of signals from winner state to output and long-term filter states */ + psDD = psDelDec[Winner_ind]; + last_smple_idx = smpl_buf_idx + decisionDelay; + for (i = 0; i < decisionDelay; i++) { + last_smple_idx = (last_smple_idx - 1) & SilkConstants.DECISION_DELAY_MASK; + pulses[pulses_ptr + i - decisionDelay] = (byte) Inlines.silk_RSHIFT_ROUND(psDD.Q_Q10[last_smple_idx], 10); + this.xq[pxq + i - decisionDelay] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND( + Inlines.silk_SMULWW(psDD.Xq_Q14[last_smple_idx], Gains_Q16[1]), 14)); + this.sLTP_shp_Q14[this.sLTP_shp_buf_idx - decisionDelay + i] = psDD.Shape_Q14[last_smple_idx]; + } + + subfr = 0; + } + + /* Rewhiten with new A coefs */ + start_idx = psEncC.ltp_mem_length - lag - psEncC.predictLPCOrder - SilkConstants.LTP_ORDER / 2; + Inlines.OpusAssert(start_idx > 0); + + Filters.silk_LPC_analysis_filter(sLTP, start_idx, this.xq, start_idx + k * psEncC.subfr_length, + PredCoef_Q12[A_Q12], 0, psEncC.ltp_mem_length - start_idx, psEncC.predictLPCOrder); + + this.sLTP_buf_idx = psEncC.ltp_mem_length; + this.rewhite_flag = 1; + } + } + + silk_nsq_del_dec_scale_states( + psEncC, + psDelDec, + x_Q3, + x_Q3_ptr, + x_sc_Q10, + sLTP, + sLTP_Q15, + k, + psEncC.nStatesDelayedDecision, + LTP_scale_Q14, + Gains_Q16, + pitchL, + psIndices.signalType, + decisionDelay); + + BoxedValueInt smpl_buf_idx_boxed = new BoxedValueInt(smpl_buf_idx); + silk_noise_shape_quantizer_del_dec( + psDelDec, + psIndices.signalType, + x_sc_Q10, + pulses, + pulses_ptr, + this.xq, + pxq, + sLTP_Q15, + delayedGain_Q10, + PredCoef_Q12[A_Q12], + LTPCoef_Q14, + k * SilkConstants.LTP_ORDER, + AR2_Q13, + k * SilkConstants.MAX_SHAPE_LPC_ORDER, + lag, + HarmShapeFIRPacked_Q14, + Tilt_Q14[k], + LF_shp_Q14[k], + Gains_Q16[k], + Lambda_Q10, + offset_Q10, + psEncC.subfr_length, + subfr++, + psEncC.shapingLPCOrder, + psEncC.predictLPCOrder, + psEncC.warping_Q16, + psEncC.nStatesDelayedDecision, + smpl_buf_idx_boxed, + decisionDelay); + smpl_buf_idx = smpl_buf_idx_boxed.Val; + + x_Q3_ptr += psEncC.subfr_length; + pulses_ptr += psEncC.subfr_length; + pxq += psEncC.subfr_length; + } + + /* Find winner */ + RDmin_Q10 = psDelDec[0].RD_Q10; + Winner_ind = 0; + for (k = 1; k < psEncC.nStatesDelayedDecision; k++) { + if (psDelDec[k].RD_Q10 < RDmin_Q10) { + RDmin_Q10 = psDelDec[k].RD_Q10; + Winner_ind = k; + } + } + + /* Copy final part of signals from winner state to output and long-term filter states */ + psDD = psDelDec[Winner_ind]; + psIndices.Seed = (byte) (psDD.SeedInit); + last_smple_idx = smpl_buf_idx + decisionDelay; + Gain_Q10 = Inlines.silk_RSHIFT32(Gains_Q16[psEncC.nb_subfr - 1], 6); + for (i = 0; i < decisionDelay; i++) { + last_smple_idx = (last_smple_idx - 1) & SilkConstants.DECISION_DELAY_MASK; + pulses[pulses_ptr + i - decisionDelay] = (byte) Inlines.silk_RSHIFT_ROUND(psDD.Q_Q10[last_smple_idx], 10); + this.xq[pxq + i - decisionDelay] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND( + Inlines.silk_SMULWW(psDD.Xq_Q14[last_smple_idx], Gain_Q10), 8)); + this.sLTP_shp_Q14[this.sLTP_shp_buf_idx - decisionDelay + i] = psDD.Shape_Q14[last_smple_idx]; + } + System.arraycopy(psDD.sLPC_Q14, psEncC.subfr_length, this.sLPC_Q14, 0, SilkConstants.NSQ_LPC_BUF_LENGTH); + System.arraycopy(psDD.sAR2_Q14, 0, this.sAR2_Q14, 0, psEncC.shapingLPCOrder); + + /* Update states */ + this.sLF_AR_shp_Q14 = psDD.LF_AR_Q14; + this.lagPrev = pitchL[psEncC.nb_subfr - 1]; + + /* Save quantized speech signal */ + Arrays.MemMove(this.xq, psEncC.frame_length, 0, psEncC.ltp_mem_length); + Arrays.MemMove(this.sLTP_shp_Q14, psEncC.frame_length, 0, psEncC.ltp_mem_length); + } + + /** + * *************************************** + */ + /* Noise shape quantizer for one subframe */ + /** + * *************************************** + */ + private void silk_noise_shape_quantizer_del_dec( + NSQ_del_dec_struct[] psDelDec, /* I/O Delayed decision states */ + int signalType, /* I Signal type */ + int[] x_Q10, /* I */ + byte[] pulses, /* O */ + int pulses_ptr, + short[] xq, /* O */ + int xq_ptr, + int[] sLTP_Q15, /* I/O LTP filter state */ + int[] delayedGain_Q10, /* I/O Gain delay buffer */ + short[] a_Q12, /* I Short term prediction coefs */ + short[] b_Q14, /* I Long term prediction coefs */ + int b_Q14_ptr, + short[] AR_shp_Q13, /* I Noise shaping coefs */ + int AR_shp_Q13_ptr, + int lag, /* I Pitch lag */ + int HarmShapeFIRPacked_Q14, /* I */ + int Tilt_Q14, /* I Spectral tilt */ + int LF_shp_Q14, /* I */ + int Gain_Q16, /* I */ + int Lambda_Q10, /* I */ + int offset_Q10, /* I */ + int length, /* I Input length */ + int subfr, /* I Subframe number */ + int shapingLPCOrder, /* I Shaping LPC filter order */ + int predictLPCOrder, /* I Prediction filter order */ + int warping_Q16, /* I */ + int nStatesDelayedDecision, /* I Number of states in decision tree */ + BoxedValueInt smpl_buf_idx, /* I Index to newest samples in buffers */ + int decisionDelay /* I */ + ) { + int i, j, k, Winner_ind, RDmin_ind, RDmax_ind, last_smple_idx; + int Winner_rand_state; + int LTP_pred_Q14, LPC_pred_Q14, n_AR_Q14, n_LTP_Q14; + int n_LF_Q14, r_Q10, rr_Q10, rd1_Q10, rd2_Q10, RDmin_Q10, RDmax_Q10; + int q1_Q0, q1_Q10, q2_Q10, exc_Q14, LPC_exc_Q14, xq_Q14, Gain_Q10; + int tmp1, tmp2, sLF_AR_shp_Q14; + int pred_lag_ptr, shp_lag_ptr, psLPC_Q14; + NSQ_sample_struct[] sampleStates; + NSQ_del_dec_struct psDD; + int SS_left; + int SS_right; + + Inlines.OpusAssert(nStatesDelayedDecision > 0); + sampleStates = new NSQ_sample_struct[2 * nStatesDelayedDecision]; + // [porting note] structs must be initialized manually here + for (int c = 0; c < 2 * nStatesDelayedDecision; c++) { + sampleStates[c] = new NSQ_sample_struct(); + } + + shp_lag_ptr = this.sLTP_shp_buf_idx - lag + SilkConstants.HARM_SHAPE_FIR_TAPS / 2; + pred_lag_ptr = this.sLTP_buf_idx - lag + SilkConstants.LTP_ORDER / 2; + Gain_Q10 = Inlines.silk_RSHIFT(Gain_Q16, 6); + + for (i = 0; i < length; i++) { + /* Perform common calculations used in all states */ + + /* Long-term prediction */ + if (signalType == SilkConstants.TYPE_VOICED) { + /* Unrolled loop */ + /* Avoids introducing a bias because Inlines.silk_SMLAWB() always rounds to -inf */ + LTP_pred_Q14 = 2; + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr], b_Q14[b_Q14_ptr + 0]); + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr - 1], b_Q14[b_Q14_ptr + 1]); + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr - 2], b_Q14[b_Q14_ptr + 2]); + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr - 3], b_Q14[b_Q14_ptr + 3]); + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr - 4], b_Q14[b_Q14_ptr + 4]); + LTP_pred_Q14 = Inlines.silk_LSHIFT(LTP_pred_Q14, 1); + /* Q13 . Q14 */ + pred_lag_ptr += 1; + } else { + LTP_pred_Q14 = 0; + } + + /* Long-term shaping */ + if (lag > 0) { + /* Symmetric, packed FIR coefficients */ + n_LTP_Q14 = Inlines.silk_SMULWB(Inlines.silk_ADD32(this.sLTP_shp_Q14[shp_lag_ptr], this.sLTP_shp_Q14[shp_lag_ptr - 2]), HarmShapeFIRPacked_Q14); + n_LTP_Q14 = Inlines.silk_SMLAWT(n_LTP_Q14, this.sLTP_shp_Q14[shp_lag_ptr - 1], HarmShapeFIRPacked_Q14); + n_LTP_Q14 = Inlines.silk_SUB_LSHIFT32(LTP_pred_Q14, n_LTP_Q14, 2); + /* Q12 . Q14 */ + shp_lag_ptr += 1; + } else { + n_LTP_Q14 = 0; + } + + for (k = 0; k < nStatesDelayedDecision; k++) { + /* Delayed decision state */ + psDD = psDelDec[k]; + int[] psDD_sAR2 = psDD.sAR2_Q14; + + /* Sample state */ + SS_left = 2 * k; + SS_right = SS_left + 1; + + /* Generate dither */ + psDD.Seed = Inlines.silk_RAND(psDD.Seed); + + /* Pointer used in short term prediction and shaping */ + psLPC_Q14 = SilkConstants.NSQ_LPC_BUF_LENGTH - 1 + i; + /* Short-term prediction */ + Inlines.OpusAssert(predictLPCOrder == 10 || predictLPCOrder == 16); + /* Avoids introducing a bias because Inlines.silk_SMLAWB() always rounds to -inf */ + LPC_pred_Q14 = Inlines.silk_RSHIFT(predictLPCOrder, 1); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14], a_Q12[0]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 1], a_Q12[1]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 2], a_Q12[2]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 3], a_Q12[3]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 4], a_Q12[4]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 5], a_Q12[5]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 6], a_Q12[6]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 7], a_Q12[7]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 8], a_Q12[8]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 9], a_Q12[9]); + if (predictLPCOrder == 16) { + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 10], a_Q12[10]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 11], a_Q12[11]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 12], a_Q12[12]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 13], a_Q12[13]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 14], a_Q12[14]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 15], a_Q12[15]); + } + LPC_pred_Q14 = Inlines.silk_LSHIFT(LPC_pred_Q14, 4); + /* Q10 . Q14 */ + + + /* Noise shape feedback */ + Inlines.OpusAssert((shapingLPCOrder & 1) == 0); + /* check that order is even */ + /* Output of lowpass section */ + tmp2 = Inlines.silk_SMLAWB(psDD.sLPC_Q14[psLPC_Q14], psDD_sAR2[0], warping_Q16); + /* Output of allpass section */ + tmp1 = Inlines.silk_SMLAWB(psDD_sAR2[0], psDD_sAR2[1] - tmp2, warping_Q16); + psDD_sAR2[0] = tmp2; + n_AR_Q14 = Inlines.silk_RSHIFT(shapingLPCOrder, 1); + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, tmp2, AR_shp_Q13[AR_shp_Q13_ptr]); + /* Loop over allpass sections */ + for (j = 2; j < shapingLPCOrder; j += 2) { + /* Output of allpass section */ + tmp2 = Inlines.silk_SMLAWB(psDD_sAR2[j - 1], psDD_sAR2[j + 0] - tmp1, warping_Q16); + psDD_sAR2[j - 1] = tmp1; + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, tmp1, AR_shp_Q13[AR_shp_Q13_ptr + j - 1]); + /* Output of allpass section */ + tmp1 = Inlines.silk_SMLAWB(psDD_sAR2[j + 0], psDD_sAR2[j + 1] - tmp2, warping_Q16); + psDD_sAR2[j + 0] = tmp2; + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, tmp2, AR_shp_Q13[AR_shp_Q13_ptr + j]); + } + psDD_sAR2[shapingLPCOrder - 1] = tmp1; + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, tmp1, AR_shp_Q13[AR_shp_Q13_ptr + shapingLPCOrder - 1]); + + n_AR_Q14 = Inlines.silk_LSHIFT(n_AR_Q14, 1); + /* Q11 . Q12 */ + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, psDD.LF_AR_Q14, Tilt_Q14); + /* Q12 */ + n_AR_Q14 = Inlines.silk_LSHIFT(n_AR_Q14, 2); + /* Q12 . Q14 */ + + n_LF_Q14 = Inlines.silk_SMULWB(psDD.Shape_Q14[smpl_buf_idx.Val], LF_shp_Q14); + /* Q12 */ + n_LF_Q14 = Inlines.silk_SMLAWT(n_LF_Q14, psDD.LF_AR_Q14, LF_shp_Q14); + /* Q12 */ + n_LF_Q14 = Inlines.silk_LSHIFT(n_LF_Q14, 2); + /* Q12 . Q14 */ + + /* Input minus prediction plus noise feedback */ + /* r = x[ i ] - LTP_pred - LPC_pred + n_AR + n_Tilt + n_LF + n_LTP */ + tmp1 = Inlines.silk_ADD32(n_AR_Q14, n_LF_Q14); + /* Q14 */ + tmp2 = Inlines.silk_ADD32(n_LTP_Q14, LPC_pred_Q14); + /* Q13 */ + tmp1 = Inlines.silk_SUB32(tmp2, tmp1); + /* Q13 */ + tmp1 = Inlines.silk_RSHIFT_ROUND(tmp1, 4); + /* Q10 */ + + r_Q10 = Inlines.silk_SUB32(x_Q10[i], tmp1); + /* residual error Q10 */ + + /* Flip sign depending on dither */ + if (psDD.Seed < 0) { + r_Q10 = -r_Q10; + } + r_Q10 = Inlines.silk_LIMIT_32(r_Q10, -(31 << 10), 30 << 10); + + /* Find two quantization level candidates and measure their rate-distortion */ + q1_Q10 = Inlines.silk_SUB32(r_Q10, offset_Q10); + q1_Q0 = Inlines.silk_RSHIFT(q1_Q10, 10); + if (q1_Q0 > 0) { + q1_Q10 = Inlines.silk_SUB32(Inlines.silk_LSHIFT(q1_Q0, 10), SilkConstants.QUANT_LEVEL_ADJUST_Q10); + q1_Q10 = Inlines.silk_ADD32(q1_Q10, offset_Q10); + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024); + rd1_Q10 = Inlines.silk_SMULBB(q1_Q10, Lambda_Q10); + rd2_Q10 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } else if (q1_Q0 == 0) { + q1_Q10 = offset_Q10; + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024 - SilkConstants.QUANT_LEVEL_ADJUST_Q10); + rd1_Q10 = Inlines.silk_SMULBB(q1_Q10, Lambda_Q10); + rd2_Q10 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } else if (q1_Q0 == -1) { + q2_Q10 = offset_Q10; + q1_Q10 = Inlines.silk_SUB32(q2_Q10, 1024 - SilkConstants.QUANT_LEVEL_ADJUST_Q10); + rd1_Q10 = Inlines.silk_SMULBB(-q1_Q10, Lambda_Q10); + rd2_Q10 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } else { + /* q1_Q0 < -1 */ + q1_Q10 = Inlines.silk_ADD32(Inlines.silk_LSHIFT(q1_Q0, 10), SilkConstants.QUANT_LEVEL_ADJUST_Q10); + q1_Q10 = Inlines.silk_ADD32(q1_Q10, offset_Q10); + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024); + rd1_Q10 = Inlines.silk_SMULBB(-q1_Q10, Lambda_Q10); + rd2_Q10 = Inlines.silk_SMULBB(-q2_Q10, Lambda_Q10); + } + rr_Q10 = Inlines.silk_SUB32(r_Q10, q1_Q10); + rd1_Q10 = Inlines.silk_RSHIFT(Inlines.silk_SMLABB(rd1_Q10, rr_Q10, rr_Q10), 10); + rr_Q10 = Inlines.silk_SUB32(r_Q10, q2_Q10); + rd2_Q10 = Inlines.silk_RSHIFT(Inlines.silk_SMLABB(rd2_Q10, rr_Q10, rr_Q10), 10); + + if (rd1_Q10 < rd2_Q10) { + sampleStates[SS_left].RD_Q10 = Inlines.silk_ADD32(psDD.RD_Q10, rd1_Q10); + sampleStates[SS_right].RD_Q10 = Inlines.silk_ADD32(psDD.RD_Q10, rd2_Q10); + sampleStates[SS_left].Q_Q10 = q1_Q10; + sampleStates[SS_right].Q_Q10 = q2_Q10; + } else { + sampleStates[SS_left].RD_Q10 = Inlines.silk_ADD32(psDD.RD_Q10, rd2_Q10); + sampleStates[SS_right].RD_Q10 = Inlines.silk_ADD32(psDD.RD_Q10, rd1_Q10); + sampleStates[SS_left].Q_Q10 = q2_Q10; + sampleStates[SS_right].Q_Q10 = q1_Q10; + } + + /* Update states for best quantization */ + + /* Quantized excitation */ + exc_Q14 = Inlines.silk_LSHIFT32(sampleStates[SS_left].Q_Q10, 4); + if (psDD.Seed < 0) { + exc_Q14 = -exc_Q14; + } + + /* Add predictions */ + LPC_exc_Q14 = Inlines.silk_ADD32(exc_Q14, LTP_pred_Q14); + xq_Q14 = Inlines.silk_ADD32(LPC_exc_Q14, LPC_pred_Q14); + + /* Update states */ + sLF_AR_shp_Q14 = Inlines.silk_SUB32(xq_Q14, n_AR_Q14); + sampleStates[SS_left].sLTP_shp_Q14 = Inlines.silk_SUB32(sLF_AR_shp_Q14, n_LF_Q14); + sampleStates[SS_left].LF_AR_Q14 = sLF_AR_shp_Q14; + sampleStates[SS_left].LPC_exc_Q14 = LPC_exc_Q14; + sampleStates[SS_left].xq_Q14 = xq_Q14; + + /* Update states for second best quantization */ + + /* Quantized excitation */ + exc_Q14 = Inlines.silk_LSHIFT32(sampleStates[SS_right].Q_Q10, 4); + if (psDD.Seed < 0) { + exc_Q14 = -exc_Q14; + } + + + /* Add predictions */ + LPC_exc_Q14 = Inlines.silk_ADD32(exc_Q14, LTP_pred_Q14); + xq_Q14 = Inlines.silk_ADD32(LPC_exc_Q14, LPC_pred_Q14); + + /* Update states */ + sLF_AR_shp_Q14 = Inlines.silk_SUB32(xq_Q14, n_AR_Q14); + sampleStates[SS_right].sLTP_shp_Q14 = Inlines.silk_SUB32(sLF_AR_shp_Q14, n_LF_Q14); + sampleStates[SS_right].LF_AR_Q14 = sLF_AR_shp_Q14; + sampleStates[SS_right].LPC_exc_Q14 = LPC_exc_Q14; + sampleStates[SS_right].xq_Q14 = xq_Q14; + } + + smpl_buf_idx.Val = (smpl_buf_idx.Val - 1) & SilkConstants.DECISION_DELAY_MASK; + /* Index to newest samples */ + last_smple_idx = (smpl_buf_idx.Val + decisionDelay) & SilkConstants.DECISION_DELAY_MASK; + /* Index to decisionDelay old samples */ + + /* Find winner */ + RDmin_Q10 = sampleStates[0].RD_Q10; + Winner_ind = 0; + for (k = 1; k < nStatesDelayedDecision; k++) { + if (sampleStates[k * 2].RD_Q10 < RDmin_Q10) { + RDmin_Q10 = sampleStates[k * 2].RD_Q10; + Winner_ind = k; + } + } + + /* Increase RD values of expired states */ + Winner_rand_state = psDelDec[Winner_ind].RandState[last_smple_idx]; + for (k = 0; k < nStatesDelayedDecision; k++) { + if (psDelDec[k].RandState[last_smple_idx] != Winner_rand_state) { + int k2 = k * 2; + sampleStates[k2].RD_Q10 = Inlines.silk_ADD32(sampleStates[k2].RD_Q10, Integer.MAX_VALUE >> 4); + sampleStates[k2 + 1].RD_Q10 = Inlines.silk_ADD32(sampleStates[k2 + 1].RD_Q10, Integer.MAX_VALUE >> 4); + Inlines.OpusAssert(sampleStates[k2].RD_Q10 >= 0); + } + } + + /* Find worst in first set and best in second set */ + RDmax_Q10 = sampleStates[0].RD_Q10; + RDmin_Q10 = sampleStates[1].RD_Q10; + RDmax_ind = 0; + RDmin_ind = 0; + for (k = 1; k < nStatesDelayedDecision; k++) { + int k2 = k * 2; + /* find worst in first set */ + if (sampleStates[k2].RD_Q10 > RDmax_Q10) { + RDmax_Q10 = sampleStates[k2].RD_Q10; + RDmax_ind = k; + } + /* find best in second set */ + if (sampleStates[k2 + 1].RD_Q10 < RDmin_Q10) { + RDmin_Q10 = sampleStates[k2 + 1].RD_Q10; + RDmin_ind = k; + } + } + + /* Replace a state if best from second set outperforms worst in first set */ + if (RDmin_Q10 < RDmax_Q10) { + psDelDec[RDmax_ind].PartialCopyFrom(psDelDec[RDmin_ind], i); + sampleStates[RDmax_ind * 2].Assign(sampleStates[RDmin_ind * 2 + 1]); + } + + /* Write samples from winner to output and long-term filter states */ + psDD = psDelDec[Winner_ind]; + if (subfr > 0 || i >= decisionDelay) { + pulses[pulses_ptr + i - decisionDelay] = (byte) Inlines.silk_RSHIFT_ROUND(psDD.Q_Q10[last_smple_idx], 10); + xq[xq_ptr + i - decisionDelay] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND( + Inlines.silk_SMULWW(psDD.Xq_Q14[last_smple_idx], delayedGain_Q10[last_smple_idx]), 8)); + this.sLTP_shp_Q14[this.sLTP_shp_buf_idx - decisionDelay] = psDD.Shape_Q14[last_smple_idx]; + sLTP_Q15[this.sLTP_buf_idx - decisionDelay] = psDD.Pred_Q15[last_smple_idx]; + } + this.sLTP_shp_buf_idx++; + this.sLTP_buf_idx++; + + /* Update states */ + for (k = 0; k < nStatesDelayedDecision; k++) { + psDD = psDelDec[k]; + SS_left = k * 2; + psDD.LF_AR_Q14 = sampleStates[SS_left].LF_AR_Q14; + psDD.sLPC_Q14[SilkConstants.NSQ_LPC_BUF_LENGTH + i] = sampleStates[SS_left].xq_Q14; + psDD.Xq_Q14[smpl_buf_idx.Val] = sampleStates[SS_left].xq_Q14; + psDD.Q_Q10[smpl_buf_idx.Val] = sampleStates[SS_left].Q_Q10; + psDD.Pred_Q15[smpl_buf_idx.Val] = Inlines.silk_LSHIFT32(sampleStates[SS_left].LPC_exc_Q14, 1); + psDD.Shape_Q14[smpl_buf_idx.Val] = sampleStates[SS_left].sLTP_shp_Q14; + psDD.Seed = Inlines.silk_ADD32_ovflw(psDD.Seed, Inlines.silk_RSHIFT_ROUND(sampleStates[SS_left].Q_Q10, 10)); + psDD.RandState[smpl_buf_idx.Val] = psDD.Seed; + psDD.RD_Q10 = sampleStates[SS_left].RD_Q10; + } + delayedGain_Q10[smpl_buf_idx.Val] = Gain_Q10; + } + + /* Update LPC states */ + for (k = 0; k < nStatesDelayedDecision; k++) { + psDD = psDelDec[k]; + System.arraycopy(psDD.sLPC_Q14, length, psDD.sLPC_Q14, 0, SilkConstants.NSQ_LPC_BUF_LENGTH); + } + } + + private void silk_nsq_del_dec_scale_states( + SilkChannelEncoder psEncC, /* I Encoder State */ + NSQ_del_dec_struct[] psDelDec, /* I/O Delayed decision states */ + int[] x_Q3, /* I Input in Q3 */ + int x_Q3_ptr, + int[] x_sc_Q10, /* O Input scaled with 1/Gain in Q10 */ + short[] sLTP, /* I Re-whitened LTP state in Q0 */ + int[] sLTP_Q15, /* O LTP state matching scaled input */ + int subfr, /* I Subframe number */ + int nStatesDelayedDecision, /* I Number of del dec states */ + int LTP_scale_Q14, /* I LTP state scaling */ + int[] Gains_Q16, /* I [MAX_NB_SUBFR] */ + int[] pitchL, /* I Pitch lag [MAX_NB_SUBFR] */ + int signal_type, /* I Signal type */ + int decisionDelay /* I Decision delay */ + ) { + int i, k, lag; + int gain_adj_Q16, inv_gain_Q31, inv_gain_Q23; + NSQ_del_dec_struct psDD; + + lag = pitchL[subfr]; + inv_gain_Q31 = Inlines.silk_INVERSE32_varQ(Inlines.silk_max(Gains_Q16[subfr], 1), 47); + Inlines.OpusAssert(inv_gain_Q31 != 0); + + /* Calculate gain adjustment factor */ + if (Gains_Q16[subfr] != this.prev_gain_Q16) { + gain_adj_Q16 = Inlines.silk_DIV32_varQ(this.prev_gain_Q16, Gains_Q16[subfr], 16); + } else { + gain_adj_Q16 = (int) 1 << 16; + } + + /* Scale input */ + inv_gain_Q23 = Inlines.silk_RSHIFT_ROUND(inv_gain_Q31, 8); + for (i = 0; i < psEncC.subfr_length; i++) { + x_sc_Q10[i] = Inlines.silk_SMULWW(x_Q3[x_Q3_ptr + i], inv_gain_Q23); + } + + /* Save inverse gain */ + this.prev_gain_Q16 = Gains_Q16[subfr]; + + /* After rewhitening the LTP state is un-scaled, so scale with inv_gain_Q16 */ + if (this.rewhite_flag != 0) { + if (subfr == 0) { + /* Do LTP downscaling */ + inv_gain_Q31 = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(inv_gain_Q31, LTP_scale_Q14), 2); + } + for (i = this.sLTP_buf_idx - lag - SilkConstants.LTP_ORDER / 2; i < this.sLTP_buf_idx; i++) { + Inlines.OpusAssert(i < SilkConstants.MAX_FRAME_LENGTH); + sLTP_Q15[i] = Inlines.silk_SMULWB(inv_gain_Q31, sLTP[i]); + } + } + + /* Adjust for changing gain */ + if (gain_adj_Q16 != (int) 1 << 16) { + /* Scale long-term shaping state */ + for (i = this.sLTP_shp_buf_idx - psEncC.ltp_mem_length; i < this.sLTP_shp_buf_idx; i++) { + this.sLTP_shp_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, this.sLTP_shp_Q14[i]); + } + + /* Scale long-term prediction state */ + if (signal_type == SilkConstants.TYPE_VOICED && this.rewhite_flag == 0) { + for (i = this.sLTP_buf_idx - lag - SilkConstants.LTP_ORDER / 2; i < this.sLTP_buf_idx - decisionDelay; i++) { + sLTP_Q15[i] = Inlines.silk_SMULWW(gain_adj_Q16, sLTP_Q15[i]); + } + } + + for (k = 0; k < nStatesDelayedDecision; k++) { + psDD = psDelDec[k]; + + /* Scale scalar states */ + psDD.LF_AR_Q14 = Inlines.silk_SMULWW(gain_adj_Q16, psDD.LF_AR_Q14); + + /* Scale short-term prediction and shaping states */ + for (i = 0; i < SilkConstants.NSQ_LPC_BUF_LENGTH; i++) { + psDD.sLPC_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, psDD.sLPC_Q14[i]); + } + for (i = 0; i < psEncC.shapingLPCOrder; i++) { + psDD.sAR2_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, psDD.sAR2_Q14[i]); + } + for (i = 0; i < SilkConstants.DECISION_DELAY; i++) { + psDD.Pred_Q15[i] = Inlines.silk_SMULWW(gain_adj_Q16, psDD.Pred_Q15[i]); + psDD.Shape_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, psDD.Shape_Q14[i]); + } + } + } + } +} diff --git a/app/src/main/java/org/concentus/SilkPrefilterState.java b/app/src/main/java/org/concentus/SilkPrefilterState.java new file mode 100644 index 0000000000..cff834ed8d --- /dev/null +++ b/app/src/main/java/org/concentus/SilkPrefilterState.java @@ -0,0 +1,62 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Prefilter state +/// +class SilkPrefilterState { + + final short[] sLTP_shp = new short[SilkConstants.LTP_BUF_LENGTH]; + final int[] sAR_shp = new int[SilkConstants.MAX_SHAPE_LPC_ORDER + 1]; + int sLTP_shp_buf_idx = 0; + int sLF_AR_shp_Q12 = 0; + int sLF_MA_shp_Q12 = 0; + int sHarmHP_Q2 = 0; + int rand_seed = 0; + int lagPrev = 0; + + SilkPrefilterState() { + + } + + void Reset() { + Arrays.MemSet(sLTP_shp, (short) 0, SilkConstants.LTP_BUF_LENGTH); + Arrays.MemSet(sAR_shp, 0, SilkConstants.MAX_SHAPE_LPC_ORDER + 1); + sLTP_shp_buf_idx = 0; + sLF_AR_shp_Q12 = 0; + sLF_MA_shp_Q12 = 0; + sHarmHP_Q2 = 0; + rand_seed = 0; + lagPrev = 0; + } +} diff --git a/app/src/main/java/org/concentus/SilkResamplerState.java b/app/src/main/java/org/concentus/SilkResamplerState.java new file mode 100644 index 0000000000..184352f6ba --- /dev/null +++ b/app/src/main/java/org/concentus/SilkResamplerState.java @@ -0,0 +1,87 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class SilkResamplerState { + + final int[] sIIR = new int[SilkConstants.SILK_RESAMPLER_MAX_IIR_ORDER]; + /* this must be the first element of this struct FIXME why? */ + final int[] sFIR_i32 = new int[SilkConstants.SILK_RESAMPLER_MAX_FIR_ORDER]; // porting note: these two fields were originally a union, so that means only 1 will ever be used at a time. + final short[] sFIR_i16 = new short[SilkConstants.SILK_RESAMPLER_MAX_FIR_ORDER]; + + final short[] delayBuf = new short[48]; + int resampler_function = 0; + int batchSize = 0; + int invRatio_Q16 = 0; + int FIR_Order = 0; + int FIR_Fracs = 0; + int Fs_in_kHz = 0; + int Fs_out_kHz = 0; + int inputDelay = 0; + + /// + /// POINTER + /// + short[] Coefs = null; + + void Reset() { + Arrays.MemSet(sIIR, 0, SilkConstants.SILK_RESAMPLER_MAX_IIR_ORDER); + Arrays.MemSet(sFIR_i32, 0, SilkConstants.SILK_RESAMPLER_MAX_FIR_ORDER); + Arrays.MemSet(sFIR_i16, (short) 0, SilkConstants.SILK_RESAMPLER_MAX_FIR_ORDER); + Arrays.MemSet(delayBuf, (short) 0, 48); + resampler_function = 0; + batchSize = 0; + invRatio_Q16 = 0; + FIR_Order = 0; + FIR_Fracs = 0; + Fs_in_kHz = 0; + Fs_out_kHz = 0; + inputDelay = 0; + Coefs = null; + } + + void Assign(SilkResamplerState other) { + resampler_function = other.resampler_function; + batchSize = other.batchSize; + invRatio_Q16 = other.invRatio_Q16; + FIR_Order = other.FIR_Order; + FIR_Fracs = other.FIR_Fracs; + Fs_in_kHz = other.Fs_in_kHz; + Fs_out_kHz = other.Fs_out_kHz; + inputDelay = other.inputDelay; + Coefs = other.Coefs; + System.arraycopy(other.sIIR, 0, this.sIIR, 0, SilkConstants.SILK_RESAMPLER_MAX_IIR_ORDER); + System.arraycopy(other.sFIR_i32, 0, this.sFIR_i32, 0, SilkConstants.SILK_RESAMPLER_MAX_FIR_ORDER); + System.arraycopy(other.sFIR_i16, 0, this.sFIR_i16, 0, SilkConstants.SILK_RESAMPLER_MAX_FIR_ORDER); + System.arraycopy(other.delayBuf, 0, this.delayBuf, 0, 48); + } +} diff --git a/app/src/main/java/org/concentus/SilkShapeState.java b/app/src/main/java/org/concentus/SilkShapeState.java new file mode 100644 index 0000000000..e5f08c90da --- /dev/null +++ b/app/src/main/java/org/concentus/SilkShapeState.java @@ -0,0 +1,50 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Noise shaping analysis state +/// +class SilkShapeState { + + byte LastGainIndex = 0; + int HarmBoost_smth_Q16 = 0; + int HarmShapeGain_smth_Q16 = 0; + int Tilt_smth_Q16 = 0; + + void Reset() { + LastGainIndex = 0; + HarmBoost_smth_Q16 = 0; + HarmShapeGain_smth_Q16 = 0; + Tilt_smth_Q16 = 0; + } +} diff --git a/app/src/main/java/org/concentus/SilkTables.java b/app/src/main/java/org/concentus/SilkTables.java new file mode 100644 index 0000000000..4ebe46804a --- /dev/null +++ b/app/src/main/java/org/concentus/SilkTables.java @@ -0,0 +1,1050 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class SilkTables { + + /// + /// Cosine approximation table for LSF conversion + /// Q12 values (even) + /// + static final short[] silk_LSFCosTab_Q12 = { + 8192, 8190, 8182, 8170, + 8152, 8130, 8104, 8072, + 8034, 7994, 7946, 7896, + 7840, 7778, 7714, 7644, + 7568, 7490, 7406, 7318, + 7226, 7128, 7026, 6922, + 6812, 6698, 6580, 6458, + 6332, 6204, 6070, 5934, + 5792, 5648, 5502, 5352, + 5198, 5040, 4880, 4718, + 4552, 4382, 4212, 4038, + 3862, 3684, 3502, 3320, + 3136, 2948, 2760, 2570, + 2378, 2186, 1990, 1794, + 1598, 1400, 1202, 1002, + 802, 602, 402, 202, + 0, -202, -402, -602, + -802, -1002, -1202, -1400, + -1598, -1794, -1990, -2186, + -2378, -2570, -2760, -2948, + -3136, -3320, -3502, -3684, + -3862, -4038, -4212, -4382, + -4552, -4718, -4880, -5040, + -5198, -5352, -5502, -5648, + -5792, -5934, -6070, -6204, + -6332, -6458, -6580, -6698, + -6812, -6922, -7026, -7128, + -7226, -7318, -7406, -7490, + -7568, -7644, -7714, -7778, + -7840, -7896, -7946, -7994, + -8034, -8072, -8104, -8130, + -8152, -8170, -8182, -8190, + -8192 + }; + + static final short[][] silk_gain_iCDF + = { + new short[]{ + 224, 112, 44, 15, 3, 2, 1, 0 + }, + new short[]{ + 254, 237, 192, 132, 70, 23, 4, 0 + }, + new short[]{ + 255, 252, 226, 155, 61, 11, 2, 0 + } + }; + + static final short[] silk_delta_gain_iCDF = { + 250, 245, 234, 203, 71, 50, 42, 38, + 35, 33, 31, 29, 28, 27, 26, 25, + 24, 23, 22, 21, 20, 19, 18, 17, + 16, 15, 14, 13, 12, 11, 10, 9, + 8, 7, 6, 5, 4, 3, 2, 1, + 0 + }; + + static final short[] silk_LTP_per_index_iCDF = { + 179, 99, 0 + }; + + static final short[] silk_LTP_gain_iCDF_0 = { + 71, 56, 43, 30, 21, 12, 6, 0 + }; + + static final short[] silk_LTP_gain_iCDF_1 = { + 199, 165, 144, 124, 109, 96, 84, 71, + 61, 51, 42, 32, 23, 15, 8, 0 + }; + + static final short[] silk_LTP_gain_iCDF_2 = { + 241, 225, 211, 199, 187, 175, 164, 153, + 142, 132, 123, 114, 105, 96, 88, 80, + 72, 64, 57, 50, 44, 38, 33, 29, + 24, 20, 16, 12, 9, 5, 2, 0 + }; + + static final short silk_LTP_gain_middle_avg_RD_Q14 = 12304; + + static final short[] silk_LTP_gain_BITS_Q5_0 = { + 15, 131, 138, 138, 155, 155, 173, 173 + }; + + static final short[] silk_LTP_gain_BITS_Q5_1 = { + 69, 93, 115, 118, 131, 138, 141, 138, + 150, 150, 155, 150, 155, 160, 166, 160 + }; + + static final short[] silk_LTP_gain_BITS_Q5_2 = { + 131, 128, 134, 141, 141, 141, 145, 145, + 145, 150, 155, 155, 155, 155, 160, 160, + 160, 160, 166, 166, 173, 173, 182, 192, + 182, 192, 192, 192, 205, 192, 205, 224 + }; + + static final short[][] silk_LTP_gain_iCDF_ptrs = { + silk_LTP_gain_iCDF_0, + silk_LTP_gain_iCDF_1, + silk_LTP_gain_iCDF_2 + }; + + static final short[][] silk_LTP_gain_BITS_Q5_ptrs = { + silk_LTP_gain_BITS_Q5_0, + silk_LTP_gain_BITS_Q5_1, + silk_LTP_gain_BITS_Q5_2 + }; + + static final byte[][] silk_LTP_gain_vq_0 + = { + new byte[]{4, 6, 24, 7, 5}, + new byte[]{0, 0, 2, 0, 0}, + new byte[]{12, 28, 41, 13, -4}, + new byte[]{-9, 15, 42, 25, 14}, + new byte[]{1, -2, 62, 41, -9}, + new byte[]{-10, 37, 65, -4, 3}, + new byte[]{-6, 4, 66, 7, -8}, + new byte[]{16, 14, 38, -3, 33} + }; + + static final byte[][] silk_LTP_gain_vq_1 + = { + new byte[]{13, 22, 39, 23, 12}, + new byte[]{-1, 36, 64, 27, -6}, + new byte[]{-7, 10, 55, 43, 17}, + new byte[]{1, 1, 8, 1, 1}, + new byte[]{6, -11, 74, 53, -9}, + new byte[]{-12, 55, 76, -12, 8}, + new byte[]{-3, 3, 93, 27, -4}, + new byte[]{26, 39, 59, 3, -8}, + new byte[]{2, 0, 77, 11, 9}, + new byte[]{-8, 22, 44, -6, 7}, + new byte[]{40, 9, 26, 3, 9}, + new byte[]{-7, 20, 101, -7, 4}, + new byte[]{3, -8, 42, 26, 0}, + new byte[]{-15, 33, 68, 2, 23}, + new byte[]{-2, 55, 46, -2, 15}, + new byte[]{3, -1, 21, 16, 41} + }; + + static final byte[][] silk_LTP_gain_vq_2 + = { + new byte[]{-6, 27, 61, 39, 5}, + new byte[]{-11, 42, 88, 4, 1}, + new byte[]{-2, 60, 65, 6, -4}, + new byte[]{-1, -5, 73, 56, 1}, + new byte[]{-9, 19, 94, 29, -9}, + new byte[]{0, 12, 99, 6, 4}, + new byte[]{8, -19, 102, 46, -13}, + new byte[]{3, 2, 13, 3, 2}, + new byte[]{9, -21, 84, 72, -18}, + new byte[]{-11, 46, 104, -22, 8}, + new byte[]{18, 38, 48, 23, 0}, + new byte[]{-16, 70, 83, -21, 11}, + new byte[]{5, -11, 117, 22, -8}, + new byte[]{-6, 23, 117, -12, 3}, + new byte[]{3, -8, 95, 28, 4}, + new byte[]{-10, 15, 77, 60, -15}, + new byte[]{-1, 4, 124, 2, -4}, + new byte[]{3, 38, 84, 24, -25}, + new byte[]{2, 13, 42, 13, 31}, + new byte[]{21, -4, 56, 46, -1}, + new byte[]{-1, 35, 79, -13, 19}, + new byte[]{-7, 65, 88, -9, -14}, + new byte[]{20, 4, 81, 49, -29}, + new byte[]{20, 0, 75, 3, -17}, + new byte[]{5, -9, 44, 92, -8}, + new byte[]{1, -3, 22, 69, 31}, + new byte[]{-6, 95, 41, -12, 5}, + new byte[]{39, 67, 16, -4, 1}, + new byte[]{0, -6, 120, 55, -36}, + new byte[]{-13, 44, 122, 4, -24}, + new byte[]{81, 5, 11, 3, 7}, + new byte[]{2, 0, 9, 10, 88} + }; + + static final byte[][][] silk_LTP_vq_ptrs_Q7 = { + silk_LTP_gain_vq_0, + silk_LTP_gain_vq_1, + silk_LTP_gain_vq_2 + }; + + /* Maximum frequency-dependent response of the pitch taps above, + computed as max(abs(freqz(taps))) */ + static final short[] silk_LTP_gain_vq_0_gain = { + 46, 2, 90, 87, 93, 91, 82, 98 + }; + + static final short[] silk_LTP_gain_vq_1_gain = { + 109, 120, 118, 12, 113, 115, 117, 119, + 99, 59, 87, 111, 63, 111, 112, 80 + }; + + static final short[] silk_LTP_gain_vq_2_gain = { + 126, 124, 125, 124, 129, 121, 126, 23, + 132, 127, 127, 127, 126, 127, 122, 133, + 130, 134, 101, 118, 119, 145, 126, 86, + 124, 120, 123, 119, 170, 173, 107, 109 + }; + + static final short[][] silk_LTP_vq_gain_ptrs_Q7 = { + silk_LTP_gain_vq_0_gain, + silk_LTP_gain_vq_1_gain, + silk_LTP_gain_vq_2_gain + }; + + static final byte[] silk_LTP_vq_sizes = { + 8, 16, 32 + }; + + static final short[] silk_NLSF_CB1_NB_MB_Q8 = { + 12, 35, 60, 83, 108, 132, 157, 180, + 206, 228, 15, 32, 55, 77, 101, 125, + 151, 175, 201, 225, 19, 42, 66, 89, + 114, 137, 162, 184, 209, 230, 12, 25, + 50, 72, 97, 120, 147, 172, 200, 223, + 26, 44, 69, 90, 114, 135, 159, 180, + 205, 225, 13, 22, 53, 80, 106, 130, + 156, 180, 205, 228, 15, 25, 44, 64, + 90, 115, 142, 168, 196, 222, 19, 24, + 62, 82, 100, 120, 145, 168, 190, 214, + 22, 31, 50, 79, 103, 120, 151, 170, + 203, 227, 21, 29, 45, 65, 106, 124, + 150, 171, 196, 224, 30, 49, 75, 97, + 121, 142, 165, 186, 209, 229, 19, 25, + 52, 70, 93, 116, 143, 166, 192, 219, + 26, 34, 62, 75, 97, 118, 145, 167, + 194, 217, 25, 33, 56, 70, 91, 113, + 143, 165, 196, 223, 21, 34, 51, 72, + 97, 117, 145, 171, 196, 222, 20, 29, + 50, 67, 90, 117, 144, 168, 197, 221, + 22, 31, 48, 66, 95, 117, 146, 168, + 196, 222, 24, 33, 51, 77, 116, 134, + 158, 180, 200, 224, 21, 28, 70, 87, + 106, 124, 149, 170, 194, 217, 26, 33, + 53, 64, 83, 117, 152, 173, 204, 225, + 27, 34, 65, 95, 108, 129, 155, 174, + 210, 225, 20, 26, 72, 99, 113, 131, + 154, 176, 200, 219, 34, 43, 61, 78, + 93, 114, 155, 177, 205, 229, 23, 29, + 54, 97, 124, 138, 163, 179, 209, 229, + 30, 38, 56, 89, 118, 129, 158, 178, + 200, 231, 21, 29, 49, 63, 85, 111, + 142, 163, 193, 222, 27, 48, 77, 103, + 133, 158, 179, 196, 215, 232, 29, 47, + 74, 99, 124, 151, 176, 198, 220, 237, + 33, 42, 61, 76, 93, 121, 155, 174, + 207, 225, 29, 53, 87, 112, 136, 154, + 170, 188, 208, 227, 24, 30, 52, 84, + 131, 150, 166, 186, 203, 229, 37, 48, + 64, 84, 104, 118, 156, 177, 201, 230 + }; + + static final short[] silk_NLSF_CB1_iCDF_NB_MB = { + 212, 178, 148, 129, 108, 96, 85, 82, + 79, 77, 61, 59, 57, 56, 51, 49, + 48, 45, 42, 41, 40, 38, 36, 34, + 31, 30, 21, 12, 10, 3, 1, 0, + 255, 245, 244, 236, 233, 225, 217, 203, + 190, 176, 175, 161, 149, 136, 125, 114, + 102, 91, 81, 71, 60, 52, 43, 35, + 28, 20, 19, 18, 12, 11, 5, 0 + }; + + static final short[] silk_NLSF_CB2_SELECT_NB_MB = { + 16, 0, 0, 0, 0, 99, 66, 36, + 36, 34, 36, 34, 34, 34, 34, 83, + 69, 36, 52, 34, 116, 102, 70, 68, + 68, 176, 102, 68, 68, 34, 65, 85, + 68, 84, 36, 116, 141, 152, 139, 170, + 132, 187, 184, 216, 137, 132, 249, 168, + 185, 139, 104, 102, 100, 68, 68, 178, + 218, 185, 185, 170, 244, 216, 187, 187, + 170, 244, 187, 187, 219, 138, 103, 155, + 184, 185, 137, 116, 183, 155, 152, 136, + 132, 217, 184, 184, 170, 164, 217, 171, + 155, 139, 244, 169, 184, 185, 170, 164, + 216, 223, 218, 138, 214, 143, 188, 218, + 168, 244, 141, 136, 155, 170, 168, 138, + 220, 219, 139, 164, 219, 202, 216, 137, + 168, 186, 246, 185, 139, 116, 185, 219, + 185, 138, 100, 100, 134, 100, 102, 34, + 68, 68, 100, 68, 168, 203, 221, 218, + 168, 167, 154, 136, 104, 70, 164, 246, + 171, 137, 139, 137, 155, 218, 219, 139 + }; + + static final short[] silk_NLSF_CB2_iCDF_NB_MB = { + 255, 254, 253, 238, 14, 3, 2, 1, + 0, 255, 254, 252, 218, 35, 3, 2, + 1, 0, 255, 254, 250, 208, 59, 4, + 2, 1, 0, 255, 254, 246, 194, 71, + 10, 2, 1, 0, 255, 252, 236, 183, + 82, 8, 2, 1, 0, 255, 252, 235, + 180, 90, 17, 2, 1, 0, 255, 248, + 224, 171, 97, 30, 4, 1, 0, 255, + 254, 236, 173, 95, 37, 7, 1, 0 + }; + + static final short[] silk_NLSF_CB2_BITS_NB_MB_Q5 = { + 255, 255, 255, 131, 6, 145, 255, 255, + 255, 255, 255, 236, 93, 15, 96, 255, + 255, 255, 255, 255, 194, 83, 25, 71, + 221, 255, 255, 255, 255, 162, 73, 34, + 66, 162, 255, 255, 255, 210, 126, 73, + 43, 57, 173, 255, 255, 255, 201, 125, + 71, 48, 58, 130, 255, 255, 255, 166, + 110, 73, 57, 62, 104, 210, 255, 255, + 251, 123, 65, 55, 68, 100, 171, 255 + }; + + static final short[] silk_NLSF_PRED_NB_MB_Q8 = { + 179, 138, 140, 148, 151, 149, 153, 151, + 163, 116, 67, 82, 59, 92, 72, 100, + 89, 92 + }; + + static final short[] silk_NLSF_DELTA_MIN_NB_MB_Q15 = { + 250, 3, 6, 3, 3, 3, 4, 3, + 3, 3, 461 + }; + + static final NLSFCodebook silk_NLSF_CB_NB_MB = new NLSFCodebook(); + + static { + silk_NLSF_CB_NB_MB.nVectors = 32; + silk_NLSF_CB_NB_MB.order = 10; + silk_NLSF_CB_NB_MB.quantStepSize_Q16 = (short) (((int) ((0.18f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.18f, 16)*/); + silk_NLSF_CB_NB_MB.invQuantStepSize_Q6 = (short) (((int) ((1.0f / 0.18f) * ((long) 1 << (6)) + 0.5))/*Inlines.SILK_CONST(1.0f / 0.18f, 6)*/); + silk_NLSF_CB_NB_MB.CB1_NLSF_Q8 = silk_NLSF_CB1_NB_MB_Q8; + silk_NLSF_CB_NB_MB.CB1_iCDF = silk_NLSF_CB1_iCDF_NB_MB; + silk_NLSF_CB_NB_MB.pred_Q8 = silk_NLSF_PRED_NB_MB_Q8; + silk_NLSF_CB_NB_MB.ec_sel = silk_NLSF_CB2_SELECT_NB_MB; + silk_NLSF_CB_NB_MB.ec_iCDF = silk_NLSF_CB2_iCDF_NB_MB; + silk_NLSF_CB_NB_MB.ec_Rates_Q5 = silk_NLSF_CB2_BITS_NB_MB_Q5; + silk_NLSF_CB_NB_MB.deltaMin_Q15 = silk_NLSF_DELTA_MIN_NB_MB_Q15; + } + ; + + static final short[] silk_NLSF_CB1_WB_Q8 = { + 7, 23, 38, 54, 69, 85, 100, 116, + 131, 147, 162, 178, 193, 208, 223, 239, + 13, 25, 41, 55, 69, 83, 98, 112, + 127, 142, 157, 171, 187, 203, 220, 236, + 15, 21, 34, 51, 61, 78, 92, 106, + 126, 136, 152, 167, 185, 205, 225, 240, + 10, 21, 36, 50, 63, 79, 95, 110, + 126, 141, 157, 173, 189, 205, 221, 237, + 17, 20, 37, 51, 59, 78, 89, 107, + 123, 134, 150, 164, 184, 205, 224, 240, + 10, 15, 32, 51, 67, 81, 96, 112, + 129, 142, 158, 173, 189, 204, 220, 236, + 8, 21, 37, 51, 65, 79, 98, 113, + 126, 138, 155, 168, 179, 192, 209, 218, + 12, 15, 34, 55, 63, 78, 87, 108, + 118, 131, 148, 167, 185, 203, 219, 236, + 16, 19, 32, 36, 56, 79, 91, 108, + 118, 136, 154, 171, 186, 204, 220, 237, + 11, 28, 43, 58, 74, 89, 105, 120, + 135, 150, 165, 180, 196, 211, 226, 241, + 6, 16, 33, 46, 60, 75, 92, 107, + 123, 137, 156, 169, 185, 199, 214, 225, + 11, 19, 30, 44, 57, 74, 89, 105, + 121, 135, 152, 169, 186, 202, 218, 234, + 12, 19, 29, 46, 57, 71, 88, 100, + 120, 132, 148, 165, 182, 199, 216, 233, + 17, 23, 35, 46, 56, 77, 92, 106, + 123, 134, 152, 167, 185, 204, 222, 237, + 14, 17, 45, 53, 63, 75, 89, 107, + 115, 132, 151, 171, 188, 206, 221, 240, + 9, 16, 29, 40, 56, 71, 88, 103, + 119, 137, 154, 171, 189, 205, 222, 237, + 16, 19, 36, 48, 57, 76, 87, 105, + 118, 132, 150, 167, 185, 202, 218, 236, + 12, 17, 29, 54, 71, 81, 94, 104, + 126, 136, 149, 164, 182, 201, 221, 237, + 15, 28, 47, 62, 79, 97, 115, 129, + 142, 155, 168, 180, 194, 208, 223, 238, + 8, 14, 30, 45, 62, 78, 94, 111, + 127, 143, 159, 175, 192, 207, 223, 239, + 17, 30, 49, 62, 79, 92, 107, 119, + 132, 145, 160, 174, 190, 204, 220, 235, + 14, 19, 36, 45, 61, 76, 91, 108, + 121, 138, 154, 172, 189, 205, 222, 238, + 12, 18, 31, 45, 60, 76, 91, 107, + 123, 138, 154, 171, 187, 204, 221, 236, + 13, 17, 31, 43, 53, 70, 83, 103, + 114, 131, 149, 167, 185, 203, 220, 237, + 17, 22, 35, 42, 58, 78, 93, 110, + 125, 139, 155, 170, 188, 206, 224, 240, + 8, 15, 34, 50, 67, 83, 99, 115, + 131, 146, 162, 178, 193, 209, 224, 239, + 13, 16, 41, 66, 73, 86, 95, 111, + 128, 137, 150, 163, 183, 206, 225, 241, + 17, 25, 37, 52, 63, 75, 92, 102, + 119, 132, 144, 160, 175, 191, 212, 231, + 19, 31, 49, 65, 83, 100, 117, 133, + 147, 161, 174, 187, 200, 213, 227, 242, + 18, 31, 52, 68, 88, 103, 117, 126, + 138, 149, 163, 177, 192, 207, 223, 239, + 16, 29, 47, 61, 76, 90, 106, 119, + 133, 147, 161, 176, 193, 209, 224, 240, + 15, 21, 35, 50, 61, 73, 86, 97, + 110, 119, 129, 141, 175, 198, 218, 237 + }; + + static final short[] silk_NLSF_CB1_iCDF_WB = { + 225, 204, 201, 184, 183, 175, 158, 154, + 153, 135, 119, 115, 113, 110, 109, 99, + 98, 95, 79, 68, 52, 50, 48, 45, + 43, 32, 31, 27, 18, 10, 3, 0, + 255, 251, 235, 230, 212, 201, 196, 182, + 167, 166, 163, 151, 138, 124, 110, 104, + 90, 78, 76, 70, 69, 57, 45, 34, + 24, 21, 11, 6, 5, 4, 3, 0 + }; + + static final short[] silk_NLSF_CB2_SELECT_WB = { + 0, 0, 0, 0, 0, 0, 0, 1, + 100, 102, 102, 68, 68, 36, 34, 96, + 164, 107, 158, 185, 180, 185, 139, 102, + 64, 66, 36, 34, 34, 0, 1, 32, + 208, 139, 141, 191, 152, 185, 155, 104, + 96, 171, 104, 166, 102, 102, 102, 132, + 1, 0, 0, 0, 0, 16, 16, 0, + 80, 109, 78, 107, 185, 139, 103, 101, + 208, 212, 141, 139, 173, 153, 123, 103, + 36, 0, 0, 0, 0, 0, 0, 1, + 48, 0, 0, 0, 0, 0, 0, 32, + 68, 135, 123, 119, 119, 103, 69, 98, + 68, 103, 120, 118, 118, 102, 71, 98, + 134, 136, 157, 184, 182, 153, 139, 134, + 208, 168, 248, 75, 189, 143, 121, 107, + 32, 49, 34, 34, 34, 0, 17, 2, + 210, 235, 139, 123, 185, 137, 105, 134, + 98, 135, 104, 182, 100, 183, 171, 134, + 100, 70, 68, 70, 66, 66, 34, 131, + 64, 166, 102, 68, 36, 2, 1, 0, + 134, 166, 102, 68, 34, 34, 66, 132, + 212, 246, 158, 139, 107, 107, 87, 102, + 100, 219, 125, 122, 137, 118, 103, 132, + 114, 135, 137, 105, 171, 106, 50, 34, + 164, 214, 141, 143, 185, 151, 121, 103, + 192, 34, 0, 0, 0, 0, 0, 1, + 208, 109, 74, 187, 134, 249, 159, 137, + 102, 110, 154, 118, 87, 101, 119, 101, + 0, 2, 0, 36, 36, 66, 68, 35, + 96, 164, 102, 100, 36, 0, 2, 33, + 167, 138, 174, 102, 100, 84, 2, 2, + 100, 107, 120, 119, 36, 197, 24, 0 + }; + + static final short[] silk_NLSF_CB2_iCDF_WB = { + 255, 254, 253, 244, 12, 3, 2, 1, + 0, 255, 254, 252, 224, 38, 3, 2, + 1, 0, 255, 254, 251, 209, 57, 4, + 2, 1, 0, 255, 254, 244, 195, 69, + 4, 2, 1, 0, 255, 251, 232, 184, + 84, 7, 2, 1, 0, 255, 254, 240, + 186, 86, 14, 2, 1, 0, 255, 254, + 239, 178, 91, 30, 5, 1, 0, 255, + 248, 227, 177, 100, 19, 2, 1, 0 + }; + + static final short[] silk_NLSF_CB2_BITS_WB_Q5 = { + 255, 255, 255, 156, 4, 154, 255, 255, + 255, 255, 255, 227, 102, 15, 92, 255, + 255, 255, 255, 255, 213, 83, 24, 72, + 236, 255, 255, 255, 255, 150, 76, 33, + 63, 214, 255, 255, 255, 190, 121, 77, + 43, 55, 185, 255, 255, 255, 245, 137, + 71, 43, 59, 139, 255, 255, 255, 255, + 131, 66, 50, 66, 107, 194, 255, 255, + 166, 116, 76, 55, 53, 125, 255, 255 + }; + + static final short[] silk_NLSF_PRED_WB_Q8 = { + 175, 148, 160, 176, 178, 173, 174, 164, + 177, 174, 196, 182, 198, 192, 182, 68, + 62, 66, 60, 72, 117, 85, 90, 118, + 136, 151, 142, 160, 142, 155 + }; + + static final short[] silk_NLSF_DELTA_MIN_WB_Q15 = { + 100, 3, 40, 3, 3, 3, 5, 14, + 14, 10, 11, 3, 8, 9, 7, 3, + 347 + }; + + static final NLSFCodebook silk_NLSF_CB_WB = new NLSFCodebook(); + + static { + silk_NLSF_CB_WB.nVectors = 32; + silk_NLSF_CB_WB.order = 16; + silk_NLSF_CB_WB.quantStepSize_Q16 = (short) (((int) ((0.15f) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.15f, 16)*/); + silk_NLSF_CB_WB.invQuantStepSize_Q6 = (short) (((int) ((1.0f / 0.15f) * ((long) 1 << (6)) + 0.5))/*Inlines.SILK_CONST(1.0f / 0.15f, 6)*/); + silk_NLSF_CB_WB.CB1_NLSF_Q8 = silk_NLSF_CB1_WB_Q8; + silk_NLSF_CB_WB.CB1_iCDF = silk_NLSF_CB1_iCDF_WB; + silk_NLSF_CB_WB.pred_Q8 = silk_NLSF_PRED_WB_Q8; + silk_NLSF_CB_WB.ec_sel = silk_NLSF_CB2_SELECT_WB; + silk_NLSF_CB_WB.ec_iCDF = silk_NLSF_CB2_iCDF_WB; + silk_NLSF_CB_WB.ec_Rates_Q5 = silk_NLSF_CB2_BITS_WB_Q5; + silk_NLSF_CB_WB.deltaMin_Q15 = silk_NLSF_DELTA_MIN_WB_Q15; + } + ; + + /* Piece-wise linear mapping from bitrate in kbps to coding quality in dB SNR */ + static final int[] silk_TargetRate_table_NB = { + 0, 8000, 9400, 11500, 13500, 17500, 25000, SilkConstants.MAX_TARGET_RATE_BPS + }; + static final int[] silk_TargetRate_table_MB = { + 0, 9000, 12000, 14500, 18500, 24500, 35500, SilkConstants.MAX_TARGET_RATE_BPS + }; + static final int[] silk_TargetRate_table_WB = { + 0, 10500, 14000, 17000, 21500, 28500, 42000, SilkConstants.MAX_TARGET_RATE_BPS + }; + static final short[] silk_SNR_table_Q1 = { + 18, 29, 38, 40, 46, 52, 62, 84 + }; + + /* Tables for stereo predictor coding */ + static final short[] silk_stereo_pred_quant_Q13 = { + -13732, -10050, -8266, -7526, -6500, -5000, -2950, -820, + 820, 2950, 5000, 6500, 7526, 8266, 10050, 13732 + }; + + static final short[] silk_stereo_pred_joint_iCDF = { + 249, 247, 246, 245, 244, + 234, 210, 202, 201, 200, + 197, 174, 82, 59, 56, + 55, 54, 46, 22, 12, + 11, 10, 9, 7, 0 + }; + static final short[] silk_stereo_only_code_mid_iCDF = {64, 0}; + + /* Tables for LBRR flags */ + static final short[] silk_LBRR_flags_2_iCDF = {203, 150, 0}; + static final short[] silk_LBRR_flags_3_iCDF = {215, 195, 166, 125, 110, 82, 0}; + static final short[][] silk_LBRR_flags_iCDF_ptr = { + silk_LBRR_flags_2_iCDF, + silk_LBRR_flags_3_iCDF + }; + + /* Table for LSB coding */ + static final short[] silk_lsb_iCDF = {120, 0}; + + /* Tables for LTPScale */ + static final short[] silk_LTPscale_iCDF = {128, 64, 0}; + + /* Tables for signal type and offset coding */ + static final short[] silk_type_offset_VAD_iCDF = { + 232, 158, 10, 0 + }; + static final short[] silk_type_offset_no_VAD_iCDF = { + 230, 0 + }; + + /* Tables for NLSF interpolation factor */ + static final short[] silk_NLSF_interpolation_factor_iCDF = {243, 221, 192, 181, 0}; + + /* Quantization offsets */ + static final short[][] silk_Quantization_Offsets_Q10 = { + new short[]{SilkConstants.OFFSET_UVL_Q10, SilkConstants.OFFSET_UVH_Q10}, + new short[]{SilkConstants.OFFSET_VL_Q10, SilkConstants.OFFSET_VH_Q10} + }; + + /* Table for LTPScale */ + static final short[] silk_LTPScales_table_Q14 = {15565, 12288, 8192}; + + /* Uniform entropy tables */ + static final short[] silk_uniform3_iCDF = {171, 85, 0}; + static final short[] silk_uniform4_iCDF = {192, 128, 64, 0}; + static final short[] silk_uniform5_iCDF = {205, 154, 102, 51, 0}; + static final short[] silk_uniform6_iCDF = {213, 171, 128, 85, 43, 0}; + static final short[] silk_uniform8_iCDF = {224, 192, 160, 128, 96, 64, 32, 0}; + + static final short[] silk_NLSF_EXT_iCDF = {100, 40, 16, 7, 3, 1, 0}; + + /* Elliptic/Cauer filters designed with 0.1 dB passband ripple, + 80 dB minimum stopband attenuation, and + [0.95 : 0.15 : 0.35] normalized cut off frequencies. */ + + /* Interpolation points for filter coefficients used in the bandwidth transition smoother */ + static final int[][] silk_Transition_LP_B_Q28 + = { + new int[]{250767114, 501534038, 250767114}, + new int[]{209867381, 419732057, 209867381}, + new int[]{170987846, 341967853, 170987846}, + new int[]{131531482, 263046905, 131531482}, + new int[]{89306658, 178584282, 89306658} + }; + + /* Interpolation points for filter coefficients used in the bandwidth transition smoother */ + static final int[][] silk_Transition_LP_A_Q28 + = { + new int[]{506393414, 239854379}, + new int[]{411067935, 169683996}, + new int[]{306733530, 116694253}, + new int[]{185807084, 77959395}, + new int[]{35497197, 57401098} + }; + + static final short[] silk_pitch_lag_iCDF = { + 253, 250, 244, 233, 212, 182, 150, 131, + 120, 110, 98, 85, 72, 60, 49, 40, + 32, 25, 19, 15, 13, 11, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0 + }; + + static final short[] silk_pitch_delta_iCDF = { + 210, 208, 206, 203, 199, 193, 183, 168, + 142, 104, 74, 52, 37, 27, 20, 14, + 10, 6, 4, 2, 0 + }; + + static final short[] silk_pitch_contour_iCDF = { + 223, 201, 183, 167, 152, 138, 124, 111, + 98, 88, 79, 70, 62, 56, 50, 44, + 39, 35, 31, 27, 24, 21, 18, 16, + 14, 12, 10, 8, 6, 4, 3, 2, + 1, 0 + }; + + static final short[] silk_pitch_contour_NB_iCDF = { + 188, 176, 155, 138, 119, 97, 67, 43, + 26, 10, 0 + }; + + static final short[] silk_pitch_contour_10_ms_iCDF = { + 165, 119, 80, 61, 47, 35, 27, 20, + 14, 9, 4, 0 + }; + + static final short[] silk_pitch_contour_10_ms_NB_iCDF = { + 113, 63, 0 + }; + + static final byte[] silk_max_pulses_table = { + 8, 10, 12, 16 + }; + + static final short[][] silk_pulses_per_block_iCDF = { + new short[]{ + 125, 51, 26, 18, 15, 12, 11, 10, + 9, 8, 7, 6, 5, 4, 3, 2, + 1, 0 + }, + new short[]{ + 198, 105, 45, 22, 15, 12, 11, 10, + 9, 8, 7, 6, 5, 4, 3, 2, + 1, 0 + }, + new short[]{ + 213, 162, 116, 83, 59, 43, 32, 24, + 18, 15, 12, 9, 7, 6, 5, 3, + 2, 0 + }, + new short[]{ + 239, 187, 116, 59, 28, 16, 11, 10, + 9, 8, 7, 6, 5, 4, 3, 2, + 1, 0 + }, + new short[]{ + 250, 229, 188, 135, 86, 51, 30, 19, + 13, 10, 8, 6, 5, 4, 3, 2, + 1, 0 + }, + new short[]{ + 249, 235, 213, 185, 156, 128, 103, 83, + 66, 53, 42, 33, 26, 21, 17, 13, + 10, 0 + }, + new short[]{ + 254, 249, 235, 206, 164, 118, 77, 46, + 27, 16, 10, 7, 5, 4, 3, 2, + 1, 0 + }, + new short[]{ + 255, 253, 249, 239, 220, 191, 156, 119, + 85, 57, 37, 23, 15, 10, 6, 4, + 2, 0 + }, + new short[]{ + 255, 253, 251, 246, 237, 223, 203, 179, + 152, 124, 98, 75, 55, 40, 29, 21, + 15, 0 + }, + new short[]{ + 255, 254, 253, 247, 220, 162, 106, 67, + 42, 28, 18, 12, 9, 6, 4, 3, + 2, 0 + } + }; + + static final short[][] silk_pulses_per_block_BITS_Q5 = { + new short[]{ + 31, 57, 107, 160, 205, 205, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255 + }, + new short[]{ + 69, 47, 67, 111, 166, 205, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255 + }, + new short[]{ + 82, 74, 79, 95, 109, 128, 145, 160, + 173, 205, 205, 205, 224, 255, 255, 224, + 255, 224 + }, + new short[]{ + 125, 74, 59, 69, 97, 141, 182, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255 + }, + new short[]{ + 173, 115, 85, 73, 76, 92, 115, 145, + 173, 205, 224, 224, 255, 255, 255, 255, + 255, 255 + }, + new short[]{ + 166, 134, 113, 102, 101, 102, 107, 118, + 125, 138, 145, 155, 166, 182, 192, 192, + 205, 150 + }, + new short[]{ + 224, 182, 134, 101, 83, 79, 85, 97, + 120, 145, 173, 205, 224, 255, 255, 255, + 255, 255 + }, + new short[]{ + 255, 224, 192, 150, 120, 101, 92, 89, + 93, 102, 118, 134, 160, 182, 192, 224, + 224, 224 + }, + new short[]{ + 255, 224, 224, 182, 155, 134, 118, 109, + 104, 102, 106, 111, 118, 131, 145, 160, + 173, 131 + } + }; + + static final short[][] silk_rate_levels_iCDF + = { + new short[]{ + 241, 190, 178, 132, 87, 74, 41, 14, + 0 + }, + new short[]{ + 223, 193, 157, 140, 106, 57, 39, 18, + 0 + } + }; + + static final short[][] silk_rate_levels_BITS_Q5 + = { + new short[]{ + 131, 74, 141, 79, 80, 138, 95, 104, + 134 + }, + new short[]{ + 95, 99, 91, 125, 93, 76, 123, 115, + 123 + } + }; + + static final short[] silk_shell_code_table0 = { + 128, 0, 214, 42, 0, 235, 128, 21, + 0, 244, 184, 72, 11, 0, 248, 214, + 128, 42, 7, 0, 248, 225, 170, 80, + 25, 5, 0, 251, 236, 198, 126, 54, + 18, 3, 0, 250, 238, 211, 159, 82, + 35, 15, 5, 0, 250, 231, 203, 168, + 128, 88, 53, 25, 6, 0, 252, 238, + 216, 185, 148, 108, 71, 40, 18, 4, + 0, 253, 243, 225, 199, 166, 128, 90, + 57, 31, 13, 3, 0, 254, 246, 233, + 212, 183, 147, 109, 73, 44, 23, 10, + 2, 0, 255, 250, 240, 223, 198, 166, + 128, 90, 58, 33, 16, 6, 1, 0, + 255, 251, 244, 231, 210, 181, 146, 110, + 75, 46, 25, 12, 5, 1, 0, 255, + 253, 248, 238, 221, 196, 164, 128, 92, + 60, 35, 18, 8, 3, 1, 0, 255, + 253, 249, 242, 229, 208, 180, 146, 110, + 76, 48, 27, 14, 7, 3, 1, 0 + }; + + static final short[] silk_shell_code_table1 = { + 129, 0, 207, 50, 0, 236, 129, 20, + 0, 245, 185, 72, 10, 0, 249, 213, + 129, 42, 6, 0, 250, 226, 169, 87, + 27, 4, 0, 251, 233, 194, 130, 62, + 20, 4, 0, 250, 236, 207, 160, 99, + 47, 17, 3, 0, 255, 240, 217, 182, + 131, 81, 41, 11, 1, 0, 255, 254, + 233, 201, 159, 107, 61, 20, 2, 1, + 0, 255, 249, 233, 206, 170, 128, 86, + 50, 23, 7, 1, 0, 255, 250, 238, + 217, 186, 148, 108, 70, 39, 18, 6, + 1, 0, 255, 252, 243, 226, 200, 166, + 128, 90, 56, 30, 13, 4, 1, 0, + 255, 252, 245, 231, 209, 180, 146, 110, + 76, 47, 25, 11, 4, 1, 0, 255, + 253, 248, 237, 219, 194, 163, 128, 93, + 62, 37, 19, 8, 3, 1, 0, 255, + 254, 250, 241, 226, 205, 177, 145, 111, + 79, 51, 30, 15, 6, 2, 1, 0 + }; + + static final short[] silk_shell_code_table2 = { + 129, 0, 203, 54, 0, 234, 129, 23, + 0, 245, 184, 73, 10, 0, 250, 215, + 129, 41, 5, 0, 252, 232, 173, 86, + 24, 3, 0, 253, 240, 200, 129, 56, + 15, 2, 0, 253, 244, 217, 164, 94, + 38, 10, 1, 0, 253, 245, 226, 189, + 132, 71, 27, 7, 1, 0, 253, 246, + 231, 203, 159, 105, 56, 23, 6, 1, + 0, 255, 248, 235, 213, 179, 133, 85, + 47, 19, 5, 1, 0, 255, 254, 243, + 221, 194, 159, 117, 70, 37, 12, 2, + 1, 0, 255, 254, 248, 234, 208, 171, + 128, 85, 48, 22, 8, 2, 1, 0, + 255, 254, 250, 240, 220, 189, 149, 107, + 67, 36, 16, 6, 2, 1, 0, 255, + 254, 251, 243, 227, 201, 166, 128, 90, + 55, 29, 13, 5, 2, 1, 0, 255, + 254, 252, 246, 234, 213, 183, 147, 109, + 73, 43, 22, 10, 4, 2, 1, 0 + }; + + static final short[] silk_shell_code_table3 = { + 130, 0, 200, 58, 0, 231, 130, 26, + 0, 244, 184, 76, 12, 0, 249, 214, + 130, 43, 6, 0, 252, 232, 173, 87, + 24, 3, 0, 253, 241, 203, 131, 56, + 14, 2, 0, 254, 246, 221, 167, 94, + 35, 8, 1, 0, 254, 249, 232, 193, + 130, 65, 23, 5, 1, 0, 255, 251, + 239, 211, 162, 99, 45, 15, 4, 1, + 0, 255, 251, 243, 223, 186, 131, 74, + 33, 11, 3, 1, 0, 255, 252, 245, + 230, 202, 158, 105, 57, 24, 8, 2, + 1, 0, 255, 253, 247, 235, 214, 179, + 132, 84, 44, 19, 7, 2, 1, 0, + 255, 254, 250, 240, 223, 196, 159, 112, + 69, 36, 15, 6, 2, 1, 0, 255, + 254, 253, 245, 231, 209, 176, 136, 93, + 55, 27, 11, 3, 2, 1, 0, 255, + 254, 253, 252, 239, 221, 194, 158, 117, + 76, 42, 18, 4, 3, 2, 1, 0 + }; + + static final short[] silk_shell_code_table_offsets = { + 0, 0, 2, 5, 9, 14, 20, 27, + 35, 44, 54, 65, 77, 90, 104, 119, + 135 + }; + + static final short[] silk_sign_iCDF = { + 254, 49, 67, 77, 82, 93, 99, + 198, 11, 18, 24, 31, 36, 45, + 255, 46, 66, 78, 87, 94, 104, + 208, 14, 21, 32, 42, 51, 66, + 255, 94, 104, 109, 112, 115, 118, + 248, 53, 69, 80, 88, 95, 102 + }; + + // Resampler tables + + /* Tables with delay compensation values to equalize total delay for different modes */ + static final byte[][] delay_matrix_enc = { + /* in \ out 8 12 16 */ + /* 8 */{6, 0, 3}, + /* 12 */ {0, 7, 3}, + /* 16 */ {0, 1, 10}, + /* 24 */ {0, 2, 6}, + /* 48 */ {18, 10, 12} + }; + + static final byte[][] delay_matrix_dec = { + /* in \ out 8 12 16 24 48 */ + /* 8 */{4, 0, 2, 0, 0}, + /* 12 */ {0, 9, 4, 7, 4}, + /* 16 */ {0, 3, 12, 7, 7} + }; + + + /* Tables with IIR and FIR coefficients for fractional downsamplers (123 Words) */ + static final /*silk_DWORD_ALIGN*/ short[] silk_Resampler_3_4_COEFS/*[2 + 3 * RESAMPLER_DOWN_ORDER_FIR0 / 2]*/ = { + -20694, -13867, + -49, 64, 17, -157, 353, -496, 163, 11047, 22205, + -39, 6, 91, -170, 186, 23, -896, 6336, 19928, + -19, -36, 102, -89, -24, 328, -951, 2568, 15909,}; + + static final /*silk_DWORD_ALIGN*/ short[] silk_Resampler_2_3_COEFS/*[2 + 2 * RESAMPLER_DOWN_ORDER_FIR0 / 2]*/ = { + -14457, -14019, + 64, 128, -122, 36, 310, -768, 584, 9267, 17733, + 12, 128, 18, -142, 288, -117, -865, 4123, 14459,}; + + static final /*silk_DWORD_ALIGN*/ short[] silk_Resampler_1_2_COEFS/*[2 + RESAMPLER_DOWN_ORDER_FIR1 / 2]*/ = { + 616, -14323, + -10, 39, 58, -46, -84, 120, 184, -315, -541, 1284, 5380, 9024,}; + + static final /*silk_DWORD_ALIGN*/ short[] silk_Resampler_1_3_COEFS/*[2 + RESAMPLER_DOWN_ORDER_FIR2 / 2]*/ = { + 16102, -15162, + -13, 0, 20, 26, 5, -31, -43, -4, 65, 90, 7, -157, -248, -44, 593, 1583, 2612, 3271,}; + + static final /*silk_DWORD_ALIGN*/ short[] silk_Resampler_1_4_COEFS/*[2 + RESAMPLER_DOWN_ORDER_FIR2 / 2]*/ = { + 22500, -15099, + 3, -14, -20, -15, 2, 25, 37, 25, -16, -71, -107, -79, 50, 292, 623, 982, 1288, 1464,}; + + static final /*silk_DWORD_ALIGN*/ short[] silk_Resampler_1_6_COEFS/*[2 + RESAMPLER_DOWN_ORDER_FIR2 / 2]*/ = { + 27540, -15257, + 17, 12, 8, 1, -10, -22, -30, -32, -22, 3, 44, 100, 168, 243, 317, 381, 429, 455,}; + + static final /*silk_DWORD_ALIGN*/ short[] silk_Resampler_2_3_COEFS_LQ/*[2 + 2 * 2]*/ = { + -2797, -6507, + 4697, 10739, + 1567, 8276,}; + + /* Table with interplation fractions of 1/24, 3/24, 5/24, ... , 23/24 : 23/24 (46 Words) */ + static final /*silk_DWORD_ALIGN*/ short[][] silk_resampler_frac_FIR_12/*[12][RESAMPLER_ORDER_FIR_12 / 2 ]*/ = { + {189, -600, 617, 30567}, + {117, -159, -1070, 29704}, + {52, 221, -2392, 28276}, + {-4, 529, -3350, 26341}, + {-48, 758, -3956, 23973}, + {-80, 905, -4235, 21254}, + {-99, 972, -4222, 18278}, + {-107, 967, -3957, 15143}, + {-103, 896, -3487, 11950}, + {-91, 773, -2865, 8798}, + {-71, 611, -2143, 5784}, + {-46, 425, -1375, 2996},}; + + /* Tables for 2x downsampler */ + public static final short silk_resampler_down2_0 = 9872; + public static final short silk_resampler_down2_1 = 39809 - 65536; + + /* Tables for 2x upsampler, high quality */ + static final short[] silk_resampler_up2_hq_0 = {1746, 14986, 39083 - 65536}; + static final short[] silk_resampler_up2_hq_1 = {6854, 25769, 55542 - 65536}; + + // from pitch_estimation_tables.c + static final byte[][] silk_CB_lags_stage2_10_ms + = { + new byte[]{0, 1, 0}, + new byte[]{0, 0, 1} + }; + + static final byte[][] silk_CB_lags_stage3_10_ms + = { + new byte[]{0, 0, 1, -1, 1, -1, 2, -2, 2, -2, 3, -3}, + new byte[]{0, 1, 0, 1, -1, 2, -1, 2, -2, 3, -2, 3} + }; + + static final byte[][] silk_CB_lags_stage2 + /*[ PE_MAX_NB_SUBFR ][ PE_NB_CBKS_STAGE2_EXT ]*/ = { + new byte[]{0, 2, -1, -1, -1, 0, 0, 1, 1, 0, 1}, + new byte[]{0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0}, + new byte[]{0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0}, + new byte[]{0, -1, 2, 1, 0, 1, 1, 0, 0, -1, -1} + }; + + static final byte[][] silk_CB_lags_stage3/*[ PE_MAX_NB_SUBFR ][ PE_NB_CBKS_STAGE3_MAX ]*/ = { + new byte[]{0, 0, 1, -1, 0, 1, -1, 0, -1, 1, -2, 2, -2, -2, 2, -3, 2, 3, -3, -4, 3, -4, 4, 4, -5, 5, -6, -5, 6, -7, 6, 5, 8, -9}, + new byte[]{0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -1, 1, 0, 0, 1, -1, 0, 1, -1, -1, 1, -1, 2, 1, -1, 2, -2, -2, 2, -2, 2, 2, 3, -3}, + new byte[]{0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, -1, 1, 0, 0, 2, 1, -1, 2, -1, -1, 2, -1, 2, 2, -1, 3, -2, -2, -2, 3}, + new byte[]{0, 1, 0, 0, 1, 0, 1, -1, 2, -1, 2, -1, 2, 3, -2, 3, -2, -2, 4, 4, -3, 5, -3, -4, 6, -4, 6, 5, -5, 8, -6, -5, -7, 9} + }; + + static final byte[][] silk_Lag_range_stage3_10_ms + = { + new byte[]{-3, 7}, + new byte[]{-2, 7} + }; + + static final byte[][][] silk_Lag_range_stage3 + = { + /* Lags to search for low number of stage3 cbks */ + new byte[][]{ + new byte[]{-5, 8}, + new byte[]{-1, 6}, + new byte[]{-1, 6}, + new byte[]{-4, 10} + }, + /* Lags to search for middle number of stage3 cbks */ + new byte[][]{ + new byte[]{-6, 10}, + new byte[]{-2, 6}, + new byte[]{-1, 6}, + new byte[]{-5, 10} + }, + /* Lags to search for max number of stage3 cbks */ + new byte[][]{ + new byte[]{-9, 12}, + new byte[]{-3, 7}, + new byte[]{-2, 7}, + new byte[]{-7, 13} + } + }; + + static final byte[] silk_nb_cbk_searchs_stage3 + = { + SilkConstants.PE_NB_CBKS_STAGE3_MIN, + SilkConstants.PE_NB_CBKS_STAGE3_MID, + SilkConstants.PE_NB_CBKS_STAGE3_MAX + }; +} diff --git a/app/src/main/java/org/concentus/SilkVADState.java b/app/src/main/java/org/concentus/SilkVADState.java new file mode 100644 index 0000000000..d7de8841d0 --- /dev/null +++ b/app/src/main/java/org/concentus/SilkVADState.java @@ -0,0 +1,101 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// VAD state +/// +class SilkVADState { + + /// + /// Analysis filterbank state: 0-8 kHz + /// + final int[] AnaState = new int[2]; + + /// + /// Analysis filterbank state: 0-4 kHz + /// + final int[] AnaState1 = new int[2]; + + /// + /// Analysis filterbank state: 0-2 kHz + /// + final int[] AnaState2 = new int[2]; + + /// + /// Subframe energies + /// + final int[] XnrgSubfr = new int[SilkConstants.VAD_N_BANDS]; + + /// + /// Smoothed energy level in each band + /// + final int[] NrgRatioSmth_Q8 = new int[SilkConstants.VAD_N_BANDS]; + + /// + /// State of differentiator in the lowest band + /// + short HPstate = 0; + + /// + /// Noise energy level in each band + /// + final int[] NL = new int[SilkConstants.VAD_N_BANDS]; + + /// + /// Inverse noise energy level in each band + /// + final int[] inv_NL = new int[SilkConstants.VAD_N_BANDS]; + + /// + /// Noise level estimator bias/offset + /// + final int[] NoiseLevelBias = new int[SilkConstants.VAD_N_BANDS]; + + /// + /// Frame counter used in the initial phase + /// + int counter = 0; + + void Reset() { + Arrays.MemSet(AnaState, 0, 2); + Arrays.MemSet(AnaState1, 0, 2); + Arrays.MemSet(AnaState2, 0, 2); + Arrays.MemSet(XnrgSubfr, 0, SilkConstants.VAD_N_BANDS); + Arrays.MemSet(NrgRatioSmth_Q8, 0, SilkConstants.VAD_N_BANDS); + HPstate = 0; + Arrays.MemSet(NL, 0, SilkConstants.VAD_N_BANDS); + Arrays.MemSet(inv_NL, 0, SilkConstants.VAD_N_BANDS); + Arrays.MemSet(NoiseLevelBias, 0, SilkConstants.VAD_N_BANDS); + counter = 0; + } +} diff --git a/app/src/main/java/org/concentus/Sort.java b/app/src/main/java/org/concentus/Sort.java new file mode 100644 index 0000000000..5b1201261a --- /dev/null +++ b/app/src/main/java/org/concentus/Sort.java @@ -0,0 +1,175 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class Sort { + + /// + /// + /// + /// (I/O) Unsorted / Sorted vector + /// (O) Index vector for the sorted elements + /// (I) Vector length + /// (I) Number of correctly sorted positions + static void silk_insertion_sort_increasing(int[] a, int[] idx, int L, int K) { + int value; + int i, j; + + // Safety checks + Inlines.OpusAssert(K > 0); + Inlines.OpusAssert(L > 0); + Inlines.OpusAssert(L >= K); + + // Write start indices in index vector + for (i = 0; i < K; i++) { + idx[i] = i; + } + + // Sort vector elements by value, increasing order + for (i = 1; i < K; i++) { + value = a[i]; + + for (j = i - 1; (j >= 0) && (value < a[j]); j--) { + a[j + 1] = a[j]; + /* Shift value */ + idx[j + 1] = idx[j]; + /* Shift index */ + } + + a[j + 1] = value; + /* Write value */ + idx[j + 1] = i; + /* Write index */ + } + + // If less than L values are asked for, check the remaining values, + // but only spend CPU to ensure that the K first values are correct + for (i = K; i < L; i++) { + value = a[i]; + + if (value < a[K - 1]) { + for (j = K - 2; (j >= 0) && (value < a[j]); j--) { + a[j + 1] = a[j]; + /* Shift value */ + idx[j + 1] = idx[j]; + /* Shift index */ + } + + a[j + 1] = value; + /* Write value */ + idx[j + 1] = i; + /* Write index */ + } + } + } + + /// + /// Insertion sort (fast for already almost sorted arrays): + /// Best case: O(n) for an already sorted array + /// Worst case: O(n^2) for an inversely sorted array + /// + /// (I/O) Unsorted / Sorted vector + /// (I) Vector length + static void silk_insertion_sort_increasing_all_values_int16(short[] a, int L) { + // FIXME: Could just use Array.Sort(a.Array, a.Offset, L); + + short value; + int i, j; + + // Safety checks + Inlines.OpusAssert(L > 0); + + // Sort vector elements by value, increasing order + for (i = 1; i < L; i++) { + value = a[i]; + for (j = i - 1; (j >= 0) && (value < a[j]); j--) { + a[j + 1] = a[j]; // Shift value + } + + a[j + 1] = value; // Write value + } + } + + /* This function is only used by the fixed-point build */ + static void silk_insertion_sort_decreasing_int16( + short[] a, /* I/O Unsorted / Sorted vector */ + int[] idx, /* O Index vector for the sorted elements */ + int L, /* I Vector length */ + int K /* I Number of correctly sorted positions */ + ) { + int i, j; + short value; + + /* Safety checks */ + Inlines.OpusAssert(K > 0); + Inlines.OpusAssert(L > 0); + Inlines.OpusAssert(L >= K); + + /* Write start indices in index vector */ + for (i = 0; i < K; i++) { + idx[i] = i; + } + + /* Sort vector elements by value, decreasing order */ + for (i = 1; i < K; i++) { + value = a[i]; + for (j = i - 1; (j >= 0) && (value > a[j]); j--) { + a[j + 1] = a[j]; + /* Shift value */ + idx[j + 1] = idx[j]; + /* Shift index */ + } + a[j + 1] = value; + /* Write value */ + idx[j + 1] = i; + /* Write index */ + } + + /* If less than L values are asked for, check the remaining values, */ + /* but only spend CPU to ensure that the K first values are correct */ + for (i = K; i < L; i++) { + value = a[i]; + if (value > a[K - 1]) { + for (j = K - 2; (j >= 0) && (value > a[j]); j--) { + a[j + 1] = a[j]; + /* Shift value */ + idx[j + 1] = idx[j]; + /* Shift index */ + } + a[j + 1] = value; + /* Write value */ + idx[j + 1] = i; + /* Write index */ + } + } + } +} diff --git a/app/src/main/java/org/concentus/SpeexResampler.java b/app/src/main/java/org/concentus/SpeexResampler.java new file mode 100644 index 0000000000..0bc1dc766c --- /dev/null +++ b/app/src/main/java/org/concentus/SpeexResampler.java @@ -0,0 +1,911 @@ +/* Copyright (C) 2007-2008 Jean-Marc Valin + Copyright (C) 2008 Thorvald Natvig + Ported to Java by Logan Stromberg + + File: Resampler.cs + Arbitrary resampling code + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + The design goals of this code are: + - Very fast algorithm + - SIMD-friendly algorithm + - Low memory requirement + - Good *perceptual* quality (and not best SNR) + + Warning: This resampler is relatively new. Although I think I got rid of + all the major bugs and I don't expect the API to change anymore, there + may be something I've missed. So use with caution. + + This algorithm is based on this original resampling algorithm: + Smith, Julius O. Digital Audio Resampling Home Page + Center for Computer Research in Music and Acoustics (CCRMA), + Stanford University, 2007. + Web published at http://www-ccrma.stanford.edu/~jos/resample/. + + There is one main difference, though. This resampler uses cubic + interpolation instead of linear interpolation in the above paper. This + makes the table much smaller and makes it possible to compute that table + on a per-stream basis. In turn, being able to tweak the table for each + stream makes it possible to both reduce complexity on simple ratios + (e.g. 2/3), and get rid of the rounding operations in the inner loop. + The latter both reduces CPU time and makes the algorithm more SIMD-friendly. +*/ + +package org.concentus; + +//public class SpeexResampler +//{ +// private final int FIXED_STACK_ALLOC = 8192; +// +// private int in_rate = 0; +// private int out_rate = 0; +// private int num_rate = 0; +// private int den_rate = 0; +// +// private int quality = 0; +// private int nb_channels = 0; +// private int filt_len = 0; +// private int mem_alloc_size = 0; +// private int buffer_size = 0; +// private int int_advance = 0; +// private int frac_advance = 0; +// private float cutoff = 0; +// private int oversample = 0; +// private int initialised = 0; +// private int started = 0; +// +// /* These are per-channel */ +// private int[] last_sample = null; +// private int[] samp_frac_num = null; +// private int[] magic_samples = null; +// +// private short[] mem = null; +// private short[] sinc_table = null; +// private int sinc_table_length = 0; +// +// int in_stride = 0; +// int out_stride = 0; +// +// /// +// /// Create() is the only way to make a resampler instance publically +// /// +// private SpeexResampler() { } +// +// private class FuncDef +// { +// public FuncDef(double[] t, int os) +// { +// table = t; +// oversample = os; +// } +// +// public double[] table; +// public int oversample; +// }; +// +// private class QualityMapping +// { +// public int base_length = 0; +// public int oversample = 0; +// public float downsample_bandwidth = 0; +// public float upsample_bandwidth = 0; +// public FuncDef window_func = null; +// +// private QualityMapping(int bl, int os, float dsb, float usb, FuncDef wf) +// { +// base_length = bl; +// oversample = os; +// downsample_bandwidth = dsb; +// upsample_bandwidth = usb; +// window_func = wf; +// } +// +// public final double[] kaiser12_table/*[68]*/ = { +// 0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076, +// 0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014, +// 0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601, +// 0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014, +// 0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490, +// 0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546, +// 0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178, +// 0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947, +// 0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058, +// 0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438, +// 0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734, +// 0.00001000, 0.00000000}; +// /* +// static double kaiser12_table[36] = { +// 0.99440475, 1.00000000, 0.99440475, 0.97779076, 0.95066529, 0.91384741, +// 0.86843014, 0.81573067, 0.75723148, 0.69451601, 0.62920216, 0.56287762, +// 0.49704014, 0.43304576, 0.37206735, 0.31506490, 0.26276832, 0.21567274, +// 0.17404546, 0.13794294, 0.10723616, 0.08164178, 0.06075685, 0.04409466, +// 0.03111947, 0.02127838, 0.01402878, 0.00886058, 0.00531256, 0.00298291, +// 0.00153438, 0.00069463, 0.00025272, 0.0000527734, 0.00000500, 0.00000000}; +// */ +// public final double[] kaiser10_table/*[36]*/ = { +// 0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446, +// 0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347, +// 0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962, +// 0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451, +// 0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739, +// 0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000}; +// +// public final double[] kaiser8_table/*[36]*/ = { +// 0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200, +// 0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126, +// 0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272, +// 0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758, +// 0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490, +// 0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000}; +// +// public final double[] kaiser6_table/*[36]*/ = { +// 0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003, +// 0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565, +// 0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561, +// 0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058, +// 0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600, +// 0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000}; +// +// /* This table maps conversion quality to private parameters. There are two +// reasons that explain why the up-sampling bandwidth is larger than the +// down-sampling bandwidth: +// 1) When up-sampling, we can assume that the spectrum is already attenuated +// close to the Nyquist rate (from an A/D or a previous resampling filter) +// 2) Any aliasing that occurs very close to the Nyquist rate will be masked +// by the sinusoids/noise just below the Nyquist rate (guaranteed only for +// up-sampling). +// */ +// public final QualityMapping[] quality_map = { +// new QualityMapping( 8, 4, 0.830f, 0.860f, new FuncDef(kaiser6_table, 32) ), /* Q0 */ +// new QualityMapping( 16, 4, 0.850f, 0.880f, new FuncDef(kaiser6_table, 32) ), /* Q1 */ +// new QualityMapping( 32, 4, 0.882f, 0.910f, new FuncDef(kaiser6_table, 32) ), /* Q2 */ /* 82.3% cutoff ( ~60 dB stop) 6 */ +// new QualityMapping( 48, 8, 0.895f, 0.917f, new FuncDef(kaiser8_table, 32) ), /* Q3 */ /* 84.9% cutoff ( ~80 dB stop) 8 */ +// new QualityMapping( 64, 8, 0.921f, 0.940f, new FuncDef(kaiser8_table, 32) ), /* Q4 */ /* 88.7% cutoff ( ~80 dB stop) 8 */ +// new QualityMapping( 80, 16, 0.922f, 0.940f, new FuncDef(kaiser10_table, 32)), /* Q5 */ /* 89.1% cutoff (~100 dB stop) 10 */ +// new QualityMapping( 96, 16, 0.940f, 0.945f, new FuncDef(kaiser10_table, 32)), /* Q6 */ /* 91.5% cutoff (~100 dB stop) 10 */ +// new QualityMapping(128, 16, 0.950f, 0.950f, new FuncDef(kaiser10_table, 32)), /* Q7 */ /* 93.1% cutoff (~100 dB stop) 10 */ +// new QualityMapping(160, 16, 0.960f, 0.960f, new FuncDef(kaiser10_table, 32)), /* Q8 */ /* 94.5% cutoff (~100 dB stop) 10 */ +// new QualityMapping(192, 32, 0.968f, 0.968f, new FuncDef(kaiser12_table, 64)), /* Q9 */ /* 95.5% cutoff (~100 dB stop) 10 */ +// new QualityMapping(256, 32, 0.975f, 0.975f, new FuncDef(kaiser12_table, 64)), /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */ +// }; +// } +// +// private static short WORD2INT(float x) +// { +// return x < Short.MIN_VALUE ? Short.MIN_VALUE : (x > Short.MAX_VALUE ? Short.MAX_VALUE : (short)x); +// } +// +// /*8,24,40,56,80,104,128,160,200,256,320*/ +// private static double compute_func(float x, FuncDef func) +// { +// float y, frac; +// double interp0, interp1, interp2, interp3; +// int ind; +// y = x * func.oversample; +// ind = (int)Math.floor(y); +// frac = (y - ind); +// /* CSE with handle the repeated powers */ +// interp3 = -0.1666666667 * frac + 0.1666666667 * (frac * frac * frac); +// interp2 = frac + 0.5 * (frac * frac) - 0.5 * (frac * frac * frac); +// /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/ +// interp0 = -0.3333333333 * frac + 0.5 * (frac * frac) - 0.1666666667 * (frac * frac * frac); +// /* Just to make sure we don't have rounding problems */ +// interp1 = 1.0f - interp3 - interp2 - interp0; +// +// /*sum = frac*accum[1] + (1-frac)*accum[2];*/ +// return interp0 * func.table[ind] + interp1 * func.table[ind + 1] + interp2 * func.table[ind + 2] + interp3 * func.table[ind + 3]; +// } +// +// /* The slow way of computing a sinc for the table. Should improve that some day */ +// private static short sinc(float cutoff, float x, int N, FuncDef window_func) +// { +// /*fprintf (stderr, "%f ", x);*/ +// float xx = x * cutoff; +// if (Math.abs(x) < 1e-6f) +// return WORD2INT(32768.0f * cutoff); +// else if (Math.abs(x) > .5f * N) +// return 0; +// /*FIXME: Can it really be any slower than this? */ +// return WORD2INT(32768.0f * cutoff * (float)Math.sin((float)Math.PI * xx) / ((float)Math.PI * xx) * (float)compute_func(Math.abs(2.0f * x / N), window_func)); +// } +// +// private static void cubic_coef(short x, BoxedValueShort interp0, BoxedValueShort interp1, BoxedValueShort interp2, BoxedValueShort interp3) +// { +// /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation +// but I know it's MMSE-optimal on a sinc */ +// short x2, x3; +// x2 = Inlines.MULT16_16_P15(x, x); +// x3 = Inlines.MULT16_16_P15(x, x2); +// interp0.Val = (short)Inlines.PSHR32(Inlines.MULT16_16(Inlines.QCONST16(-0.16667f, 15), x) + Inlines.MULT16_16(Inlines.QCONST16(0.16667f, 15), x3), 15); +// interp1.Val = Inlines.EXTRACT16(Inlines.EXTEND32(x) + Inlines.SHR32(Inlines.SUB32(Inlines.EXTEND32(x2), Inlines.EXTEND32(x3)), 1)); +// interp3.Val = (short)Inlines.PSHR32(Inlines.MULT16_16(Inlines.QCONST16(-0.33333f, 15), x) + Inlines.MULT16_16(Inlines.QCONST16(.5f, 15), x2) - Inlines.MULT16_16(Inlines.QCONST16(0.16667f, 15), x3), 15); +// /* Just to make sure we don't have rounding problems */ +// interp2.Val = (short)(CeltConstants.Q15_ONE - interp0.Val - interp1.Val - interp3.Val); +// if (interp2.Val < 32767) +// interp2.Val += 1; +// } +// +// private int resampler_basic_direct_single(int channel_index, short[] input, int input_ptr, BoxedValueInt in_len, short[] output, int output_ptr, BoxedValueInt out_len) +// { +// int N = this.filt_len; +// int out_sample = 0; +// int last_sample = this.last_sample[channel_index]; +// int samp_frac_num = this.samp_frac_num[channel_index]; +// +// int sum; +// +// while (!(last_sample >= in_len.Val || out_sample >= out_len.Val)) +// { +// int sinct = (int)samp_frac_num * N; +// int iptr = input_ptr + last_sample; +// +// int j; +// sum = 0; +// for (j = 0; j < N; j++) +// { +// sum += Inlines.MULT16_16(this.sinc_table[sinct + j], input[iptr + j]); +// } +// +// output[output_ptr + (this.out_stride * out_sample++)] = Inlines.SATURATE16(Inlines.PSHR32(sum, 15)); +// last_sample += this.int_advance; +// samp_frac_num += (int)this.frac_advance; +// if (samp_frac_num >= this.den_rate) +// { +// samp_frac_num -= this.den_rate; +// last_sample++; +// } +// } +// +// this.last_sample[channel_index] = last_sample; +// this.samp_frac_num[channel_index] = samp_frac_num; +// return out_sample; +// } +// +// private int resampler_basic_interpolate_single(int channel_index, short[] input, int input_ptr, BoxedValueInt in_len, short[] output, int output_ptr, BoxedValueInt out_len) +// { +// int N = this.filt_len; +// int out_sample = 0; +// int last_sample = this.last_sample[channel_index]; +// int samp_frac_num = this.samp_frac_num[channel_index]; +// int sum; +// BoxedValueShort interp0 = new BoxedValueShort((short)0); +// BoxedValueShort interp1 = new BoxedValueShort((short)0); +// BoxedValueShort interp2 = new BoxedValueShort((short)0); +// BoxedValueShort interp3 = new BoxedValueShort((short)0); +// while (!(last_sample >= in_len.Val || out_sample >= out_len.Val)) +// { +// int iptr = input_ptr + last_sample; +// +// int offset = samp_frac_num * this.oversample / this.den_rate; +// short frac = (short)Inlines.PDIV32(Inlines.SHL32((samp_frac_num * this.oversample) % this.den_rate, 15), this.den_rate); +// +// int j; +// int accum0 = 0; +// int accum1 = 0; +// int accum2 = 0; +// int accum3 = 0; +// +// for (j = 0; j < N; j++) +// { +// short curr_in = input[iptr + j]; +// accum0 += Inlines.MULT16_16(curr_in, this.sinc_table[4 + (j + 1) * (int)this.oversample - offset - 2]); +// accum1 += Inlines.MULT16_16(curr_in, this.sinc_table[4 + (j + 1) * (int)this.oversample - offset - 1]); +// accum2 += Inlines.MULT16_16(curr_in, this.sinc_table[4 + (j + 1) * (int)this.oversample - offset]); +// accum3 += Inlines.MULT16_16(curr_in, this.sinc_table[4 + (j + 1) * (int)this.oversample - offset + 1]); +// } +// +// cubic_coef(frac, interp0, interp1, interp2, interp3); +// sum = Inlines.MULT16_32_Q15(interp0.Val, Inlines.SHR32(accum0, 1)) + +// Inlines.MULT16_32_Q15(interp1.Val, Inlines.SHR32(accum1, 1)) + +// Inlines.MULT16_32_Q15(interp2.Val, Inlines.SHR32(accum2, 1)) + +// Inlines.MULT16_32_Q15(interp3.Val, Inlines.SHR32(accum3, 1)); +// +// output[output_ptr + (out_stride * out_sample++)] = Inlines.SATURATE16(Inlines.PSHR32(sum, 14)); +// last_sample += int_advance; +// samp_frac_num += (int)frac_advance; +// if (samp_frac_num >= den_rate) +// { +// samp_frac_num -= den_rate; +// last_sample++; +// } +// } +// +// this.last_sample[channel_index] = last_sample; +// this.samp_frac_num[channel_index] = samp_frac_num; +// return out_sample; +// } +// +// private void update_filter() +// { +// int old_length; +// +// old_length = this.filt_len; +// this.oversample = QualityMapping.quality_map[this.quality].oversample; +// this.filt_len = QualityMapping.quality_map[this.quality].base_length; +// +// if (this.num_rate > this.den_rate) +// { +// /* down-sampling */ +// this.cutoff = QualityMapping.quality_map[this.quality].downsample_bandwidth * this.den_rate / this.num_rate; +// /* FIXME: divide the numerator and denominator by a certain amount if they're too large */ +// this.filt_len = this.filt_len * this.num_rate / this.den_rate; +// /* Round up to make sure we have a multiple of 8 */ +// this.filt_len = ((this.filt_len - 1) & (~0x7)) + 8; +// if (2 * this.den_rate < this.num_rate) +// this.oversample >>= 1; +// if (4 * this.den_rate < this.num_rate) +// this.oversample >>= 1; +// if (8 * this.den_rate < this.num_rate) +// this.oversample >>= 1; +// if (16 * this.den_rate < this.num_rate) +// this.oversample >>= 1; +// if (this.oversample < 1) +// this.oversample = 1; +// } +// else { +// /* up-sampling */ +// this.cutoff = QualityMapping.quality_map[this.quality].upsample_bandwidth; +// } +// +// if (this.den_rate <= 16 * (this.oversample + 8)) +// { +// int i; +// if (this.sinc_table == null) +// this.sinc_table = new short[this.filt_len * this.den_rate]; +// else if (this.sinc_table_length < this.filt_len * this.den_rate) +// { +// this.sinc_table = new short[this.filt_len * this.den_rate]; +// this.sinc_table_length = this.filt_len * this.den_rate; +// } +// for (i = 0; i < this.den_rate; i++) +// { +// int j; +// for (j = 0; j < this.filt_len; j++) +// { +// this.sinc_table[i * this.filt_len + j] = sinc(this.cutoff, ((j - (int)this.filt_len / 2 + 1) - ((float)i) / this.den_rate), this.filt_len, QualityMapping.quality_map[this.quality].window_func); +// } +// } +// this.resampler_ptr = resampler_basic_direct_single; +// /*fprintf (stderr, "resampler uses direct sinc table and normalised cutoff %f\n", cutoff);*/ +// } +// else { +// int i; +// if (this.sinc_table == null) +// this.sinc_table = new short[this.filt_len * this.oversample + 8]; +// else if (this.sinc_table_length < this.filt_len * this.oversample + 8) +// { +// this.sinc_table = new short[this.filt_len * this.oversample + 8]; +// this.sinc_table_length = this.filt_len * this.oversample + 8; +// } +// for (i = -4; i < (int)(this.oversample * this.filt_len + 4); i++) +// this.sinc_table[i + 4] = sinc(this.cutoff, (i / (float)this.oversample - this.filt_len / 2), this.filt_len, QualityMapping.quality_map[this.quality].window_func); +// this.resampler_ptr = resampler_basic_interpolate_single; +// /*fprintf (stderr, "resampler uses interpolated sinc table and normalised cutoff %f\n", cutoff);*/ +// } +// this.int_advance = this.num_rate / this.den_rate; +// this.frac_advance = this.num_rate % this.den_rate; +// +// +// /* Here's the place where we update the filter memory to take into account +// the change in filter length. It's probably the messiest part of the code +// due to handling of lots of corner cases. */ +// if (this.mem == null) +// { +// int i; +// this.mem_alloc_size = this.filt_len - 1 + this.buffer_size; +// this.mem = new short[this.nb_channels * this.mem_alloc_size]; +// for (i = 0; i < this.nb_channels * this.mem_alloc_size; i++) +// this.mem[i] = 0; +// /*speex_warning("init filter");*/ +// } +// else if (this.started == 0) +// { +// int i; +// this.mem_alloc_size = this.filt_len - 1 + this.buffer_size; +// this.mem = new short[this.nb_channels * this.mem_alloc_size]; +// for (i = 0; i < this.nb_channels * this.mem_alloc_size; i++) +// this.mem[i] = 0; +// /*speex_warning("reinit filter");*/ +// } +// else if (this.filt_len > old_length) +// { +// int i; +// /* Increase the filter length */ +// /*speex_warning("increase filter size");*/ +// int old_alloc_size = this.mem_alloc_size; +// if ((this.filt_len - 1 + this.buffer_size) > this.mem_alloc_size) +// { +// this.mem_alloc_size = this.filt_len - 1 + this.buffer_size; +// this.mem = new short[this.nb_channels * this.mem_alloc_size]; +// } +// for (i = this.nb_channels - 1; i >= 0; i--) +// { +// int j; +// int olen = old_length; +// /*if (st.magic_samples[i])*/ +// { +// /* Try and remove the magic samples as if nothing had happened */ +// +// /* FIXME: This is wrong but for now we need it to avoid going over the array bounds */ +// olen = old_length + 2 * this.magic_samples[i]; +// for (j = old_length - 2 + this.magic_samples[i]; j >= 0; j--) +// this.mem[i * this.mem_alloc_size + j + this.magic_samples[i]] = this.mem[i * old_alloc_size + j]; +// for (j = 0; j < this.magic_samples[i]; j++) +// this.mem[i * this.mem_alloc_size + j] = 0; +// this.magic_samples[i] = 0; +// } +// if (this.filt_len > olen) +// { +// /* If the new filter length is still bigger than the "augmented" length */ +// /* Copy data going backward */ +// for (j = 0; j < olen - 1; j++) +// this.mem[i * this.mem_alloc_size + (this.filt_len - 2 - j)] = this.mem[i * this.mem_alloc_size + (olen - 2 - j)]; +// /* Then put zeros for lack of anything better */ +// for (; j < this.filt_len - 1; j++) +// this.mem[i * this.mem_alloc_size + (this.filt_len - 2 - j)] = 0; +// /* Adjust last_sample */ +// this.last_sample[i] += (this.filt_len - olen) / 2; +// } +// else { +// /* Put back some of the magic! */ +// this.magic_samples[i] = (olen - this.filt_len) / 2; +// for (j = 0; j < this.filt_len - 1 + this.magic_samples[i]; j++) +// this.mem[i * this.mem_alloc_size + j] = this.mem[i * this.mem_alloc_size + j + this.magic_samples[i]]; +// } +// } +// } +// else if (this.filt_len < old_length) +// { +// int i; +// /* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic" +// samples so they can be used directly as input the next time(s) */ +// for (i = 0; i < this.nb_channels; i++) +// { +// int j; +// int old_magic = this.magic_samples[i]; +// this.magic_samples[i] = (old_length - this.filt_len) / 2; +// /* We must copy some of the memory that's no longer used */ +// /* Copy data going backward */ +// for (j = 0; j < this.filt_len - 1 + this.magic_samples[i] + old_magic; j++) +// this.mem[i * this.mem_alloc_size + j] = this.mem[i * this.mem_alloc_size + j + this.magic_samples[i]]; +// this.magic_samples[i] += old_magic; +// } +// } +// } +// +// private void speex_resampler_process_native(int channel_index, BoxedValueInt in_len, short[] output, int output_ptr, BoxedValueInt out_len) +// { +// int j = 0; +// int N = this.filt_len; +// int out_sample = 0; +// int mem_ptr = channel_index * this.mem_alloc_size; +// int ilen; +// +// this.started = 1; +// +// /* Call the right resampler through the function ptr */ +// out_sample = this.resampler_ptr(channel_index, this.mem, mem_ptr, ref in_len, output, output_ptr, ref out_len); +// +// if (this.last_sample[channel_index] < (int)in_len.Val) +// in_len.Val = this.last_sample[channel_index]; +// out_len = out_sample; +// this.last_sample[channel_index] -= in_len; +// +// ilen = in_len.Val; +// +// for (j = mem_ptr; j < N - 1 + mem_ptr; ++j) +// this.mem[j] = this.mem[j + ilen]; +// } +// +// private int speex_resampler_magic(int channel_index, short[] output, BoxedValueInt output_ptr, int out_len) +// { +// int tmp_in_len = this.magic_samples[channel_index]; +// int mem_ptr = channel_index * this.mem_alloc_size; +// int N = this.filt_len; +// +// this.speex_resampler_process_native(channel_index, ref tmp_in_len, output, output_ptr, ref out_len); +// +// this.magic_samples[channel_index] -= tmp_in_len; +// +// /* If we couldn't process all "magic" input samples, save the rest for next time */ +// if (this.magic_samples[channel_index] != 0) +// { +// int i; +// for (i = mem_ptr; i < this.magic_samples[channel_index] + mem_ptr; i++) +// this.mem[N - 1 + i] = this.mem[N - 1 + i + tmp_in_len]; +// } +// output_ptr.Val += out_len * this.out_stride; +// return out_len; +// } +// +// /// +// /// Create a new resampler with integer input and output rates (in hertz). +// /// +// /// The number of channels to be processed +// /// Input sampling rate, in hertz +// /// Output sampling rate, in hertz +// /// Resampling quality, from 0 to 10 +// /// A newly created restampler +// public static SpeexResampler Create(int nb_channels, int in_rate, int out_rate, int quality) +// { +// return Create(nb_channels, in_rate, out_rate, in_rate, out_rate, quality); +// } +// +// /// +// /// Create a new resampler with fractional input/output rates. The sampling +// /// rate ratio is an arbitrary rational number with both the numerator and +// /// denominator being 32-bit integers. +// /// +// /// The number of channels to be processed +// /// Numerator of sampling rate ratio +// /// Denominator of sampling rate ratio +// /// Input sample rate rounded to the nearest integer (in hz) +// /// Output sample rate rounded to the nearest integer (in hz) +// /// Resampling quality, from 0 to 10 +// /// A newly created restampler +// public static SpeexResampler Create(int nb_channels, int ratio_num, int ratio_den, int in_rate, int out_rate, int quality) +// { +// int i; +// SpeexResampler st; +// if (quality > 10 || quality < 0) +// { +// throw new IllegalArgumentException("Quality must be between 0 and 10"); +// } +// st = new SpeexResampler(); +// st.initialised = 0; +// st.started = 0; +// st.in_rate = 0; +// st.out_rate = 0; +// st.num_rate = 0; +// st.den_rate = 0; +// st.quality = -1; +// st.sinc_table_length = 0; +// st.mem_alloc_size = 0; +// st.filt_len = 0; +// st.mem = null; +// st.cutoff = 1.0f; +// st.nb_channels = nb_channels; +// st.in_stride = 1; +// st.out_stride = 1; +// st.buffer_size = 160; +// +// /* Per channel data */ +// st.last_sample = new int[nb_channels]; +// st.magic_samples = new int[nb_channels]; +// st.samp_frac_num = new int[nb_channels]; +// for (i = 0; i < nb_channels; i++) +// { +// st.last_sample[i] = 0; +// st.magic_samples[i] = 0; +// st.samp_frac_num[i] = 0; +// } +// +// st.Quality = quality; +// st.SetRateFraction(ratio_num, ratio_den, in_rate, out_rate); +// +// st.update_filter(); +// +// st.initialised = 1; +// +// return st; +// } +// +// public class ResamplerResult +// { +// ResamplerResult(int in_len, int out_len) +// { +// inputSamplesProcessed = in_len; +// outputSamplesGenerated = out_len; +// } +// +// int inputSamplesProcessed; +// int outputSamplesGenerated; +// } +// +// /// +// /// Resample an int array. The input and output buffers must *not* overlap +// /// +// /// The index of the channel to process (for multichannel input, 0 otherwise) +// /// Input buffer +// /// Offset to start from when reading input +// /// Number of input samples in the input buffer. After this function returns, this value +// /// will be set to the number of input samples actually processed +// /// Output buffer +// /// Offset to start from when writing output +// /// Size of the output buffer. After this function returns, this value will be set to the number +// /// of output samples actually produced +// public ResamplerResult Process(int channel_index, short[] input, int input_ptr, int in_len, short[] output, int output_ptr, int out_len) +// { +// int j; +// int ilen = in_len; +// int olen = out_len; +// int x = channel_index * this.mem_alloc_size; +// int filt_offs = this.filt_len - 1; +// int xlen = this.mem_alloc_size - filt_offs; +// int istride = this.in_stride; +// +// if (this.magic_samples[channel_index] != 0) +// { +// olen -= this.speex_resampler_magic(channel_index, output, ref output_ptr, olen); +// } +// if (this.magic_samples[channel_index] == 0) +// { +// while (ilen != 0 && olen != 0) +// { +// int ichunk = (ilen > xlen) ? xlen : ilen; +// int ochunk = olen; +// +// if (input != null) +// { +// for (j = 0; j < ichunk; ++j) +// this.mem[x + j + filt_offs] = input[input_ptr + j * istride]; +// } +// else { +// for (j = 0; j < ichunk; ++j) +// +// this.mem[x + j + filt_offs] = 0; +// } +// this.speex_resampler_process_native(channel_index, ref ichunk, output, output_ptr, ref ochunk); +// ilen -= ichunk; +// olen -= ochunk; +// output_ptr += ochunk * this.out_stride; +// if (input != null) +// +// input_ptr += ichunk * istride; +// } +// } +// return new ResamplerResult(in_len - ilen, out_len - olen); +// } +// +// /// +// /// Resamples an interleaved float array. The stride is automatically determined by the number of channels of the resampler. +// /// +// /// Input buffer +// /// Offset to start from when reading input +// /// The number of samples *PER-CHANNEL* in the input buffer. After this function returns, this +// /// value will be set to the number of input samples actually processed +// /// Output buffer +// /// Offset to start from when writing output +// /// The size of the output buffer in samples-per-channel. After this function returns, this value +// /// will be set to the number of samples per channel actually produced +// public ResamplerResult ProcessInterleaved(short[] input, int input_ptr, int in_len, short[] output, int output_ptr, int out_len) +// { +// int i; +// int istride_save, ostride_save; +// int bak_out_len = out_len; +// int bak_in_len = in_len; +// istride_save = this.in_stride; +// ostride_save = this.out_stride; +// this.in_stride = this.out_stride = this.nb_channels; +// for (i = 0; i < this.nb_channels; i++) +// { +// out_len = bak_out_len; +// in_len = bak_in_len; +// ResamplerResult r; +// if (input != null) +// { +// r = this.Process(i, input, input_ptr + i, in_len, output, output_ptr + i, out_len); +// } +// else +// { +// r = this.Process(i, null, 0, in_len, output, output_ptr + i, out_len); +// } +// +// in_len = r.inputSamplesProcessed; +// out_len = r.outputSamplesGenerated; +// } +// this.in_stride = istride_save; +// this.out_stride = ostride_save; +// +// return new ResamplerResult(in_len, out_len); +// } +// +// /// +// /// Make sure that the first samples to go out of the resamplers don't have +// /// leading zeros. This is only useful before starting to use a newly created +// /// resampler. It is recommended to use that when resampling an audio file, as +// /// it will generate a file with the same length.For real-time processing, +// /// it is probably easier not to use this call (so that the output duration +// /// is the same for the first frame). +// /// +// public void SkipZeroes() +// { +// int i; +// for (i = 0; i < this.nb_channels; i++) +// this.last_sample[i] = this.filt_len / 2; +// } +// +// /// +// /// Clears the resampler buffers so a new (unrelated) stream can be processed. +// /// +// public void ResetMem() +// { +// int i; +// for (i = 0; i < this.nb_channels; i++) +// { +// this.last_sample[i] = 0; +// this.magic_samples[i] = 0; +// this.samp_frac_num[i] = 0; +// } +// for (i = 0; i < this.nb_channels * (this.filt_len - 1); i++) +// this.mem[i] = 0; +// } +// +// /// +// /// Sets the input and output rates +// /// +// /// Input sampling rate, in hertz +// /// Output sampling rate, in hertz +// public void SetRates(int in_rate, int out_rate) +// { +// this.SetRateFraction(in_rate, out_rate, in_rate, out_rate); +// } +// +// /// +// /// Get the current input/output sampling rates (integer value). +// /// +// /// (Output) Sampling rate of input +// /// (Output) Sampling rate of output +// public void GetRates(out int in_rate, out int out_rate) +// { +// in_rate = this.in_rate; +// out_rate = this.out_rate; +// } +// +// /// +// /// Sets the input/output sampling rates and resampling ration (fractional values in Hz supported) +// /// +// /// Numerator of the sampling rate ratio +// /// Denominator of the sampling rate ratio +// /// Input sampling rate rounded to the nearest integer (in Hz) +// /// Output sampling rate rounded to the nearest integer (in Hz) +// public void SetRateFraction(int ratio_num, int ratio_den, int in_rate, int out_rate) +// { +// int fact; +// int old_den; +// int i; +// if (this.in_rate == in_rate && this.out_rate == out_rate && this.num_rate == ratio_num && this.den_rate == ratio_den) +// return; +// +// old_den = this.den_rate; +// this.in_rate = in_rate; +// this.out_rate = out_rate; +// this.num_rate = ratio_num; +// this.den_rate = ratio_den; +// /* FIXME: This is terribly inefficient, but who cares (at least for now)? */ +// for (fact = 2; fact <= Inlines.IMIN(this.num_rate, this.den_rate); fact++) +// { +// while ((this.num_rate % fact == 0) && (this.den_rate % fact == 0)) +// { +// this.num_rate /= fact; +// this.den_rate /= fact; +// } +// } +// +// if (old_den > 0) +// { +// for (i = 0; i < this.nb_channels; i++) +// { +// this.samp_frac_num[i] = this.samp_frac_num[i] * this.den_rate / old_den; +// /* Safety net */ +// if (this.samp_frac_num[i] >= this.den_rate) +// this.samp_frac_num[i] = this.den_rate - 1; +// } +// } +// +// if (this.initialised != 0) +// this.update_filter(); +// } +// +// /// +// /// Gets the current resampling ratio. This will be reduced to the least common denominator +// /// +// /// (Output) numerator of the sampling rate ratio +// /// (Output) denominator of the sampling rate ratio +// public void GetRateFraction(out int ratio_num, out int ratio_den) +// { +// ratio_num = this.num_rate; +// ratio_den = this.den_rate; +// } +// +// /// +// /// Gets or sets the resampling quality between 0 and 10, where 0 has poor +// /// quality and 10 has very high quality. +// /// +// public int Quality +// { +// get +// { +// return this.quality; +// } +// set +// { +// if (value > 10 || value < 0) +// throw new IllegalArgumentException("Quality must be between 0 and 10"); +// if (this.quality == value) +// return; +// this.quality = value; +// if (this.initialised != 0) +// this.update_filter(); +// } +// } +// +// /// +// /// Gets or sets the input stride +// /// +// public int InputStride +// { +// get +// { +// return this.in_stride; +// } +// set +// { +// this.in_stride = value; +// } +// } +// +// /// +// /// Gets or sets the output stride +// /// +// public int OutputStride +// { +// get +// { +// return this.out_stride; +// } +// set +// { +// this.out_stride = value; +// } +// } +// +// /// +// /// Get the latency introduced by the resampler measured in input samples. +// /// +// public int InputLatency +// { +// get +// { +// return this.filt_len / 2; +// } +// } +// +// /// +// /// Gets the latency introduced by the resampler measured in output samples. +// /// +// public int OutputLatency +// { +// get +// { +// return ((this.filt_len / 2) * this.den_rate + (this.num_rate >> 1)) / this.num_rate; +// } +// } +//} \ No newline at end of file diff --git a/app/src/main/java/org/concentus/Spread.java b/app/src/main/java/org/concentus/Spread.java new file mode 100644 index 0000000000..f03bbeae82 --- /dev/null +++ b/app/src/main/java/org/concentus/Spread.java @@ -0,0 +1,43 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class Spread { + + public static final int SPREAD_NONE = 0; + public static final int SPREAD_LIGHT = 1; + public static final int SPREAD_NORMAL = 2; + public static final int SPREAD_AGGRESSIVE = 3; +} diff --git a/app/src/main/java/org/concentus/Stereo.java b/app/src/main/java/org/concentus/Stereo.java new file mode 100644 index 0000000000..aa1f3a724f --- /dev/null +++ b/app/src/main/java/org/concentus/Stereo.java @@ -0,0 +1,509 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class Stereo { + + /// + /// Decode mid/side predictors + /// + /// I/O Compressor data structure + /// O Predictors + static void silk_stereo_decode_pred( + EntropyCoder psRangeDec, + int[] pred_Q13) { + int n; + int[][] ix = Arrays.InitTwoDimensionalArrayInt(2, 3); + int low_Q13, step_Q13; + + // Entropy decoding + n = psRangeDec.dec_icdf(SilkTables.silk_stereo_pred_joint_iCDF, 8); + ix[0][2] = Inlines.silk_DIV32_16(n, 5); + ix[1][2] = n - 5 * ix[0][2]; + for (n = 0; n < 2; n++) { + ix[n][0] = psRangeDec.dec_icdf(SilkTables.silk_uniform3_iCDF, 8); + ix[n][1] = psRangeDec.dec_icdf(SilkTables.silk_uniform5_iCDF, 8); + } + + // Dequantize + for (n = 0; n < 2; n++) { + ix[n][0] += 3 * ix[n][2]; + low_Q13 = SilkTables.silk_stereo_pred_quant_Q13[ix[n][0]]; + step_Q13 = Inlines.silk_SMULWB(SilkTables.silk_stereo_pred_quant_Q13[ix[n][0] + 1] - low_Q13, + ((int) ((0.5f / SilkConstants.STEREO_QUANT_SUB_STEPS) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.5f / SilkConstants.STEREO_QUANT_SUB_STEPS, 16)*/); + pred_Q13[n] = Inlines.silk_SMLABB(low_Q13, step_Q13, 2 * ix[n][1] + 1); + } + + /* Subtract second from first predictor (helps when actually applying these) */ + pred_Q13[0] -= pred_Q13[1]; + } + + /// + /// Decode mid-only flag + /// + /// I/O Compressor data structure + /// O Flag that only mid channel has been coded + static void silk_stereo_decode_mid_only( + EntropyCoder psRangeDec, + BoxedValueInt decode_only_mid + ) { + /* Decode flag that only mid channel is coded */ + decode_only_mid.Val = psRangeDec.dec_icdf(SilkTables.silk_stereo_only_code_mid_iCDF, 8); + } + + /// + /// Entropy code the mid/side quantization indices + /// + /// I/O Compressor data structure + /// I Quantization indices [ 2 ][ 3 ] + static void silk_stereo_encode_pred(EntropyCoder psRangeEnc, byte[][] ix) { + int n; + + /* Entropy coding */ + n = 5 * ix[0][2] + ix[1][2]; + Inlines.OpusAssert(n < 25); + psRangeEnc.enc_icdf(n, SilkTables.silk_stereo_pred_joint_iCDF, 8); + for (n = 0; n < 2; n++) { + Inlines.OpusAssert(ix[n][0] < 3); + Inlines.OpusAssert(ix[n][1] < SilkConstants.STEREO_QUANT_SUB_STEPS); + psRangeEnc.enc_icdf(ix[n][0], SilkTables.silk_uniform3_iCDF, 8); + psRangeEnc.enc_icdf(ix[n][1], SilkTables.silk_uniform5_iCDF, 8); + } + } + + /// + /// Entropy code the mid-only flag + /// + /// I/O Compressor data structure + /// + static void silk_stereo_encode_mid_only(EntropyCoder psRangeEnc, byte mid_only_flag) { + /* Encode flag that only mid channel is coded */ + psRangeEnc.enc_icdf(mid_only_flag, SilkTables.silk_stereo_only_code_mid_iCDF, 8); + } + + /// + /// Find least-squares prediction gain for one signal based on another and quantize it + /// + /// O Ratio of residual and mid energies + /// I Basis signal + /// I Target signal + /// I/O Smoothed mid, residual norms + /// I Number of samples + /// I Smoothing coefficient + /// O Returns predictor in Q13 + static int silk_stereo_find_predictor( + BoxedValueInt ratio_Q14, + short[] x, + short[] y, + int[] mid_res_amp_Q0, + int mid_res_amp_Q0_ptr, + int length, + int smooth_coef_Q16) { + int scale; + // fixme optimize this unboxing + BoxedValueInt nrgx = new BoxedValueInt(0); + BoxedValueInt nrgy = new BoxedValueInt(0); + BoxedValueInt scale1 = new BoxedValueInt(0); + BoxedValueInt scale2 = new BoxedValueInt(0); + int corr, pred_Q13, pred2_Q10; + + /* Find predictor */ + SumSqrShift.silk_sum_sqr_shift(nrgx, scale1, x, length); + SumSqrShift.silk_sum_sqr_shift(nrgy, scale2, y, length); + scale = Inlines.silk_max_int(scale1.Val, scale2.Val); + scale = scale + (scale & 1); + /* make even */ + nrgy.Val = Inlines.silk_RSHIFT32(nrgy.Val, scale - scale2.Val); + nrgx.Val = Inlines.silk_RSHIFT32(nrgx.Val, scale - scale1.Val); + nrgx.Val = Inlines.silk_max_int(nrgx.Val, 1); + corr = Inlines.silk_inner_prod_aligned_scale(x, y, scale, length); + pred_Q13 = Inlines.silk_DIV32_varQ(corr, nrgx.Val, 13); + pred_Q13 = Inlines.silk_LIMIT(pred_Q13, -(1 << 14), 1 << 14); + pred2_Q10 = Inlines.silk_SMULWB(pred_Q13, pred_Q13); + + /* Faster update for signals with large prediction parameters */ + smooth_coef_Q16 = (int) Inlines.silk_max_int(smooth_coef_Q16, Inlines.silk_abs(pred2_Q10)); + + /* Smoothed mid and residual norms */ + Inlines.OpusAssert(smooth_coef_Q16 < 32768); + scale = Inlines.silk_RSHIFT(scale, 1); + mid_res_amp_Q0[mid_res_amp_Q0_ptr] = Inlines.silk_SMLAWB(mid_res_amp_Q0[mid_res_amp_Q0_ptr], + Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(nrgx.Val), scale) - mid_res_amp_Q0[mid_res_amp_Q0_ptr], smooth_coef_Q16); + /* Residual energy = nrgy - 2 * pred * corr + pred^2 * nrgx */ + nrgy.Val = Inlines.silk_SUB_LSHIFT32(nrgy.Val, Inlines.silk_SMULWB(corr, pred_Q13), 3 + 1); + nrgy.Val = Inlines.silk_ADD_LSHIFT32(nrgy.Val, Inlines.silk_SMULWB(nrgx.Val, pred2_Q10), 6); + mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1] = Inlines.silk_SMLAWB(mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1], + Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(nrgy.Val), scale) - mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1], smooth_coef_Q16); + + /* Ratio of smoothed residual and mid norms */ + ratio_Q14.Val = Inlines.silk_DIV32_varQ(mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1], Inlines.silk_max(mid_res_amp_Q0[mid_res_amp_Q0_ptr], 1), 14); + ratio_Q14.Val = Inlines.silk_LIMIT(ratio_Q14.Val, 0, 32767); + + return pred_Q13; + } + + /// + /// Convert Left/Right stereo signal to adaptive Mid/Side representation + /// + /// I/O State + /// I/O Left input signal, becomes mid signal + /// I/O Right input signal, becomes side signal + /// O Quantization indices [ 2 ][ 3 ] + /// O Flag: only mid signal coded + /// O Bitrates for mid and side signals + /// I Total bitrate + /// I Speech activity level in previous frame + /// I Last frame before a stereo.mono transition + /// I Sample rate (kHz) + /// I Number of samples + static void silk_stereo_LR_to_MS( + StereoEncodeState state, + short[] x1, + int x1_ptr, + short[] x2, + int x2_ptr, + byte[][] ix, + BoxedValueByte mid_only_flag, + int[] mid_side_rates_bps, + int total_rate_bps, + int prev_speech_act_Q8, + int toMono, + int fs_kHz, + int frame_length) { + int n, is10msFrame, denom_Q16, delta0_Q13, delta1_Q13; + int sum, diff, smooth_coef_Q16, pred0_Q13, pred1_Q13; + int[] pred_Q13 = new int[2]; + int frac_Q16, frac_3_Q16, min_mid_rate_bps, width_Q14, w_Q24, deltaw_Q24; + BoxedValueInt LP_ratio_Q14 = new BoxedValueInt(0); + BoxedValueInt HP_ratio_Q14 = new BoxedValueInt(0); + short[] side; + short[] LP_mid; + short[] HP_mid; + short[] LP_side; + short[] HP_side; + int mid = x1_ptr - 2; + + side = new short[frame_length + 2]; + + /* Convert to basic mid/side signals */ + for (n = 0; n < frame_length + 2; n++) { + sum = x1[x1_ptr + n - 2] + (int) x2[x2_ptr + n - 2]; + diff = x1[x1_ptr + n - 2] - (int) x2[x2_ptr + n - 2]; + x1[mid + n] = (short) Inlines.silk_RSHIFT_ROUND(sum, 1); + side[n] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(diff, 1)); + } + + /* Buffering */ + System.arraycopy(state.sMid, 0, x1, mid, 2); + System.arraycopy(state.sSide, 0, side, 0, 2); + System.arraycopy(x1, mid + frame_length, state.sMid, 0, 2); + System.arraycopy(side, frame_length, state.sSide, 0, 2); + + /* LP and HP filter mid signal */ + LP_mid = new short[frame_length]; + HP_mid = new short[frame_length]; + for (n = 0; n < frame_length; n++) { + sum = Inlines.silk_RSHIFT_ROUND(Inlines.silk_ADD_LSHIFT32(x1[mid + n] + x1[mid + n + 2], x1[mid + n + 1], 1), 2); + LP_mid[n] = (short) (sum); + HP_mid[n] = (short) (x1[mid + n + 1] - sum); + } + + /* LP and HP filter side signal */ + LP_side = new short[frame_length]; + HP_side = new short[frame_length]; + for (n = 0; n < frame_length; n++) { + sum = Inlines.silk_RSHIFT_ROUND(Inlines.silk_ADD_LSHIFT32(side[n] + side[n + 2], side[n + 1], 1), 2); + LP_side[n] = (short) (sum); + HP_side[n] = (short) (side[n + 1] - sum); + } + + /* Find energies and predictors */ + is10msFrame = (frame_length == 10 * fs_kHz ? 1 : 0); + smooth_coef_Q16 = is10msFrame != 0 + ? ((int) ((SilkConstants.STEREO_RATIO_SMOOTH_COEF / 2) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.STEREO_RATIO_SMOOTH_COEF / 2, 16)*/ : ((int) ((SilkConstants.STEREO_RATIO_SMOOTH_COEF) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.STEREO_RATIO_SMOOTH_COEF, 16)*/; + smooth_coef_Q16 = Inlines.silk_SMULWB(Inlines.silk_SMULBB(prev_speech_act_Q8, prev_speech_act_Q8), smooth_coef_Q16); + + pred_Q13[0] = silk_stereo_find_predictor(LP_ratio_Q14, LP_mid, LP_side, state.mid_side_amp_Q0, 0, frame_length, smooth_coef_Q16); + pred_Q13[1] = silk_stereo_find_predictor(HP_ratio_Q14, HP_mid, HP_side, state.mid_side_amp_Q0, 2, frame_length, smooth_coef_Q16); + + /* Ratio of the norms of residual and mid signals */ + frac_Q16 = Inlines.silk_SMLABB(HP_ratio_Q14.Val, LP_ratio_Q14.Val, 3); + frac_Q16 = Inlines.silk_min(frac_Q16, ((int) ((1) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(1, 16)*/); + + /* Determine bitrate distribution between mid and side, and possibly reduce stereo width */ + total_rate_bps -= is10msFrame != 0 ? 1200 : 600; + /* Subtract approximate bitrate for coding stereo parameters */ + if (total_rate_bps < 1) { + total_rate_bps = 1; + } + min_mid_rate_bps = Inlines.silk_SMLABB(2000, fs_kHz, 900); + Inlines.OpusAssert(min_mid_rate_bps < 32767); + /* Default bitrate distribution: 8 parts for Mid and (5+3*frac) parts for Side. so: mid_rate = ( 8 / ( 13 + 3 * frac ) ) * total_ rate */ + frac_3_Q16 = Inlines.silk_MUL(3, frac_Q16); + mid_side_rates_bps[0] = Inlines.silk_DIV32_varQ(total_rate_bps, ((int) ((8 + 5) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(8 + 5, 16)*/ + frac_3_Q16, 16 + 3); + /* If Mid bitrate below minimum, reduce stereo width */ + if (mid_side_rates_bps[0] < min_mid_rate_bps) { + mid_side_rates_bps[0] = min_mid_rate_bps; + mid_side_rates_bps[1] = total_rate_bps - mid_side_rates_bps[0]; + /* width = 4 * ( 2 * side_rate - min_rate ) / ( ( 1 + 3 * frac ) * min_rate ) */ + width_Q14 = Inlines.silk_DIV32_varQ(Inlines.silk_LSHIFT(mid_side_rates_bps[1], 1) - min_mid_rate_bps, + Inlines.silk_SMULWB(((int) ((1) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(1, 16)*/ + frac_3_Q16, min_mid_rate_bps), 14 + 2); + width_Q14 = Inlines.silk_LIMIT(width_Q14, 0, ((int) ((1) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(1, 14)*/); + } else { + mid_side_rates_bps[1] = total_rate_bps - mid_side_rates_bps[0]; + width_Q14 = ((int) ((1) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(1, 14)*/; + } + + /* Smoother */ + state.smth_width_Q14 = (short) Inlines.silk_SMLAWB(state.smth_width_Q14, width_Q14 - state.smth_width_Q14, smooth_coef_Q16); + + /* At very low bitrates or for inputs that are nearly amplitude panned, switch to panned-mono coding */ + mid_only_flag.Val = 0; + if (toMono != 0) { + /* Last frame before stereo.mono transition; collapse stereo width */ + width_Q14 = 0; + pred_Q13[0] = 0; + pred_Q13[1] = 0; + silk_stereo_quant_pred(pred_Q13, ix); + } else if (state.width_prev_Q14 == 0 + && (8 * total_rate_bps < 13 * min_mid_rate_bps || Inlines.silk_SMULWB(frac_Q16, state.smth_width_Q14) < ((int) ((0.05f) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.05f, 14)*/)) { + /* Code as panned-mono; previous frame already had zero width */ + /* Scale down and quantize predictors */ + pred_Q13[0] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[0]), 14); + pred_Q13[1] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[1]), 14); + silk_stereo_quant_pred(pred_Q13, ix); + /* Collapse stereo width */ + width_Q14 = 0; + pred_Q13[0] = 0; + pred_Q13[1] = 0; + mid_side_rates_bps[0] = total_rate_bps; + mid_side_rates_bps[1] = 0; + mid_only_flag.Val = 1; + } else if (state.width_prev_Q14 != 0 + && (8 * total_rate_bps < 11 * min_mid_rate_bps || Inlines.silk_SMULWB(frac_Q16, state.smth_width_Q14) < ((int) ((0.02f) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.02f, 14)*/)) { + /* Transition to zero-width stereo */ + /* Scale down and quantize predictors */ + pred_Q13[0] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[0]), 14); + pred_Q13[1] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[1]), 14); + silk_stereo_quant_pred(pred_Q13, ix); + /* Collapse stereo width */ + width_Q14 = 0; + pred_Q13[0] = 0; + pred_Q13[1] = 0; + } else if (state.smth_width_Q14 > ((int) ((0.95f) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.95f, 14)*/) { + /* Full-width stereo coding */ + silk_stereo_quant_pred(pred_Q13, ix); + width_Q14 = ((int) ((1) * ((long) 1 << (14)) + 0.5))/*Inlines.SILK_CONST(1, 14)*/; + } else { + /* Reduced-width stereo coding; scale down and quantize predictors */ + pred_Q13[0] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[0]), 14); + pred_Q13[1] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[1]), 14); + silk_stereo_quant_pred(pred_Q13, ix); + width_Q14 = state.smth_width_Q14; + } + + /* Make sure to keep on encoding until the tapered output has been transmitted */ + if (mid_only_flag.Val == 1) { + state.silent_side_len += (short) (frame_length - SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz); + if (state.silent_side_len < SilkConstants.LA_SHAPE_MS * fs_kHz) { + mid_only_flag.Val = 0; + } else { + /* Limit to avoid wrapping around */ + state.silent_side_len = 10000; + } + } else { + state.silent_side_len = 0; + } + + if (mid_only_flag.Val == 0 && mid_side_rates_bps[1] < 1) { + mid_side_rates_bps[1] = 1; + mid_side_rates_bps[0] = Inlines.silk_max_int(1, total_rate_bps - mid_side_rates_bps[1]); + } + + /* Interpolate predictors and subtract prediction from side channel */ + pred0_Q13 = -state.pred_prev_Q13[0]; + pred1_Q13 = -state.pred_prev_Q13[1]; + w_Q24 = Inlines.silk_LSHIFT(state.width_prev_Q14, 10); + denom_Q16 = Inlines.silk_DIV32_16((int) 1 << 16, SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz); + delta0_Q13 = 0 - Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULBB(pred_Q13[0] - state.pred_prev_Q13[0], denom_Q16), 16); + delta1_Q13 = 0 - Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULBB(pred_Q13[1] - state.pred_prev_Q13[1], denom_Q16), 16); + deltaw_Q24 = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(width_Q14 - state.width_prev_Q14, denom_Q16), 10); + for (n = 0; n < SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz; n++) { + pred0_Q13 += delta0_Q13; + pred1_Q13 += delta1_Q13; + w_Q24 += deltaw_Q24; + sum = Inlines.silk_LSHIFT(Inlines.silk_ADD_LSHIFT(x1[mid + n] + x1[mid + n + 2], x1[mid + n + 1], 1), 9); + /* Q11 */ + sum = Inlines.silk_SMLAWB(Inlines.silk_SMULWB(w_Q24, side[n + 1]), sum, pred0_Q13); + /* Q8 */ + sum = Inlines.silk_SMLAWB(sum, Inlines.silk_LSHIFT((int) x1[mid + n + 1], 11), pred1_Q13); + /* Q8 */ + x2[x2_ptr + n - 1] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sum, 8)); + } + + pred0_Q13 = 0 - pred_Q13[0]; + pred1_Q13 = 0 - pred_Q13[1]; + w_Q24 = Inlines.silk_LSHIFT(width_Q14, 10); + for (n = SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz; n < frame_length; n++) { + sum = Inlines.silk_LSHIFT(Inlines.silk_ADD_LSHIFT(x1[mid + n] + x1[mid + n + 2], x1[mid + n + 1], 1), 9); + /* Q11 */ + sum = Inlines.silk_SMLAWB(Inlines.silk_SMULWB(w_Q24, side[n + 1]), sum, pred0_Q13); + /* Q8 */ + sum = Inlines.silk_SMLAWB(sum, Inlines.silk_LSHIFT((int) x1[mid + n + 1], 11), pred1_Q13); + /* Q8 */ + x2[x2_ptr + n - 1] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sum, 8)); + } + state.pred_prev_Q13[0] = (short) pred_Q13[0]; + state.pred_prev_Q13[1] = (short) pred_Q13[1]; + state.width_prev_Q14 = (short) width_Q14; + } + + /// + /// Convert adaptive Mid/Side representation to Left/Right stereo signal + /// + /// I/O State + /// I/O Left input signal, becomes mid signal + /// I/O Right input signal, becomes side signal + /// I Predictors + /// I Samples rate (kHz) + /// I Number of samples + static void silk_stereo_MS_to_LR( + StereoDecodeState state, + short[] x1, + int x1_ptr, + short[] x2, + int x2_ptr, + int[] pred_Q13, + int fs_kHz, + int frame_length) { + int n, denom_Q16, delta0_Q13, delta1_Q13; + int sum, diff, pred0_Q13, pred1_Q13; + + /* Buffering */ + System.arraycopy(state.sMid, 0, x1, x1_ptr, 2); + System.arraycopy(state.sSide, 0, x2, x2_ptr, 2); + System.arraycopy(x1, x1_ptr + frame_length, state.sMid, 0, 2); + System.arraycopy(x2, x2_ptr + frame_length, state.sSide, 0, 2); + + /* Interpolate predictors and add prediction to side channel */ + pred0_Q13 = state.pred_prev_Q13[0]; + pred1_Q13 = state.pred_prev_Q13[1]; + denom_Q16 = Inlines.silk_DIV32_16((int) 1 << 16, SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz); + delta0_Q13 = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULBB(pred_Q13[0] - state.pred_prev_Q13[0], denom_Q16), 16); + delta1_Q13 = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULBB(pred_Q13[1] - state.pred_prev_Q13[1], denom_Q16), 16); + for (n = 0; n < SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz; n++) { + pred0_Q13 += delta0_Q13; + pred1_Q13 += delta1_Q13; + sum = Inlines.silk_LSHIFT(Inlines.silk_ADD_LSHIFT(x1[x1_ptr + n] + x1[x1_ptr + n + 2], x1[x1_ptr + n + 1], 1), 9); + /* Q11 */ + sum = Inlines.silk_SMLAWB(Inlines.silk_LSHIFT((int) x2[x2_ptr + n + 1], 8), sum, pred0_Q13); + /* Q8 */ + sum = Inlines.silk_SMLAWB(sum, Inlines.silk_LSHIFT((int) x1[x1_ptr + n + 1], 11), pred1_Q13); + /* Q8 */ + x2[x2_ptr + n + 1] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sum, 8)); + } + pred0_Q13 = pred_Q13[0]; + pred1_Q13 = pred_Q13[1]; + for (n = SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz; n < frame_length; n++) { + sum = Inlines.silk_LSHIFT(Inlines.silk_ADD_LSHIFT(x1[x1_ptr + n] + x1[x1_ptr + n + 2], x1[x1_ptr + n + 1], 1), 9); + /* Q11 */ + sum = Inlines.silk_SMLAWB(Inlines.silk_LSHIFT((int) x2[x2_ptr + n + 1], 8), sum, pred0_Q13); + /* Q8 */ + sum = Inlines.silk_SMLAWB(sum, Inlines.silk_LSHIFT((int) x1[x1_ptr + n + 1], 11), pred1_Q13); + /* Q8 */ + x2[x2_ptr + n + 1] = (short) Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sum, 8)); + } + state.pred_prev_Q13[0] = (short) (pred_Q13[0]); + state.pred_prev_Q13[1] = (short) (pred_Q13[1]); + + /* Convert to left/right signals */ + for (n = 0; n < frame_length; n++) { + sum = x1[x1_ptr + n + 1] + (int) x2[x2_ptr + n + 1]; + diff = x1[x1_ptr + n + 1] - (int) x2[x2_ptr + n + 1]; + x1[x1_ptr + n + 1] = (short) Inlines.silk_SAT16(sum); + x2[x2_ptr + n + 1] = (short) Inlines.silk_SAT16(diff); + } + } + + /// + /// Quantize mid/side predictors + /// + /// I/O Predictors (out: quantized) + /// O Quantization indices [ 2 ][ 3 ] + static void silk_stereo_quant_pred( + int[] pred_Q13, + byte[][] ix) { + byte i, j; // [porting note] these were originally ints + int n; + int low_Q13, step_Q13, lvl_Q13, err_min_Q13, err_Q13, quant_pred_Q13 = 0; + + // FIXME: ix was formerly an out parameter that was newly allocated here + // but now it relies on the caller to initialize it + // clear ix + Arrays.MemSet(ix[0], (byte) 0, 3); + Arrays.MemSet(ix[1], (byte) 0, 3); + + /* Quantize */ + for (n = 0; n < 2; n++) { + boolean done = false; + + /* Brute-force search over quantization levels */ + err_min_Q13 = Integer.MAX_VALUE; + for (i = 0; !done && i < SilkConstants.STEREO_QUANT_TAB_SIZE - 1; i++) { + low_Q13 = SilkTables.silk_stereo_pred_quant_Q13[i]; + step_Q13 = Inlines.silk_SMULWB(SilkTables.silk_stereo_pred_quant_Q13[i + 1] - low_Q13, + ((int) ((0.5f / SilkConstants.STEREO_QUANT_SUB_STEPS) * ((long) 1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.5f / SilkConstants.STEREO_QUANT_SUB_STEPS, 16)*/); + + for (j = 0; !done && j < SilkConstants.STEREO_QUANT_SUB_STEPS; j++) { + lvl_Q13 = Inlines.silk_SMLABB(low_Q13, step_Q13, 2 * j + 1); + err_Q13 = Inlines.silk_abs(pred_Q13[n] - lvl_Q13); + if (err_Q13 < err_min_Q13) { + err_min_Q13 = err_Q13; + quant_pred_Q13 = lvl_Q13; + ix[n][0] = i; + ix[n][1] = j; + } else { + /* Error increasing, so we're past the optimum */ + done = true; + } + } + } + + ix[n][2] = (byte) (Inlines.silk_DIV32_16(ix[n][0], 3)); + ix[n][0] = (byte) (ix[n][0] - (byte) (ix[n][2] * 3)); + pred_Q13[n] = quant_pred_Q13; + } + + /* Subtract second from first predictor (helps when actually applying these) */ + pred_Q13[0] -= pred_Q13[1]; + } +} diff --git a/app/src/main/java/org/concentus/StereoDecodeState.java b/app/src/main/java/org/concentus/StereoDecodeState.java new file mode 100644 index 0000000000..4e9956fcd7 --- /dev/null +++ b/app/src/main/java/org/concentus/StereoDecodeState.java @@ -0,0 +1,45 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class StereoDecodeState { + + final short[] pred_prev_Q13 = new short[2]; + final short[] sMid = new short[2]; + final short[] sSide = new short[2]; + + void Reset() { + Arrays.MemSet(pred_prev_Q13, (short) 0, 2); + Arrays.MemSet(sMid, (short) 0, 2); + Arrays.MemSet(sSide, (short) 0, 2); + } +} diff --git a/app/src/main/java/org/concentus/StereoEncodeState.java b/app/src/main/java/org/concentus/StereoEncodeState.java new file mode 100644 index 0000000000..e151a25ef0 --- /dev/null +++ b/app/src/main/java/org/concentus/StereoEncodeState.java @@ -0,0 +1,62 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class StereoEncodeState { + + final short[] pred_prev_Q13 = new short[2]; + final short[] sMid = new short[2]; + final short[] sSide = new short[2]; + final int[] mid_side_amp_Q0 = new int[4]; + short smth_width_Q14 = 0; + short width_prev_Q14 = 0; + short silent_side_len = 0; + final byte[][][] predIx = Arrays.InitThreeDimensionalArrayByte(SilkConstants.MAX_FRAMES_PER_PACKET, 2, 3); + final byte[] mid_only_flags = new byte[SilkConstants.MAX_FRAMES_PER_PACKET]; + + void Reset() { + Arrays.MemSet(pred_prev_Q13, (short) 0, 2); + Arrays.MemSet(sMid, (short) 0, 2); + Arrays.MemSet(sSide, (short) 0, 2); + Arrays.MemSet(mid_side_amp_Q0, 0, 4); + smth_width_Q14 = 0; + width_prev_Q14 = 0; + silent_side_len = 0; + for (int x = 0; x < SilkConstants.MAX_FRAMES_PER_PACKET; x++) { + for (int y = 0; y < 2; y++) { + Arrays.MemSet(predIx[x][y], (byte) 0, 3); + } + } + + Arrays.MemSet(mid_only_flags, (byte) 0, SilkConstants.MAX_FRAMES_PER_PACKET); + } +} diff --git a/app/src/main/java/org/concentus/StereoWidthState.java b/app/src/main/java/org/concentus/StereoWidthState.java new file mode 100644 index 0000000000..5f92990400 --- /dev/null +++ b/app/src/main/java/org/concentus/StereoWidthState.java @@ -0,0 +1,52 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class StereoWidthState { + + int XX; + int XY; + int YY; + int smoothed_width; + int max_follower; + + void Reset() { + XX = 0; + XY = 0; + YY = 0; + smoothed_width = 0; + max_follower = 0; + } +} diff --git a/app/src/main/java/org/concentus/SumSqrShift.java b/app/src/main/java/org/concentus/SumSqrShift.java new file mode 100644 index 0000000000..b243d1bb3a --- /dev/null +++ b/app/src/main/java/org/concentus/SumSqrShift.java @@ -0,0 +1,157 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class SumSqrShift { + + /// + /// Compute number of bits to right shift the sum of squares of a vector + /// of int16s to make it fit in an int32 + /// + /// O Energy of x, after shifting to the right + /// O Number of bits right shift applied to energy + /// I Input vector + /// I Length of input vector + static void silk_sum_sqr_shift( + BoxedValueInt energy, + BoxedValueInt shift, + short[] x, + int x_ptr, + int len) { + int i, shft; + int nrg_tmp, nrg; + + nrg = 0; + shft = 0; + len--; + + for (i = 0; i < len; i += 2) { + nrg = Inlines.silk_SMLABB_ovflw(nrg, x[x_ptr + i], x[x_ptr + i]); + nrg = Inlines.silk_SMLABB_ovflw(nrg, x[x_ptr + i + 1], x[x_ptr + i + 1]); + if (nrg < 0) { + /* Scale down */ + nrg = ((int) Inlines.silk_RSHIFT_uint(nrg, 2)); + shft = 2; + i += 2; + break; + } + } + + for (; i < len; i += 2) { + nrg_tmp = Inlines.silk_SMULBB(x[x_ptr + i], x[x_ptr + i]); + nrg_tmp = Inlines.silk_SMLABB_ovflw(nrg_tmp, x[x_ptr + i + 1], x[x_ptr + i + 1]); + nrg = ((int) Inlines.silk_ADD_RSHIFT_uint(nrg, nrg_tmp, shft)); + if (nrg < 0) { + /* Scale down */ + nrg = ((int) Inlines.silk_RSHIFT_uint(nrg, 2)); + shft += 2; + } + } + + if (i == len) { + /* One sample left to process */ + nrg_tmp = Inlines.silk_SMULBB(x[x_ptr + i], x[x_ptr + i]); + nrg = ((int) Inlines.silk_ADD_RSHIFT_uint(nrg, nrg_tmp, shft)); + } + + /* Make sure to have at least one extra leading zero (two leading zeros in total) */ + if ((nrg & 0xC0000000) != 0) { + nrg = ((int) Inlines.silk_RSHIFT_uint(nrg, 2)); + shft += 2; + } + + /* Output arguments */ + shift.Val = shft; + energy.Val = nrg; + } + + /// + /// Zero-index variant + /// Compute number of bits to right shift the sum of squares of a vector + /// of int16s to make it fit in an int32 + /// + /// O Energy of x, after shifting to the right + /// O Number of bits right shift applied to energy + /// I Input vector + /// I Length of input vector + static void silk_sum_sqr_shift( + BoxedValueInt energy, + BoxedValueInt shift, + short[] x, + int len) { + int i, shft; + int nrg_tmp, nrg; + + nrg = 0; + shft = 0; + len--; + + for (i = 0; i < len; i += 2) { + nrg = Inlines.silk_SMLABB_ovflw(nrg, x[i], x[i]); + nrg = Inlines.silk_SMLABB_ovflw(nrg, x[i + 1], x[i + 1]); + if (nrg < 0) { + /* Scale down */ + nrg = ((int) Inlines.silk_RSHIFT_uint(nrg, 2)); + shft = 2; + i += 2; + break; + } + } + + for (; i < len; i += 2) { + nrg_tmp = Inlines.silk_SMULBB(x[i], x[i]); + nrg_tmp = Inlines.silk_SMLABB_ovflw(nrg_tmp, x[i + 1], x[i + 1]); + nrg = ((int) Inlines.silk_ADD_RSHIFT_uint(nrg, nrg_tmp, shft)); + if (nrg < 0) { + /* Scale down */ + nrg = ((int) Inlines.silk_RSHIFT_uint(nrg, 2)); + shft += 2; + } + } + + if (i == len) { + /* One sample left to process */ + nrg_tmp = Inlines.silk_SMULBB(x[i], x[i]); + nrg = ((int) Inlines.silk_ADD_RSHIFT_uint(nrg, nrg_tmp, shft)); + } + + /* Make sure to have at least one extra leading zero (two leading zeros in total) */ + if ((nrg & 0xC0000000) != 0) { + nrg = ((int) Inlines.silk_RSHIFT_uint(nrg, 2)); + shft += 2; + } + + /* Output arguments */ + shift.Val = shft; + energy.Val = nrg; + } +} diff --git a/app/src/main/java/org/concentus/TOCStruct.java b/app/src/main/java/org/concentus/TOCStruct.java new file mode 100644 index 0000000000..fd6fd7e7c7 --- /dev/null +++ b/app/src/main/java/org/concentus/TOCStruct.java @@ -0,0 +1,59 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Struct for TOC (Table of Contents) +/// +class TOCStruct { + + /// + /// Voice activity for packet + /// + int VADFlag = 0; + + /// + /// Voice activity for each frame in packet + /// + final int[] VADFlags = new int[SilkConstants.SILK_MAX_FRAMES_PER_PACKET]; + + /// + /// Flag indicating if packet contains in-band FEC + /// + int inbandFECFlag = 0; + + void Reset() { + VADFlag = 0; + Arrays.MemSet(VADFlags, 0, SilkConstants.SILK_MAX_FRAMES_PER_PACKET); + inbandFECFlag = 0; + } +} diff --git a/app/src/main/java/org/concentus/TonalityAnalysisState.java b/app/src/main/java/org/concentus/TonalityAnalysisState.java new file mode 100644 index 0000000000..a8ce1c6d8f --- /dev/null +++ b/app/src/main/java/org/concentus/TonalityAnalysisState.java @@ -0,0 +1,130 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class TonalityAnalysisState { + + boolean enabled = false; + final float[] angle = new float[240]; + final float[] d_angle = new float[240]; + final float[] d2_angle = new float[240]; + final int[] inmem = new int[OpusConstants.ANALYSIS_BUF_SIZE]; + int mem_fill; + /* number of usable samples in the buffer */ + final float[] prev_band_tonality = new float[OpusConstants.NB_TBANDS]; + float prev_tonality; + final float[][] E = Arrays.InitTwoDimensionalArrayFloat(OpusConstants.NB_FRAMES, OpusConstants.NB_TBANDS); + final float[] lowE = new float[OpusConstants.NB_TBANDS]; + final float[] highE = new float[OpusConstants.NB_TBANDS]; + final float[] meanE = new float[OpusConstants.NB_TOT_BANDS]; + final float[] mem = new float[32]; + final float[] cmean = new float[8]; + final float[] std = new float[9]; + float music_prob; + float Etracker; + float lowECount; + int E_count; + int last_music; + int last_transition; + int count; + final float[] subframe_mem = new float[3]; + int analysis_offset; + /** + * Probability of having speech for time i to DETECT_SIZE-1 (and music + * before). pspeech[0] is the probability that all frames in the window are + * speech. + */ + final float[] pspeech = new float[OpusConstants.DETECT_SIZE]; + /** + * Probability of having music for time i to DETECT_SIZE-1 (and speech + * before). pmusic[0] is the probability that all frames in the window are + * music. + */ + final float[] pmusic = new float[OpusConstants.DETECT_SIZE]; + float speech_confidence; + float music_confidence; + int speech_confidence_count; + int music_confidence_count; + int write_pos; + int read_pos; + int read_subframe; + final AnalysisInfo[] info = new AnalysisInfo[OpusConstants.DETECT_SIZE]; + + TonalityAnalysisState() { + for (int c = 0; c < OpusConstants.DETECT_SIZE; c++) { + info[c] = new AnalysisInfo(); + } + } + + void Reset() { + Arrays.MemSet(angle, 0f, 240); + Arrays.MemSet(d_angle, 0f, 240); + Arrays.MemSet(d2_angle, 0f, 240); + Arrays.MemSet(inmem, 0, OpusConstants.ANALYSIS_BUF_SIZE); + mem_fill = 0; + Arrays.MemSet(prev_band_tonality, 0f, OpusConstants.NB_TBANDS); + prev_tonality = 0; + for (int c = 0; c < OpusConstants.NB_FRAMES; c++) { + Arrays.MemSet(E[c], 0f, OpusConstants.NB_TBANDS); + } + Arrays.MemSet(lowE, 0f, OpusConstants.NB_TBANDS); + Arrays.MemSet(highE, 0f, OpusConstants.NB_TBANDS); + Arrays.MemSet(meanE, 0f, OpusConstants.NB_TOT_BANDS); + Arrays.MemSet(mem, 0f, 32); + Arrays.MemSet(cmean, 0f, 8); + Arrays.MemSet(std, 0f, 9); + music_prob = 0; + Etracker = 0; + lowECount = 0; + E_count = 0; + last_music = 0; + last_transition = 0; + count = 0; + Arrays.MemSet(subframe_mem, 0f, 3); + analysis_offset = 0; + Arrays.MemSet(pspeech, 0f, OpusConstants.DETECT_SIZE); + Arrays.MemSet(pmusic, 0f, OpusConstants.DETECT_SIZE); + speech_confidence = 0; + music_confidence = 0; + speech_confidence_count = 0; + music_confidence_count = 0; + write_pos = 0; + read_pos = 0; + read_subframe = 0; + for (int c = 0; c < OpusConstants.DETECT_SIZE; c++) { + info[c].Reset(); + } + } +} diff --git a/app/src/main/java/org/concentus/TuningParameters.java b/app/src/main/java/org/concentus/TuningParameters.java new file mode 100644 index 0000000000..c8c0f7184e --- /dev/null +++ b/app/src/main/java/org/concentus/TuningParameters.java @@ -0,0 +1,186 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class TuningParameters { + + /* Decay time for EntropyCoder.BITREServoir */ + static final int BITRESERVOIR_DECAY_TIME_MS = 500; + + /** + * **************** + */ + /* Pitch estimator */ + /** + * **************** + */ + + /* Level of noise floor for whitening filter LPC analysis in pitch analysis */ + static final float FIND_PITCH_WHITE_NOISE_FRACTION = 1e-3f; + + /* Bandwidth expansion for whitening filter in pitch analysis */ + static final float FIND_PITCH_BANDWIDTH_EXPANSION = 0.99f; + + /** + * ****************** + */ + /* Linear prediction */ + /** + * ****************** + */ + + /* LPC analysis regularization */ + static final float FIND_LPC_COND_FAC = 1e-5f; + + /* LTP analysis defines */ + static final float FIND_LTP_COND_FAC = 1e-5f; + static final float LTP_DAMPING = 0.05f; + static final float LTP_SMOOTHING = 0.1f; + + /* LTP quantization settings */ + static final float MU_LTP_QUANT_NB = 0.03f; + static final float MU_LTP_QUANT_MB = 0.025f; + static final float MU_LTP_QUANT_WB = 0.02f; + + /* Max cumulative LTP gain */ + static final float MAX_SUM_LOG_GAIN_DB = 250.0f; + + /** + * ******************** + */ + /* High pass filtering */ + /** + * ******************** + */ + + /* Smoothing parameters for low end of pitch frequency range estimation */ + static final float VARIABLE_HP_SMTH_COEF1 = 0.1f; + static final float VARIABLE_HP_SMTH_COEF2 = 0.015f; + static final float VARIABLE_HP_MAX_DELTA_FREQ = 0.4f; + + /* Min and max cut-off frequency values (-3 dB points) */ + static final int VARIABLE_HP_MIN_CUTOFF_HZ = 60; + static final int VARIABLE_HP_MAX_CUTOFF_HZ = 100; + + /** + * ******** + */ + /* Various */ + /** + * ******** + */ + + /* VAD threshold */ + static final float SPEECH_ACTIVITY_DTX_THRES = 0.05f; + + /* Speech Activity LBRR enable threshold */ + static final float LBRR_SPEECH_ACTIVITY_THRES = 0.3f; + + /** + * ********************** + */ + /* Perceptual parameters */ + /** + * ********************** + */ + + /* reduction in coding SNR during low speech activity */ + static final float BG_SNR_DECR_dB = 2.0f; + + /* factor for reducing quantization noise during voiced speech */ + static final float HARM_SNR_INCR_dB = 2.0f; + + /* factor for reducing quantization noise for unvoiced sparse signals */ + static final float SPARSE_SNR_INCR_dB = 2.0f; + + /* threshold for sparseness measure above which to use lower quantization offset during unvoiced */ + static final float SPARSENESS_THRESHOLD_QNT_OFFSET = 0.75f; + + /* warping control */ + static final float WARPING_MULTIPLIER = 0.015f; + + /* fraction added to first autocorrelation value */ + static final float SHAPE_WHITE_NOISE_FRACTION = 5e-5f; + + /* noise shaping filter chirp factor */ + static final float BANDWIDTH_EXPANSION = 0.95f; + + /* difference between chirp factors for analysis and synthesis noise shaping filters at low bitrates */ + static final float LOW_RATE_BANDWIDTH_EXPANSION_DELTA = 0.01f; + + /* extra harmonic boosting (signal shaping) at low bitrates */ + static final float LOW_RATE_HARMONIC_BOOST = 0.1f; + + /* extra harmonic boosting (signal shaping) for noisy input signals */ + static final float LOW_INPUT_QUALITY_HARMONIC_BOOST = 0.1f; + + /* harmonic noise shaping */ + static final float HARMONIC_SHAPING = 0.3f; + + /* extra harmonic noise shaping for high bitrates or noisy input */ + static final float HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING = 0.2f; + + /* parameter for shaping noise towards higher frequencies */ + static final float HP_NOISE_COEF = 0.25f; + + /* parameter for shaping noise even more towards higher frequencies during voiced speech */ + static final float HARM_HP_NOISE_COEF = 0.35f; + + /* parameter for applying a high-pass tilt to the input signal */ + static final float INPUT_TILT = 0.05f; + + /* parameter for extra high-pass tilt to the input signal at high rates */ + static final float HIGH_RATE_INPUT_TILT = 0.1f; + + /* parameter for reducing noise at the very low frequencies */ + static final float LOW_FREQ_SHAPING = 4.0f; + + /* less reduction of noise at the very low frequencies for signals with low SNR at low frequencies */ + static final float LOW_QUALITY_LOW_FREQ_SHAPING_DECR = 0.5f; + + /* subframe smoothing coefficient for HarmBoost, HarmShapeGain, Tilt (lower . more smoothing) */ + static final float SUBFR_SMTH_COEF = 0.4f; + + /* parameters defining the R/D tradeoff in the residual quantizer */ + static final float LAMBDA_OFFSET = 1.2f; + static final float LAMBDA_SPEECH_ACT = -0.2f; + static final float LAMBDA_DELAYED_DECISIONS = -0.05f; + static final float LAMBDA_INPUT_QUALITY = -0.1f; + static final float LAMBDA_CODING_QUALITY = -0.2f; + static final float LAMBDA_QUANT_OFFSET = 0.8f; + + /* Compensation in bitrate calculations for 10 ms modes */ + static final int REDUCE_BITRATE_10_MS_BPS = 2200; + + /* Maximum time before allowing a bandwidth transition */ + static final int MAX_BANDWIDTH_SWITCH_DELAY_MS = 5000; +} diff --git a/app/src/main/java/org/concentus/VQ.java b/app/src/main/java/org/concentus/VQ.java new file mode 100644 index 0000000000..4e9eb6f771 --- /dev/null +++ b/app/src/main/java/org/concentus/VQ.java @@ -0,0 +1,369 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class VQ { + + static void exp_rotation1(int[] X, int X_ptr, int len, int stride, int c, int s) { + int i; + int ms; + int Xptr; + Xptr = X_ptr; + ms = Inlines.NEG16(s); + for (i = 0; i < len - stride; i++) { + int x1, x2; + x1 = X[Xptr]; + x2 = X[Xptr + stride]; + X[Xptr + stride] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MAC16_16(Inlines.MULT16_16(c, x2), s, x1), 15)); + X[Xptr] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MAC16_16(Inlines.MULT16_16(c, x1), ms, x2), 15)); + Xptr++; + } + Xptr = X_ptr + (len - 2 * stride - 1); + for (i = len - 2 * stride - 1; i >= 0; i--) { + int x1, x2; + x1 = X[Xptr]; + x2 = X[Xptr + stride]; + X[Xptr + stride] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MAC16_16(Inlines.MULT16_16(c, x2), s, x1), 15)); + X[Xptr] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MAC16_16(Inlines.MULT16_16(c, x1), ms, x2), 15)); + Xptr--; + } + } + + private static int[] SPREAD_FACTOR = {15, 10, 5}; + + static void exp_rotation(int[] X, int X_ptr, int len, int dir, int stride, int K, int spread) { + int i; + int c, s; + int gain, theta; + int stride2 = 0; + int factor; + + if (2 * K >= len || spread == Spread.SPREAD_NONE) { + return; + } + + factor = SPREAD_FACTOR[spread - 1]; + + gain = (Inlines.celt_div((int) Inlines.MULT16_16(CeltConstants.Q15_ONE, len), (int) (len + factor * K))); + theta = Inlines.HALF16(Inlines.MULT16_16_Q15(gain, gain)); + + c = Inlines.celt_cos_norm(Inlines.EXTEND32(theta)); + s = Inlines.celt_cos_norm(Inlines.EXTEND32(Inlines.SUB16(CeltConstants.Q15ONE, theta))); + /* sin(theta) */ + + if (len >= 8 * stride) { + stride2 = 1; + /* This is just a simple (equivalent) way of computing sqrt(len/stride) with rounding. + It's basically incrementing long as (stride2+0.5)^2 < len/stride. */ + while ((stride2 * stride2 + stride2) * stride + (stride >> 2) < len) { + stride2++; + } + } + + /*NOTE: As a minor optimization, we could be passing around log2(B), not B, for both this and for + extract_collapse_mask().*/ + len = Inlines.celt_udiv(len, stride); + for (i = 0; i < stride; i++) { + if (dir < 0) { + if (stride2 != 0) { + exp_rotation1(X, X_ptr + (i * len), len, stride2, s, c); + } + + exp_rotation1(X, X_ptr + (i * len), len, 1, c, s); + } else { + exp_rotation1(X, X_ptr + (i * len), len, 1, c, (short) (0 - s)); + + if (stride2 != 0) { + exp_rotation1(X, X_ptr + (i * len), len, stride2, s, (short) (0 - c)); + } + } + } + } + + /** + * Takes the pitch vector and the decoded residual vector, computes the gain + * that will give ||p+g*y||=1 and mixes the residual with the pitch. + */ + static void normalise_residual(int[] iy, int[] X, int X_ptr, + int N, int Ryy, int gain) { + int i; + int k; + int t; + int g; + + k = Inlines.celt_ilog2(Ryy) >> 1; + t = Inlines.VSHR32(Ryy, 2 * (k - 7)); + g = Inlines.MULT16_16_P15(Inlines.celt_rsqrt_norm(t), gain); + + i = 0; + do { + X[X_ptr + i] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MULT16_16(g, iy[i]), k + 1)); + } while (++i < N); + } + + static int extract_collapse_mask(int[] iy, int N, int B) { + int collapse_mask; + int N0; + int i; + if (B <= 1) { + return 1; + } + /*NOTE: As a minor optimization, we could be passing around log2(B), not B, for both this and for + exp_rotation().*/ + N0 = Inlines.celt_udiv(N, B); + collapse_mask = 0; + i = 0; + do { + int j; + int tmp = 0; + j = 0; + do { + tmp |= iy[i * N0 + j]; + } while (++j < N0); + + collapse_mask |= (tmp != 0 ? 1 : 0) << i; + } while (++i < B); + + return collapse_mask; + } + + static int alg_quant(int[] X, int X_ptr, int N, int K, int spread, int B, EntropyCoder enc + ) { + int[] y = new int[N]; + int[] iy = new int[N]; + int[] signx = new int[N]; + int i, j; + int s; + int pulsesLeft; + int sum; + int xy; + int yy; + int collapse_mask; + + Inlines.OpusAssert(K > 0, "alg_quant() needs at least one pulse"); + Inlines.OpusAssert(N > 1, "alg_quant() needs at least two dimensions"); + + exp_rotation(X, X_ptr, N, 1, B, K, spread); + + /* Get rid of the sign */ + sum = 0; + j = 0; + do { + if (X[X_ptr + j] > 0) { + signx[j] = 1; + } else { + signx[j] = -1; + X[X_ptr + j] = (0 - X[X_ptr + j]); + } + + iy[j] = 0; + y[j] = 0; + } while (++j < N); + + xy = yy = 0; + + pulsesLeft = K; + + /* Do a pre-search by projecting on the pyramid */ + if (K > (N >> 1)) { + int rcp; + j = 0; + do { + sum += X[X_ptr + j]; + } while (++j < N); + + /* If X is too small, just replace it with a pulse at 0 */ + /* Prevents infinities and NaNs from causing too many pulses + to be allocated. 64 is an approximation of infinity here. */ + if (sum <= K) { + X[X_ptr] = ((short) (0.5 + (1.0f) * (((int) 1) << (14))))/*Inlines.QCONST16(1.0f, 14)*/; + j = X_ptr + 1; + do { + X[j] = 0; + } while (++j < N + X_ptr); + + sum = ((short) (0.5 + (1.0f) * (((int) 1) << (14))))/*Inlines.QCONST16(1.0f, 14)*/; + } + + rcp = Inlines.EXTRACT16(Inlines.MULT16_32_Q16((K - 1), Inlines.celt_rcp(sum))); + j = 0; + + do { + /* It's really important to round *towards zero* here */ + iy[j] = Inlines.MULT16_16_Q15(X[X_ptr + j], rcp); + y[j] = (int) iy[j]; + yy = (Inlines.MAC16_16(yy, y[j], y[j])); + xy = Inlines.MAC16_16(xy, X[X_ptr + j], y[j]); + y[j] *= 2; + pulsesLeft -= iy[j]; + } while (++j < N); + } + + Inlines.OpusAssert(pulsesLeft >= 1, "Allocated too many pulses in the quick pass"); + + /* This should never happen, but just in case it does (e.g. on silence) + we fill the first bin with pulses. */ + if (pulsesLeft > N + 3) { + int tmp = (int) pulsesLeft; + yy = (Inlines.MAC16_16(yy, tmp, tmp)); + yy = (Inlines.MAC16_16(yy, tmp, y[0])); + iy[0] += pulsesLeft; + pulsesLeft = 0; + } + + s = 1; + for (i = 0; i < pulsesLeft; i++) { + int best_id; + int best_num = 0 - CeltConstants.VERY_LARGE16; + int best_den = 0; + int rshift = 1 + Inlines.celt_ilog2(K - pulsesLeft + i + 1); + best_id = 0; + /* The squared magnitude term gets added anyway, so we might as well + add it outside the loop */ + yy = Inlines.ADD16(yy, 1); // opus bug - was add32 + j = 0; + do { + int Rxy, Ryy; + /* Temporary sums of the new pulse(s) */ + Rxy = Inlines.EXTRACT16(Inlines.SHR32(Inlines.ADD32(xy, Inlines.EXTEND32(X[X_ptr + j])), rshift)); + /* We're multiplying y[j] by two so we don't have to do it here */ + Ryy = Inlines.ADD16(yy, y[j]); + + /* Approximate score: we maximise Rxy/sqrt(Ryy) (we're guaranteed that + Rxy is positive because the sign is pre-computed) */ + Rxy = Inlines.MULT16_16_Q15(Rxy, Rxy); + /* The idea is to check for num/den >= best_num/best_den, but that way + we can do it without any division */ + /* OPT: Make sure to use conditional moves here */ + if (Inlines.MULT16_16(best_den, Rxy) > Inlines.MULT16_16(Ryy, best_num)) { + best_den = Ryy; + best_num = Rxy; + best_id = j; + } + } while (++j < N); + + /* Updating the sums of the new pulse(s) */ + xy = Inlines.ADD32(xy, Inlines.EXTEND32(X[X_ptr + best_id])); + /* We're multiplying y[j] by two so we don't have to do it here */ + yy = Inlines.ADD16(yy, y[best_id]); + + /* Only now that we've made the final choice, update y/iy */ + /* Multiplying y[j] by 2 so we don't have to do it everywhere else */ + y[best_id] = (y[best_id] + (2 * s)); + iy[best_id]++; + } + + /* Put the original sign back */ + j = 0; + do { + X[X_ptr + j] = (Inlines.MULT16_16(signx[j], X[X_ptr + j])); + if (signx[j] < 0) { + iy[j] = -iy[j]; + } + } while (++j < N); + + CWRS.encode_pulses(iy, N, K, enc); + + collapse_mask = extract_collapse_mask(iy, N, B); + + return collapse_mask; + } + + /** + * Decode pulse vector and combine the result with the pitch vector to + * produce the final normalised signal in the current band. + */ + static int alg_unquant(int[] X, int X_ptr, int N, int K, int spread, int B, + EntropyCoder dec, int gain) { + int Ryy; + int collapse_mask; + int[] iy = new int[N]; + Inlines.OpusAssert(K > 0, "alg_unquant() needs at least one pulse"); + Inlines.OpusAssert(N > 1, "alg_unquant() needs at least two dimensions"); + Ryy = CWRS.decode_pulses(iy, N, K, dec); + normalise_residual(iy, X, X_ptr, N, Ryy, gain); + exp_rotation(X, X_ptr, N, -1, B, K, spread); + collapse_mask = extract_collapse_mask(iy, N, B); + + return collapse_mask; + } + + static void renormalise_vector(int[] X, int X_ptr, int N, int gain) { + int i; + int k; + int E; + int g; + int t; + int xptr; + E = CeltConstants.EPSILON + Kernels.celt_inner_prod(X, X_ptr, X, X_ptr, N); + k = Inlines.celt_ilog2(E) >> 1; + t = Inlines.VSHR32(E, 2 * (k - 7)); + g = Inlines.MULT16_16_P15(Inlines.celt_rsqrt_norm(t), gain); + + xptr = X_ptr; + for (i = 0; i < N; i++) { + X[xptr] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MULT16_16(g, X[xptr]), k + 1)); + xptr++; + } + /*return celt_sqrt(E);*/ + } + + static int stereo_itheta(int[] X, int X_ptr, int[] Y, int Y_ptr, int stereo, int N) { + int i; + int itheta; + int mid, side; + int Emid, Eside; + + Emid = Eside = CeltConstants.EPSILON; + if (stereo != 0) { + for (i = 0; i < N; i++) { + int m, s; + m = Inlines.ADD16(Inlines.SHR16(X[X_ptr + i], 1), Inlines.SHR16(Y[Y_ptr + i], 1)); + s = Inlines.SUB16(Inlines.SHR16(X[X_ptr + i], 1), Inlines.SHR16(Y[Y_ptr + i], 1)); + Emid = Inlines.MAC16_16(Emid, m, m); + Eside = Inlines.MAC16_16(Eside, s, s); + } + } else { + Emid += Kernels.celt_inner_prod(X, X_ptr, X, X_ptr, N); + Eside += Kernels.celt_inner_prod(Y, Y_ptr, Y, Y_ptr, N); + } + mid = (Inlines.celt_sqrt(Emid)); + side = (Inlines.celt_sqrt(Eside)); + /* 0.63662 = 2/pi */ + itheta = Inlines.MULT16_16_Q15(((short) (0.5 + (0.63662f) * (((int) 1) << (15))))/*Inlines.QCONST16(0.63662f, 15)*/, Inlines.celt_atan2p(side, mid)); + + return itheta; + } +} diff --git a/app/src/main/java/org/concentus/VQ_WMat_EC.java b/app/src/main/java/org/concentus/VQ_WMat_EC.java new file mode 100644 index 0000000000..bf6e9784f4 --- /dev/null +++ b/app/src/main/java/org/concentus/VQ_WMat_EC.java @@ -0,0 +1,123 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class VQ_WMat_EC { + + /* Entropy constrained matrix-weighted VQ, hard-coded to 5-element vectors, for a single input data vector */ + static void silk_VQ_WMat_EC( + BoxedValueByte ind, /* O index of best codebook vector */ + BoxedValueInt rate_dist_Q14, /* O best weighted quant error + mu * rate */ + BoxedValueInt gain_Q7, /* O sum of absolute LTP coefficients */ + short[] in_Q14, /* I input vector to be quantized */ + int in_Q14_ptr, + int[] W_Q18, /* I weighting matrix */ + int W_Q18_ptr, + byte[][] cb_Q7, /* I codebook */ + short[] cb_gain_Q7, /* I codebook effective gain */ + short[] cl_Q5, /* I code length for each codebook vector */ + int mu_Q9, /* I tradeoff betw. weighted error and rate */ + int max_gain_Q7, /* I maximum sum of absolute LTP coefficients */ + int L /* I number of vectors in codebook */ + ) { + int k, gain_tmp_Q7; + byte[] cb_row_Q7; + int cb_row_Q7_ptr = 0; + short[] diff_Q14 = new short[5]; + int sum1_Q14, sum2_Q16; + + /* Loop over codebook */ + rate_dist_Q14.Val = Integer.MAX_VALUE; + for (k = 0; k < L; k++) { + /* Go to next cbk vector */ + cb_row_Q7 = cb_Q7[cb_row_Q7_ptr++]; + gain_tmp_Q7 = cb_gain_Q7[k]; + + diff_Q14[0] = (short) (in_Q14[in_Q14_ptr] - Inlines.silk_LSHIFT(cb_row_Q7[0], 7)); + diff_Q14[1] = (short) (in_Q14[in_Q14_ptr + 1] - Inlines.silk_LSHIFT(cb_row_Q7[1], 7)); + diff_Q14[2] = (short) (in_Q14[in_Q14_ptr + 2] - Inlines.silk_LSHIFT(cb_row_Q7[2], 7)); + diff_Q14[3] = (short) (in_Q14[in_Q14_ptr + 3] - Inlines.silk_LSHIFT(cb_row_Q7[3], 7)); + diff_Q14[4] = (short) (in_Q14[in_Q14_ptr + 4] - Inlines.silk_LSHIFT(cb_row_Q7[4], 7)); + + /* Weighted rate */ + sum1_Q14 = Inlines.silk_SMULBB(mu_Q9, cl_Q5[k]); + + /* Penalty for too large gain */ + sum1_Q14 = Inlines.silk_ADD_LSHIFT32(sum1_Q14, Inlines.silk_max(Inlines.silk_SUB32(gain_tmp_Q7, max_gain_Q7), 0), 10); + + Inlines.OpusAssert(sum1_Q14 >= 0); + + /* first row of W_Q18 */ + sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 1], diff_Q14[1]); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 2], diff_Q14[2]); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 3], diff_Q14[3]); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 4], diff_Q14[4]); + sum2_Q16 = Inlines.silk_LSHIFT(sum2_Q16, 1); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr], diff_Q14[0]); + sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[0]); + + /* second row of W_Q18 */ + sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 7], diff_Q14[2]); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 8], diff_Q14[3]); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 9], diff_Q14[4]); + sum2_Q16 = Inlines.silk_LSHIFT(sum2_Q16, 1); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 6], diff_Q14[1]); + sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[1]); + + /* third row of W_Q18 */ + sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 13], diff_Q14[3]); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 14], diff_Q14[4]); + sum2_Q16 = Inlines.silk_LSHIFT(sum2_Q16, 1); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 12], diff_Q14[2]); + sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[2]); + + /* fourth row of W_Q18 */ + sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 19], diff_Q14[4]); + sum2_Q16 = Inlines.silk_LSHIFT(sum2_Q16, 1); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 18], diff_Q14[3]); + sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[3]); + + /* last row of W_Q18 */ + sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 24], diff_Q14[4]); + sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[4]); + + Inlines.OpusAssert(sum1_Q14 >= 0); + + /* find best */ + if (sum1_Q14 < rate_dist_Q14.Val) { + rate_dist_Q14.Val = sum1_Q14; + ind.Val = (byte) k; + gain_Q7.Val = gain_tmp_Q7; + } + } + } +} diff --git a/app/src/main/java/org/concentus/VoiceActivityDetection.java b/app/src/main/java/org/concentus/VoiceActivityDetection.java new file mode 100644 index 0000000000..c7df2d16ef --- /dev/null +++ b/app/src/main/java/org/concentus/VoiceActivityDetection.java @@ -0,0 +1,402 @@ +/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +/// +/// Voice Activity Detection module for silk codec +/// +class VoiceActivityDetection { + + /// + /// Weighting factors for tilt measure + /// + private static final int[] tiltWeights = {30000, 6000, -12000, -12000}; + + /// + /// Initialization of the Silk VAD + /// + /// O Pointer to Silk VAD state. Cannot be nullptr + /// 0 if success + static int silk_VAD_Init(SilkVADState psSilk_VAD) { + int b, ret = 0; + + /* reset state memory */ + psSilk_VAD.Reset(); + + /* init noise levels */ + /* Initialize array with approx pink noise levels (psd proportional to inverse of frequency) */ + for (b = 0; b < SilkConstants.VAD_N_BANDS; b++) { + psSilk_VAD.NoiseLevelBias[b] = Inlines.silk_max_32(Inlines.silk_DIV32_16(SilkConstants.VAD_NOISE_LEVELS_BIAS, (short) (b + 1)), 1); + } + + /* Initialize state */ + for (b = 0; b < SilkConstants.VAD_N_BANDS; b++) { + psSilk_VAD.NL[b] = Inlines.silk_MUL(100, psSilk_VAD.NoiseLevelBias[b]); + psSilk_VAD.inv_NL[b] = Inlines.silk_DIV32(Integer.MAX_VALUE, psSilk_VAD.NL[b]); + } + + psSilk_VAD.counter = 15; + + /* init smoothed energy-to-noise ratio*/ + for (b = 0; b < SilkConstants.VAD_N_BANDS; b++) { + psSilk_VAD.NrgRatioSmth_Q8[b] = 100 * 256; + /* 100 * 256 -. 20 dB SNR */ + } + + return (ret); + } + + /// + /// Get the speech activity level in Q8 + /// + /// I/O Encoder state + /// I PCM input + /// 0 if success + static int silk_VAD_GetSA_Q8( + SilkChannelEncoder psEncC, + short[] pIn, + int pIn_ptr) { + int SA_Q15, pSNR_dB_Q7, input_tilt; + int decimated_framelength1, decimated_framelength2; + int decimated_framelength; + int dec_subframe_length, dec_subframe_offset, SNR_Q7, i, b, s; + int sumSquared = 0, smooth_coef_Q16; + short HPstateTmp; + short[] X; + int[] Xnrg = new int[SilkConstants.VAD_N_BANDS]; + int[] NrgToNoiseRatio_Q8 = new int[SilkConstants.VAD_N_BANDS]; + int speech_nrg, x_tmp; + int[] X_offset = new int[SilkConstants.VAD_N_BANDS]; + int ret = 0; + SilkVADState psSilk_VAD = psEncC.sVAD; + + /* Safety checks */ + Inlines.OpusAssert(SilkConstants.VAD_N_BANDS == 4); + Inlines.OpusAssert(SilkConstants.MAX_FRAME_LENGTH >= psEncC.frame_length); + Inlines.OpusAssert(psEncC.frame_length <= 512); + Inlines.OpusAssert(psEncC.frame_length == 8 * Inlines.silk_RSHIFT(psEncC.frame_length, 3)); + + /** + * ******************** + */ + /* Filter and Decimate */ + /** + * ******************** + */ + decimated_framelength1 = Inlines.silk_RSHIFT(psEncC.frame_length, 1); + decimated_framelength2 = Inlines.silk_RSHIFT(psEncC.frame_length, 2); + decimated_framelength = Inlines.silk_RSHIFT(psEncC.frame_length, 3); + + /* Decimate into 4 bands: + 0 L 3L L 3L 5L + - -- - -- -- + 8 8 2 4 4 + + [0-1 kHz| temp. |1-2 kHz| 2-4 kHz | 4-8 kHz | + + They're arranged to allow the minimal ( frame_length / 4 ) extra + scratch space during the downsampling process */ + X_offset[0] = 0; + X_offset[1] = decimated_framelength + decimated_framelength2; + X_offset[2] = X_offset[1] + decimated_framelength; + X_offset[3] = X_offset[2] + decimated_framelength2; + X = new short[X_offset[3] + decimated_framelength1]; + + /* 0-8 kHz to 0-4 kHz and 4-8 kHz */ + Filters.silk_ana_filt_bank_1(pIn, pIn_ptr, psSilk_VAD.AnaState, + X, X, X_offset[3], psEncC.frame_length); + + /* 0-4 kHz to 0-2 kHz and 2-4 kHz */ + Filters.silk_ana_filt_bank_1(X, 0, psSilk_VAD.AnaState1, + X, X, X_offset[2], decimated_framelength1); + + /* 0-2 kHz to 0-1 kHz and 1-2 kHz */ + Filters.silk_ana_filt_bank_1(X, 0, psSilk_VAD.AnaState2, + X, X, X_offset[1], decimated_framelength2); + + /** + * ****************************************** + */ + /* HP filter on lowest band (differentiator) */ + /** + * ****************************************** + */ + X[decimated_framelength - 1] = (short) (Inlines.silk_RSHIFT(X[decimated_framelength - 1], 1)); + HPstateTmp = X[decimated_framelength - 1]; + + for (i = decimated_framelength - 1; i > 0; i--) { + X[i - 1] = (short) (Inlines.silk_RSHIFT(X[i - 1], 1)); + X[i] -= X[i - 1]; + } + + X[0] -= psSilk_VAD.HPstate; + psSilk_VAD.HPstate = HPstateTmp; + + /** + * ********************************** + */ + /* Calculate the energy in each band */ + /** + * ********************************** + */ + for (b = 0; b < SilkConstants.VAD_N_BANDS; b++) { + /* Find the decimated framelength in the non-uniformly divided bands */ + decimated_framelength = Inlines.silk_RSHIFT(psEncC.frame_length, Inlines.silk_min_int(SilkConstants.VAD_N_BANDS - b, SilkConstants.VAD_N_BANDS - 1)); + + /* Split length into subframe lengths */ + dec_subframe_length = Inlines.silk_RSHIFT(decimated_framelength, SilkConstants.VAD_INTERNAL_SUBFRAMES_LOG2); + dec_subframe_offset = 0; + + /* Compute energy per sub-frame */ + /* initialize with summed energy of last subframe */ + Xnrg[b] = psSilk_VAD.XnrgSubfr[b]; + for (s = 0; s < SilkConstants.VAD_INTERNAL_SUBFRAMES; s++) { + sumSquared = 0; + + for (i = 0; i < dec_subframe_length; i++) { + /* The energy will be less than dec_subframe_length * ( silk_int16_MIN / 8 ) ^ 2. */ + /* Therefore we can accumulate with no risk of overflow (unless dec_subframe_length > 128) */ + x_tmp = Inlines.silk_RSHIFT( + X[X_offset[b] + i + dec_subframe_offset], 3); + sumSquared = Inlines.silk_SMLABB(sumSquared, x_tmp, x_tmp); + /* Safety check */ + Inlines.OpusAssert(sumSquared >= 0); + } + + /* Add/saturate summed energy of current subframe */ + if (s < SilkConstants.VAD_INTERNAL_SUBFRAMES - 1) { + Xnrg[b] = Inlines.silk_ADD_POS_SAT32(Xnrg[b], sumSquared); + } else { + /* Look-ahead subframe */ + Xnrg[b] = Inlines.silk_ADD_POS_SAT32(Xnrg[b], Inlines.silk_RSHIFT(sumSquared, 1)); + } + + dec_subframe_offset += dec_subframe_length; + } + + psSilk_VAD.XnrgSubfr[b] = sumSquared; + } + + /** + * ***************** + */ + /* Noise estimation */ + /** + * ***************** + */ + silk_VAD_GetNoiseLevels(Xnrg, psSilk_VAD); + + /** + * ******************************************** + */ + /* Signal-plus-noise to noise ratio estimation */ + /** + * ******************************************** + */ + sumSquared = 0; + input_tilt = 0; + for (b = 0; b < SilkConstants.VAD_N_BANDS; b++) { + speech_nrg = Xnrg[b] - psSilk_VAD.NL[b]; + if (speech_nrg > 0) { + /* Divide, with sufficient resolution */ + if ((Xnrg[b] & 0xFF800000) == 0) { + NrgToNoiseRatio_Q8[b] = Inlines.silk_DIV32(Inlines.silk_LSHIFT(Xnrg[b], 8), psSilk_VAD.NL[b] + 1); + } else { + NrgToNoiseRatio_Q8[b] = Inlines.silk_DIV32(Xnrg[b], Inlines.silk_RSHIFT(psSilk_VAD.NL[b], 8) + 1); + } + + /* Convert to log domain */ + SNR_Q7 = Inlines.silk_lin2log(NrgToNoiseRatio_Q8[b]) - 8 * 128; + + /* Sum-of-squares */ + sumSquared = Inlines.silk_SMLABB(sumSquared, SNR_Q7, SNR_Q7); + /* Q14 */ + + /* Tilt measure */ + if (speech_nrg < ((int) 1 << 20)) { + /* Scale down SNR value for small subband speech energies */ + SNR_Q7 = Inlines.silk_SMULWB(Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(speech_nrg), 6), SNR_Q7); + } + input_tilt = Inlines.silk_SMLAWB(input_tilt, tiltWeights[b], SNR_Q7); + } else { + NrgToNoiseRatio_Q8[b] = 256; + } + } + + /* Mean-of-squares */ + sumSquared = Inlines.silk_DIV32_16(sumSquared, SilkConstants.VAD_N_BANDS); + /* Q14 */ + + /* Root-mean-square approximation, scale to dBs, and write to output pointer */ + pSNR_dB_Q7 = (short) (3 * Inlines.silk_SQRT_APPROX(sumSquared)); + /* Q7 */ + + /** + * ****************************** + */ + /* Speech Probability Estimation */ + /** + * ****************************** + */ + SA_Q15 = Sigmoid.silk_sigm_Q15(Inlines.silk_SMULWB(SilkConstants.VAD_SNR_FACTOR_Q16, pSNR_dB_Q7) - SilkConstants.VAD_NEGATIVE_OFFSET_Q5); + + /** + * *********************** + */ + /* Frequency Tilt Measure */ + /** + * *********************** + */ + psEncC.input_tilt_Q15 = Inlines.silk_LSHIFT(Sigmoid.silk_sigm_Q15(input_tilt) - 16384, 1); + + /** + * *********************************************** + */ + /* Scale the sigmoid output based on power levels */ + /** + * *********************************************** + */ + speech_nrg = 0; + for (b = 0; b < SilkConstants.VAD_N_BANDS; b++) { + /* Accumulate signal-without-noise energies, higher frequency bands have more weight */ + speech_nrg += (b + 1) * Inlines.silk_RSHIFT(Xnrg[b] - psSilk_VAD.NL[b], 4); + } + + /* Power scaling */ + if (speech_nrg <= 0) { + SA_Q15 = Inlines.silk_RSHIFT(SA_Q15, 1); + } else if (speech_nrg < 32768) { + if (psEncC.frame_length == 10 * psEncC.fs_kHz) { + speech_nrg = Inlines.silk_LSHIFT_SAT32(speech_nrg, 16); + } else { + speech_nrg = Inlines.silk_LSHIFT_SAT32(speech_nrg, 15); + } + + /* square-root */ + speech_nrg = Inlines.silk_SQRT_APPROX(speech_nrg); + SA_Q15 = Inlines.silk_SMULWB(32768 + speech_nrg, SA_Q15); + } + + /* Copy the resulting speech activity in Q8 */ + psEncC.speech_activity_Q8 = Inlines.silk_min_int(Inlines.silk_RSHIFT(SA_Q15, 7), 255); + + /** + * ******************************** + */ + /* Energy Level and SNR estimation */ + /** + * ******************************** + */ + /* Smoothing coefficient */ + smooth_coef_Q16 = Inlines.silk_SMULWB(SilkConstants.VAD_SNR_SMOOTH_COEF_Q18, Inlines.silk_SMULWB((int) SA_Q15, SA_Q15)); + + if (psEncC.frame_length == 10 * psEncC.fs_kHz) { + smooth_coef_Q16 >>= 1; + } + + for (b = 0; b < SilkConstants.VAD_N_BANDS; b++) { + /* compute smoothed energy-to-noise ratio per band */ + psSilk_VAD.NrgRatioSmth_Q8[b] = Inlines.silk_SMLAWB(psSilk_VAD.NrgRatioSmth_Q8[b], + NrgToNoiseRatio_Q8[b] - psSilk_VAD.NrgRatioSmth_Q8[b], smooth_coef_Q16); + /* signal to noise ratio in dB per band */ + SNR_Q7 = 3 * (Inlines.silk_lin2log(psSilk_VAD.NrgRatioSmth_Q8[b]) - 8 * 128); + /* quality = sigmoid( 0.25 * ( SNR_dB - 16 ) ); */ + psEncC.input_quality_bands_Q15[b] = Sigmoid.silk_sigm_Q15(Inlines.silk_RSHIFT(SNR_Q7 - 16 * 128, 4)); + } + + return (ret); + } + + /// + /// Noise level estimation + /// + /// I subband energies [VAD_N_BANDS] + /// I/O Pointer to Silk VAD state + static void silk_VAD_GetNoiseLevels( + int[] pX, + SilkVADState psSilk_VAD) { + int k; + int nl, nrg, inv_nrg; + int coef, min_coef; + + /* Initially faster smoothing */ + if (psSilk_VAD.counter < 1000) { + /* 1000 = 20 sec */ + min_coef = Inlines.silk_DIV32_16(Short.MAX_VALUE, (short) (Inlines.silk_RSHIFT(psSilk_VAD.counter, 4) + 1)); + } else { + min_coef = 0; + } + + for (k = 0; k < SilkConstants.VAD_N_BANDS; k++) { + /* Get old noise level estimate for current band */ + nl = psSilk_VAD.NL[k]; + Inlines.OpusAssert(nl >= 0); + + /* Add bias */ + nrg = Inlines.silk_ADD_POS_SAT32(pX[k], psSilk_VAD.NoiseLevelBias[k]); + Inlines.OpusAssert(nrg > 0); + + /* Invert energies */ + inv_nrg = Inlines.silk_DIV32(Integer.MAX_VALUE, nrg); + Inlines.OpusAssert(inv_nrg >= 0); + + /* Less update when subband energy is high */ + if (nrg > Inlines.silk_LSHIFT(nl, 3)) { + coef = SilkConstants.VAD_NOISE_LEVEL_SMOOTH_COEF_Q16 >> 3; + } else if (nrg < nl) { + coef = SilkConstants.VAD_NOISE_LEVEL_SMOOTH_COEF_Q16; + } else { + coef = Inlines.silk_SMULWB(Inlines.silk_SMULWW(inv_nrg, nl), SilkConstants.VAD_NOISE_LEVEL_SMOOTH_COEF_Q16 << 1); + } + + /* Initially faster smoothing */ + coef = Inlines.silk_max_int(coef, min_coef); + + /* Smooth inverse energies */ + psSilk_VAD.inv_NL[k] = Inlines.silk_SMLAWB(psSilk_VAD.inv_NL[k], inv_nrg - psSilk_VAD.inv_NL[k], coef); + Inlines.OpusAssert(psSilk_VAD.inv_NL[k] >= 0); + + /* Compute noise level by inverting again */ + nl = Inlines.silk_DIV32(Integer.MAX_VALUE, psSilk_VAD.inv_NL[k]); + Inlines.OpusAssert(nl >= 0); + + /* Limit noise levels (guarantee 7 bits of head room) */ + nl = Inlines.silk_min(nl, 0x00FFFFFF); + + /* Store as part of state */ + psSilk_VAD.NL[k] = nl; + } + + /* Increment frame counter */ + psSilk_VAD.counter++; + } +} diff --git a/app/src/main/java/org/concentus/VorbisLayout.java b/app/src/main/java/org/concentus/VorbisLayout.java new file mode 100644 index 0000000000..65968fcff9 --- /dev/null +++ b/app/src/main/java/org/concentus/VorbisLayout.java @@ -0,0 +1,59 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2011 Xiph.Org Foundation + Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos, + Timothy B. Terriberry, and the Opus open-source contributors + Ported to Java by Logan Stromberg + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.concentus; + +class VorbisLayout { + + VorbisLayout(int streams, int coupled_streams, short[] map) { + nb_streams = streams; + nb_coupled_streams = coupled_streams; + mapping = map; + } + + int nb_streams; + int nb_coupled_streams; + short[] mapping; + + /* Index is nb_channel-1*/ + static final VorbisLayout[] vorbis_mappings = { + new VorbisLayout(1, 0, new short[]{0}), /* 1: mono */ + new VorbisLayout(1, 1, new short[]{0, 1}), /* 2: stereo */ + new VorbisLayout(2, 1, new short[]{0, 2, 1}), /* 3: 1-d surround */ + new VorbisLayout(2, 2, new short[]{0, 1, 2, 3}), /* 4: quadraphonic surround */ + new VorbisLayout(3, 2, new short[]{0, 4, 1, 2, 3}), /* 5: 5-channel surround */ + new VorbisLayout(4, 2, new short[]{0, 4, 1, 2, 3, 5}), /* 6: 5.1 surround */ + new VorbisLayout(4, 3, new short[]{0, 4, 1, 2, 3, 5, 6}), /* 7: 6.1 surround */ + new VorbisLayout(5, 3, new short[]{0, 6, 1, 2, 3, 4, 5, 7}), /* 8: 7.1 surround */}; +}