Files
LuaCsForBarotraumaEP/Libraries/Concentus/CSharp/Concentus/Opus/CodecHelpers.cs

713 lines
29 KiB
C#

/* 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 C# 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.
*/
using Concentus.Celt;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus;
using Concentus.Enums;
using Concentus.Silk;
using Concentus.Silk.Structs;
using Concentus.Structs;
using System;
namespace Concentus
{
public static class CodecHelpers
{
internal static byte gen_toc(OpusMode mode, int framerate, OpusBandwidth bandwidth, int channels)
{
int period;
byte toc;
period = 0;
while (framerate < 400)
{
framerate <<= 1;
period++;
}
if (mode == OpusMode.MODE_SILK_ONLY)
{
toc = (byte)((bandwidth - OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) << 5);
toc |= (byte)((period - 2) << 3);
}
else if (mode == OpusMode.MODE_CELT_ONLY)
{
int tmp = bandwidth - OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND;
if (tmp < 0)
tmp = 0;
toc = 0x80;
toc |= (byte)(tmp << 5);
toc |= (byte)(period << 3);
}
else /* Hybrid */
{
toc = 0x60;
toc |= (byte)((bandwidth - OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND) << 4);
toc |= (byte)((period - 2) << 3);
}
toc |= (byte)((channels == 2 ? 1 : 0) << 2);
return toc;
}
internal 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 <= int.MaxValue / ((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);
}
}
internal 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));
}
}
}
internal 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);
}
}
internal 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 const int MAX_DYNAMIC_FRAMESIZE = 24;
/* Estimates how much the bitrate will be boosted based on the sub-frame energy */
internal 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)
*/
internal static int transient_viterbi(float[] E, float[] E_1, int N, int frame_cost, int rate)
{
int i;
float[][] cost = Arrays.InitTwoDimensionalArray<float>(MAX_DYNAMIC_FRAMESIZE, 16);
int[][] states = Arrays.InitTwoDimensionalArray<int>(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;
}
internal static int optimize_framesize<T>(T[] x, int x_ptr, int len, int C, int Fs,
int bitrate, int tonality, float[] mem, int buffering,
Downmix.downmix_func<T> downmix)
{
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(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;
}
internal 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 (variable_duration >= OpusFramesize.OPUS_FRAMESIZE_2_5_MS && variable_duration <= OpusFramesize.OPUS_FRAMESIZE_60_MS)
new_size = Inlines.IMIN(3 * Fs / 50, (Fs / 400) << (variable_duration - 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;
}
internal static int compute_frame_size<T>(T[] analysis_pcm, int analysis_pcm_ptr, int frame_size,
OpusFramesize variable_duration, int C, int Fs, int bitrate_bps,
int delay_compensation, Downmix.downmix_func<T> downmix, float[] subframe_mem, bool 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, downmix);
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;
}
internal 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));
}
internal 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));
}
}
}
//internal static void opus_pcm_soft_clip(Pointer<float> _x, int N, int C, Pointer<float> declip_mem)
//{
// int c;
// int i;
// Pointer<float> 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;
// }
//}
internal static string opus_strerror(int error)
{
string[] error_strings = {
"success",
"invalid argument",
"buffer too small",
"internal error",
"corrupted stream",
"request not implemented",
"invalid state",
"memory allocation failed"
};
if (error > 0 || error < -7)
return "unknown error";
else
return error_strings[-error];
}
/// <summary>
/// Returns the version number of this library
/// </summary>
/// <returns></returns>
public static string GetVersionString()
{
return "Concentus 1.1.6"
#if DEBUG
+ "-debug"
#endif
#if FUZZING
+ "-fuzzing"
#endif
#if PARITY
#else
+ "-nonparity"
#endif
;
}
}
}