(caf7e6a2e) Replaced Concentus NuGet package with csproj (ensures correct System.Runtime references)
This commit is contained in:
@@ -0,0 +1,590 @@
|
||||
/* 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.Structs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Concentus
|
||||
{
|
||||
internal static class Analysis
|
||||
{
|
||||
private const double M_PI = 3.141592653;
|
||||
private const float cA = 0.43157974f;
|
||||
private const float cB = 0.67848403f;
|
||||
private const float cC = 0.08595542f;
|
||||
private const float cE = ((float)M_PI / 2);
|
||||
|
||||
private const int NB_TONAL_SKIP_BANDS = 9;
|
||||
|
||||
internal 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);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void tonality_analysis_init(TonalityAnalysisState tonal)
|
||||
{
|
||||
tonal.Reset();
|
||||
}
|
||||
|
||||
internal 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of signal being handled (either short or float) - changes based on which API is used</typeparam>
|
||||
/// <param name="tonal"></param>
|
||||
/// <param name="celt_mode"></param>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="len"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <param name="c1"></param>
|
||||
/// <param name="c2"></param>
|
||||
/// <param name="C"></param>
|
||||
/// <param name="lsb_depth"></param>
|
||||
/// <param name="downmix"></param>
|
||||
internal static void tonality_analysis<T>(TonalityAnalysisState tonal, CeltMode celt_mode, T[] x, int x_ptr, int len, int offset, int c1, int c2, int C, int lsb_depth, Downmix.downmix_func<T> downmix)
|
||||
{
|
||||
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(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 = Tables.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.MemMoveInt(tonal.inmem, OpusConstants.ANALYSIS_BUF_SIZE - 240, 0, 240);
|
||||
|
||||
remaining = len - (OpusConstants.ANALYSIS_BUF_SIZE - tonal.mem_fill);
|
||||
downmix(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 = Tables.tbands[b]; i < Tables.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 = Tables.extra_bands[b];
|
||||
band_end = Tables.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 += Tables.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;
|
||||
|
||||
mlp.mlp_process(Tables.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;
|
||||
|
||||
info.bandwidth = bandwidth;
|
||||
info.noisiness = frame_noisiness;
|
||||
info.valid = 1;
|
||||
}
|
||||
|
||||
internal static void run_analysis<T>(TonalityAnalysisState analysis, CeltMode celt_mode, T[] analysis_pcm, int analysis_pcm_ptr,
|
||||
int analysis_frame_size, int frame_size, int c1, int c2, int C, int Fs,
|
||||
int lsb_depth, Downmix.downmix_func<T> downmix, 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, downmix);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,712 @@
|
||||
/* 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
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/* 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.Common;
|
||||
using Concentus.Common.CPlusPlus;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace Concentus
|
||||
{
|
||||
internal static class Downmix
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of signal being handled (either short or float)</typeparam>
|
||||
/// <param name="_x"></param>
|
||||
/// <param name="sub"></param>
|
||||
/// <param name="subframe"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <param name="c1"></param>
|
||||
/// <param name="c2"></param>
|
||||
/// <param name="C"></param>
|
||||
public delegate void downmix_func<T>(T[] _x, int x_ptr, int[] sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C);
|
||||
|
||||
internal static void downmix_float(float[] x, int x_ptr, int[] sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C)
|
||||
{
|
||||
int scale;
|
||||
int j;
|
||||
int c1x = c1 + x_ptr;
|
||||
for (j = 0; j < subframe; j++)
|
||||
sub[sub_ptr + j] = Inlines.FLOAT2INT16(x[(j + offset) * C + c1x]);
|
||||
if (c2 > -1)
|
||||
{
|
||||
int c2x = c2 + x_ptr;
|
||||
for (j = 0; j < subframe; j++)
|
||||
sub[sub_ptr + j] += Inlines.FLOAT2INT16(x[(j + offset) * C + c2x]);
|
||||
}
|
||||
else if (c2 == -2)
|
||||
{
|
||||
int c;
|
||||
int cx;
|
||||
for (c = 1; c < C; c++)
|
||||
{
|
||||
cx = c + x_ptr;
|
||||
for (j = 0; j < subframe; j++)
|
||||
sub[sub_ptr + j] += Inlines.FLOAT2INT16(x[(j + offset) * C + cx]);
|
||||
}
|
||||
}
|
||||
scale = (1 << CeltConstants.SIG_SHIFT);
|
||||
if (C == -2)
|
||||
scale /= C;
|
||||
else
|
||||
scale /= 2;
|
||||
for (j = 0; j < subframe; j++)
|
||||
sub[sub_ptr + j] *= scale;
|
||||
}
|
||||
|
||||
internal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
namespace Concentus.Enums
|
||||
{
|
||||
public enum OpusApplication
|
||||
{
|
||||
OPUS_APPLICATION_UNIMPLEMENTED = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Best for most VoIP/videoconference applications where listening quality and intelligibility matter most
|
||||
/// </summary>
|
||||
OPUS_APPLICATION_VOIP = 2048,
|
||||
|
||||
/// <summary>
|
||||
/// Best for broadcast/high-fidelity application where the decoded audio should be as close as possible to the input
|
||||
/// </summary>
|
||||
OPUS_APPLICATION_AUDIO = 2049,
|
||||
|
||||
/// <summary>
|
||||
/// Only use when lowest-achievable latency is what matters most. Voice-optimized modes cannot be used.
|
||||
/// </summary>
|
||||
OPUS_APPLICATION_RESTRICTED_LOWDELAY = 2051
|
||||
}
|
||||
}
|
||||
@@ -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 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.
|
||||
*/
|
||||
|
||||
namespace Concentus.Enums
|
||||
{
|
||||
public enum OpusBandwidth
|
||||
{
|
||||
OPUS_BANDWIDTH_AUTO = -1000,
|
||||
OPUS_BANDWIDTH_NARROWBAND = 1101,
|
||||
OPUS_BANDWIDTH_MEDIUMBAND = 1102,
|
||||
OPUS_BANDWIDTH_WIDEBAND = 1103,
|
||||
OPUS_BANDWIDTH_SUPERWIDEBAND = 1104,
|
||||
OPUS_BANDWIDTH_FULLBAND = 1105
|
||||
}
|
||||
|
||||
// FIXME: We should remove all cases where bandwidth is cast to int, it's.....improper
|
||||
internal static class OpusBandwidthHelpers
|
||||
{
|
||||
internal static int GetOrdinal(OpusBandwidth bw)
|
||||
{
|
||||
return (int)bw - (int)OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND;
|
||||
}
|
||||
|
||||
internal static OpusBandwidth MIN(OpusBandwidth a, OpusBandwidth b)
|
||||
{
|
||||
if ((int)a < (int)b)
|
||||
return a;
|
||||
return b;
|
||||
}
|
||||
|
||||
internal static OpusBandwidth MAX(OpusBandwidth a, OpusBandwidth b)
|
||||
{
|
||||
if ((int)a > (int)b)
|
||||
return a;
|
||||
return b;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
namespace Concentus.Enums
|
||||
{
|
||||
public static class OpusControl
|
||||
{
|
||||
/** These are the actual Encoder CTL ID numbers.
|
||||
* They should not be used directly by applications.
|
||||
* In general, SETs should be even and GETs should be odd.*/
|
||||
public const int OPUS_SET_APPLICATION_REQUEST = 4000;
|
||||
public const int OPUS_GET_APPLICATION_REQUEST = 4001;
|
||||
public const int OPUS_SET_BITRATE_REQUEST = 4002;
|
||||
public const int OPUS_GET_BITRATE_REQUEST = 4003;
|
||||
public const int OPUS_SET_MAX_BANDWIDTH_REQUEST = 4004;
|
||||
public const int OPUS_GET_MAX_BANDWIDTH_REQUEST = 4005;
|
||||
public const int OPUS_SET_VBR_REQUEST = 4006;
|
||||
public const int OPUS_GET_VBR_REQUEST = 4007;
|
||||
public const int OPUS_SET_BANDWIDTH_REQUEST = 4008;
|
||||
public const int OPUS_GET_BANDWIDTH_REQUEST = 4009;
|
||||
public const int OPUS_SET_COMPLEXITY_REQUEST = 4010;
|
||||
public const int OPUS_GET_COMPLEXITY_REQUEST = 4011;
|
||||
public const int OPUS_SET_INBAND_FEC_REQUEST = 4012;
|
||||
public const int OPUS_GET_INBAND_FEC_REQUEST = 4013;
|
||||
public const int OPUS_SET_PACKET_LOSS_PERC_REQUEST = 4014;
|
||||
public const int OPUS_GET_PACKET_LOSS_PERC_REQUEST = 4015;
|
||||
public const int OPUS_SET_DTX_REQUEST = 4016;
|
||||
public const int OPUS_GET_DTX_REQUEST = 4017;
|
||||
public const int OPUS_SET_VBR_CONSTRAINT_REQUEST = 4020;
|
||||
public const int OPUS_GET_VBR_CONSTRAINT_REQUEST = 4021;
|
||||
public const int OPUS_SET_FORCE_CHANNELS_REQUEST = 4022;
|
||||
public const int OPUS_GET_FORCE_CHANNELS_REQUEST = 4023;
|
||||
public const int OPUS_SET_SIGNAL_REQUEST = 4024;
|
||||
public const int OPUS_GET_SIGNAL_REQUEST = 4025;
|
||||
public const int OPUS_GET_LOOKAHEAD_REQUEST = 4027;
|
||||
/* public const int OPUS_RESET_STATE 4028 */
|
||||
public const int OPUS_GET_SAMPLE_RATE_REQUEST = 4029;
|
||||
public const int OPUS_GET_FINAL_RANGE_REQUEST = 4031;
|
||||
public const int OPUS_GET_PITCH_REQUEST = 4033;
|
||||
public const int OPUS_SET_GAIN_REQUEST = 4034;
|
||||
public const int OPUS_GET_GAIN_REQUEST = 4045;
|
||||
public const int OPUS_SET_LSB_DEPTH_REQUEST = 4036;
|
||||
public const int OPUS_GET_LSB_DEPTH_REQUEST = 4037;
|
||||
public const int OPUS_GET_LAST_PACKET_DURATION_REQUEST = 4039;
|
||||
public const int OPUS_SET_EXPERT_FRAME_DURATION_REQUEST = 4040;
|
||||
public const int OPUS_GET_EXPERT_FRAME_DURATION_REQUEST = 4041;
|
||||
public const int OPUS_SET_PREDICTION_DISABLED_REQUEST = 4042;
|
||||
public const int OPUS_GET_PREDICTION_DISABLED_REQUEST = 4043;
|
||||
|
||||
/// <summary>
|
||||
/// Resets the codec state to be equivalent to a freshly initialized state.
|
||||
/// This should be called when switching streams in order to prevent
|
||||
/// the back to back decoding from giving different results from
|
||||
/// one at a time decoding.
|
||||
/// </summary>
|
||||
public const int OPUS_RESET_STATE = 4028;
|
||||
|
||||
public const int OPUS_SET_VOICE_RATIO_REQUEST = 11018;
|
||||
public const int OPUS_GET_VOICE_RATIO_REQUEST = 11019;
|
||||
public const int OPUS_SET_FORCE_MODE_REQUEST = 11002;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
namespace Concentus.Enums
|
||||
{
|
||||
/// <summary>
|
||||
/// Note that since most API-level errors are detected and thrown as
|
||||
/// OpusExceptions, direct use of this class is not usually needed
|
||||
/// </summary>
|
||||
public static class OpusError
|
||||
{
|
||||
/** No error*/
|
||||
public const int OPUS_OK = 0;
|
||||
|
||||
/** One or more invalid/out of range arguments*/
|
||||
public const int OPUS_BAD_ARG = -1;
|
||||
|
||||
/** Not enough bytes allocated in the buffer*/
|
||||
public const int OPUS_BUFFER_TOO_SMALL = -2;
|
||||
|
||||
/** An public error was detected*/
|
||||
public const int OPUS_INTERNAL_ERROR = -3;
|
||||
|
||||
/** The compressed data passed is corrupted*/
|
||||
public const int OPUS_INVALID_PACKET = -4;
|
||||
|
||||
/** Invalid/unsupported request number*/
|
||||
public const int OPUS_UNIMPLEMENTED = -5;
|
||||
|
||||
/** An encoder or decoder structure is invalid or already freed*/
|
||||
public const int OPUS_INVALID_STATE = -6;
|
||||
|
||||
/** Memory allocation has failed*/
|
||||
public const int OPUS_ALLOC_FAIL = -7;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
namespace Concentus.Enums
|
||||
{
|
||||
public enum OpusFramesize
|
||||
{
|
||||
/// <summary>
|
||||
/// Select frame size from the argument (default)
|
||||
/// </summary>
|
||||
OPUS_FRAMESIZE_ARG = 5000,
|
||||
|
||||
/// <summary>
|
||||
/// Use 2.5 ms frames
|
||||
/// </summary>
|
||||
OPUS_FRAMESIZE_2_5_MS = 5001,
|
||||
|
||||
/// <summary>
|
||||
/// Use 5 ms frames
|
||||
/// </summary>
|
||||
OPUS_FRAMESIZE_5_MS = 5002,
|
||||
|
||||
/// <summary>
|
||||
/// Use 10 ms frames
|
||||
/// </summary>
|
||||
OPUS_FRAMESIZE_10_MS = 5003,
|
||||
|
||||
/// <summary>
|
||||
/// Use 20 ms frames
|
||||
/// </summary>
|
||||
OPUS_FRAMESIZE_20_MS = 5004,
|
||||
|
||||
/// <summary>
|
||||
/// Use 40 ms frames
|
||||
/// </summary>
|
||||
OPUS_FRAMESIZE_40_MS = 5005,
|
||||
|
||||
/// <summary>
|
||||
/// Use 60 ms frames
|
||||
/// </summary>
|
||||
OPUS_FRAMESIZE_60_MS = 5006,
|
||||
|
||||
/// <summary>
|
||||
/// Do not use - not fully implemented. Optimize the frame size dynamically.
|
||||
/// </summary>
|
||||
OPUS_FRAMESIZE_VARIABLE = 5010
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
namespace Concentus.Enums
|
||||
{
|
||||
public enum OpusMode
|
||||
{
|
||||
MODE_AUTO = -1000,
|
||||
MODE_SILK_ONLY = 1000,
|
||||
MODE_HYBRID = 1001,
|
||||
MODE_CELT_ONLY = 1002
|
||||
}
|
||||
}
|
||||
@@ -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 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.
|
||||
*/
|
||||
|
||||
namespace Concentus.Enums
|
||||
{
|
||||
public enum OpusSignal
|
||||
{
|
||||
OPUS_SIGNAL_AUTO = -1000,
|
||||
|
||||
/// <summary>
|
||||
/// Signal being encoded is voice
|
||||
/// </summary>
|
||||
OPUS_SIGNAL_VOICE = 3001,
|
||||
|
||||
/// <summary>
|
||||
/// Signal being encoded is music
|
||||
/// </summary>
|
||||
OPUS_SIGNAL_MUSIC = 3002
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/* Copyright (c) 2008-2011 Octasic Inc.
|
||||
Originally written by Jean-Marc Valin
|
||||
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.Common;
|
||||
using Concentus.Common.CPlusPlus;
|
||||
using Concentus.Structs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace Concentus
|
||||
{
|
||||
/// <summary>
|
||||
/// multi-layer perceptron processor
|
||||
/// </summary>
|
||||
internal static class mlp
|
||||
{
|
||||
private const int MAX_NEURONS = 100;
|
||||
|
||||
internal 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 = Tables.tansig_table[i];
|
||||
dy = 1 - y * y;
|
||||
y = y + x * dy * (1 - y * x);
|
||||
return sign * y;
|
||||
}
|
||||
|
||||
internal static void mlp_process(MLP 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 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.Common.CPlusPlus;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace Concentus
|
||||
{
|
||||
internal static class OpusCompare
|
||||
{
|
||||
private const int NBANDS = 21;
|
||||
private const int NFREQS = 240;
|
||||
private const int TEST_WIN_SIZE = 480;
|
||||
private const int TEST_WIN_STEP = 120;
|
||||
|
||||
/*Bands on which we compute the pseudo-NMR (Bark-derived CELT bands).*/
|
||||
private static readonly int[] BANDS/*[NBANDS + 1]*/ ={
|
||||
0,2,4,6,8,10,12,14,16,20,24,28,32,40,48,56,68,80,96,120,156,200
|
||||
};
|
||||
|
||||
private static void band_energy(Pointer<float> _out, Pointer<float> _ps,
|
||||
Pointer<int> _bands, int _nbands,
|
||||
Pointer<float> _in, int _nchannels, int _nframes, int _window_sz,
|
||||
int _step, int _downsample)
|
||||
{
|
||||
Pointer<float> window;
|
||||
Pointer<float> x;
|
||||
Pointer<float> c;
|
||||
Pointer<float> s;
|
||||
int xi;
|
||||
int xj;
|
||||
int ps_sz;
|
||||
window = Pointer.Malloc<float>((3 + _nchannels) * _window_sz);
|
||||
c = window.Point(_window_sz);
|
||||
s = c.Point(_window_sz);
|
||||
x = s.Point(_window_sz);
|
||||
ps_sz = _window_sz / 2;
|
||||
for (xj = 0; xj < _window_sz; xj++)
|
||||
{
|
||||
window[xj] = (float)(0.5 - 0.5 * Math.Cos((2 * Math.PI / (_window_sz - 1)) * xj));
|
||||
}
|
||||
for (xj = 0; xj < _window_sz; xj++)
|
||||
{
|
||||
c[xj] = (float)Math.Cos((2 * Math.PI / _window_sz) * xj);
|
||||
}
|
||||
for (xj = 0; xj < _window_sz; xj++)
|
||||
{
|
||||
s[xj] = (float)Math.Sin((2 * Math.PI / _window_sz) * xj);
|
||||
}
|
||||
for (xi = 0; xi < _nframes; xi++)
|
||||
{
|
||||
int ci;
|
||||
int xk;
|
||||
int bi;
|
||||
for (ci = 0; ci < _nchannels; ci++)
|
||||
{
|
||||
for (xk = 0; xk < _window_sz; xk++)
|
||||
{
|
||||
x[ci * _window_sz + xk] = window[xk] * _in[(xi * _step + xk) * _nchannels + ci];
|
||||
}
|
||||
}
|
||||
for (bi = xj = 0; bi < _nbands; bi++)
|
||||
{
|
||||
float[] p = { 0, 0 };
|
||||
for (; xj < _bands[bi + 1]; xj++)
|
||||
{
|
||||
for (ci = 0; ci < _nchannels; ci++)
|
||||
{
|
||||
float re;
|
||||
float im;
|
||||
int ti;
|
||||
ti = 0;
|
||||
re = im = 0;
|
||||
for (xk = 0; xk < _window_sz; xk++)
|
||||
{
|
||||
re += c[ti] * x[ci * _window_sz + xk];
|
||||
im -= s[ti] * x[ci * _window_sz + xk];
|
||||
ti += xj;
|
||||
if (ti >= _window_sz) ti -= _window_sz;
|
||||
}
|
||||
re *= _downsample;
|
||||
im *= _downsample;
|
||||
_ps[(xi * ps_sz + xj) * _nchannels + ci] = re * re + im * im + 100000;
|
||||
p[ci] += _ps[(xi * ps_sz + xj) * _nchannels + ci];
|
||||
}
|
||||
}
|
||||
if (_out != null)
|
||||
{
|
||||
_out[(xi * _nbands + bi) * _nchannels] = p[0] / (_bands[bi + 1] - _bands[bi]);
|
||||
if (_nchannels == 2)
|
||||
{
|
||||
_out[(xi * _nbands + bi) * _nchannels + 1] = p[1] / (_bands[bi + 1] - _bands[bi]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static float compare(float[] x, float[] y, int nchannels, int rate = 48000)
|
||||
{
|
||||
Pointer<float> xb;
|
||||
Pointer<float> X;
|
||||
Pointer<float> Y;
|
||||
double err;
|
||||
float Q;
|
||||
int xlength = x.Length;
|
||||
int ylength = y.Length;
|
||||
int nframes;
|
||||
int xi;
|
||||
int ci;
|
||||
int xj;
|
||||
int bi;
|
||||
int downsample;
|
||||
int ybands;
|
||||
int yfreqs;
|
||||
int max_compare;
|
||||
ybands = NBANDS;
|
||||
yfreqs = NFREQS;
|
||||
if (rate != 8000 && rate != 12000 && rate != 16000 && rate != 24000 && rate != 48000)
|
||||
{
|
||||
throw new ArgumentException("Sampling rate must be 8000, 12000, 16000, 24000, or 48000\n");
|
||||
}
|
||||
if (rate != 48000)
|
||||
{
|
||||
downsample = 48000 / rate;
|
||||
switch (rate)
|
||||
{
|
||||
case 8000: ybands = 13; break;
|
||||
case 12000: ybands = 15; break;
|
||||
case 16000: ybands = 17; break;
|
||||
case 24000: ybands = 19; break;
|
||||
}
|
||||
yfreqs = NFREQS / downsample;
|
||||
}
|
||||
else
|
||||
{
|
||||
downsample = 1;
|
||||
}
|
||||
|
||||
if (xlength != ylength * downsample)
|
||||
{
|
||||
throw new ArgumentException("Sample counts do not match");
|
||||
}
|
||||
|
||||
if (xlength < TEST_WIN_SIZE)
|
||||
{
|
||||
throw new ArgumentException("Insufficient sample data");
|
||||
}
|
||||
|
||||
nframes = (xlength - TEST_WIN_SIZE + TEST_WIN_STEP) / TEST_WIN_STEP;
|
||||
xb = Pointer.Malloc<float>(nframes * NBANDS * nchannels);
|
||||
X = Pointer.Malloc<float>(nframes * NFREQS * nchannels);
|
||||
Y = Pointer.Malloc<float>(nframes * yfreqs * nchannels);
|
||||
/*Compute the per-band spectral energy of the original signal
|
||||
and the error.*/
|
||||
band_energy(xb, X, BANDS.GetPointer(), NBANDS, x.GetPointer(), nchannels, nframes,
|
||||
TEST_WIN_SIZE, TEST_WIN_STEP, 1);
|
||||
band_energy(null, Y, BANDS.GetPointer(), ybands, y.GetPointer(), nchannels, nframes,
|
||||
TEST_WIN_SIZE / downsample, TEST_WIN_STEP / downsample, downsample);
|
||||
for (xi = 0; xi < nframes; xi++)
|
||||
{
|
||||
/*Frequency masking (low to high): 10 dB/Bark slope.*/
|
||||
for (bi = 1; bi < NBANDS; bi++)
|
||||
{
|
||||
for (ci = 0; ci < nchannels; ci++)
|
||||
{
|
||||
xb[(xi * NBANDS + bi) * nchannels + ci] +=
|
||||
0.1F * xb[(xi * NBANDS + bi - 1) * nchannels + ci];
|
||||
}
|
||||
}
|
||||
/*Frequency masking (high to low): 15 dB/Bark slope.*/
|
||||
for (bi = NBANDS - 1; bi-- > 0;)
|
||||
{
|
||||
for (ci = 0; ci < nchannels; ci++)
|
||||
{
|
||||
xb[(xi * NBANDS + bi) * nchannels + ci] +=
|
||||
0.03F * xb[(xi * NBANDS + bi + 1) * nchannels + ci];
|
||||
}
|
||||
}
|
||||
if (xi > 0)
|
||||
{
|
||||
/*Temporal masking: -3 dB/2.5ms slope.*/
|
||||
for (bi = 0; bi < NBANDS; bi++)
|
||||
{
|
||||
for (ci = 0; ci < nchannels; ci++)
|
||||
{
|
||||
xb[(xi * NBANDS + bi) * nchannels + ci] +=
|
||||
0.5F * xb[((xi - 1) * NBANDS + bi) * nchannels + ci];
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Allowing some cross-talk */
|
||||
if (nchannels == 2)
|
||||
{
|
||||
for (bi = 0; bi < NBANDS; bi++)
|
||||
{
|
||||
float l, r;
|
||||
l = xb[(xi * NBANDS + bi) * nchannels + 0];
|
||||
r = xb[(xi * NBANDS + bi) * nchannels + 1];
|
||||
xb[(xi * NBANDS + bi) * nchannels + 0] += 0.01F * r;
|
||||
xb[(xi * NBANDS + bi) * nchannels + 1] += 0.01F * l;
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply masking */
|
||||
for (bi = 0; bi < ybands; bi++)
|
||||
{
|
||||
for (xj = BANDS[bi]; xj < BANDS[bi + 1]; xj++)
|
||||
{
|
||||
for (ci = 0; ci < nchannels; ci++)
|
||||
{
|
||||
X[(xi * NFREQS + xj) * nchannels + ci] +=
|
||||
0.1F * xb[(xi * NBANDS + bi) * nchannels + ci];
|
||||
Y[(xi * yfreqs + xj) * nchannels + ci] +=
|
||||
0.1F * xb[(xi * NBANDS + bi) * nchannels + ci];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Average of consecutive frames to make comparison slightly less sensitive */
|
||||
for (bi = 0; bi < ybands; bi++)
|
||||
{
|
||||
for (xj = BANDS[bi]; xj < BANDS[bi + 1]; xj++)
|
||||
{
|
||||
for (ci = 0; ci < nchannels; ci++)
|
||||
{
|
||||
float xtmp;
|
||||
float ytmp;
|
||||
xtmp = X[xj * nchannels + ci];
|
||||
ytmp = Y[xj * nchannels + ci];
|
||||
for (xi = 1; xi < nframes; xi++)
|
||||
{
|
||||
float xtmp2;
|
||||
float ytmp2;
|
||||
xtmp2 = X[(xi * NFREQS + xj) * nchannels + ci];
|
||||
ytmp2 = Y[(xi * yfreqs + xj) * nchannels + ci];
|
||||
X[(xi * NFREQS + xj) * nchannels + ci] += xtmp;
|
||||
Y[(xi * yfreqs + xj) * nchannels + ci] += ytmp;
|
||||
xtmp = xtmp2;
|
||||
ytmp = ytmp2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*If working at a lower sampling rate, don't take into account the last
|
||||
300 Hz to allow for different transition bands.
|
||||
For 12 kHz, we don't skip anything, because the last band already skips
|
||||
400 Hz.*/
|
||||
if (rate == 48000) max_compare = BANDS[NBANDS];
|
||||
else if (rate == 12000) max_compare = BANDS[ybands];
|
||||
else max_compare = BANDS[ybands] - 3;
|
||||
err = 0;
|
||||
for (xi = 0; xi < nframes; xi++)
|
||||
{
|
||||
double Ef;
|
||||
Ef = 0;
|
||||
for (bi = 0; bi < ybands; bi++)
|
||||
{
|
||||
double Eb;
|
||||
Eb = 0;
|
||||
for (xj = BANDS[bi]; xj < BANDS[bi + 1] && xj < max_compare; xj++)
|
||||
{
|
||||
for (ci = 0; ci < nchannels; ci++)
|
||||
{
|
||||
float re;
|
||||
float im;
|
||||
re = Y[(xi * yfreqs + xj) * nchannels + ci] / X[(xi * NFREQS + xj) * nchannels + ci];
|
||||
im = re - (float)Math.Log(re) - 1;
|
||||
/*Make comparison less sensitive around the SILK/CELT cross-over to
|
||||
allow for mode freedom in the filters.*/
|
||||
if (xj >= 79 && xj <= 81) im *= 0.1F;
|
||||
if (xj == 80) im *= 0.1F;
|
||||
Eb += im;
|
||||
}
|
||||
}
|
||||
Eb /= (BANDS[bi + 1] - BANDS[bi]) * nchannels;
|
||||
Ef += Eb * Eb;
|
||||
}
|
||||
/*Using a fixed normalization value means we're willing to accept slightly
|
||||
lower quality for lower sampling rates.*/
|
||||
Ef /= NBANDS;
|
||||
Ef *= Ef;
|
||||
err += Ef * Ef;
|
||||
}
|
||||
err = Math.Pow(err / nframes, 1.0 / 16);
|
||||
Q = (float)(100 * (1 - 0.5 * Math.Log(1 + err) / Math.Log(1.13)));
|
||||
|
||||
if (Q < 0)
|
||||
{
|
||||
Debug.WriteLine("Test vector FAILS");
|
||||
Debug.WriteLine(string.Format("Internal weighted error is {0}", err));
|
||||
}
|
||||
else {
|
||||
Debug.WriteLine("Test vector PASSES");
|
||||
Debug.WriteLine(string.Format("Opus quality metric: {0} (internal weighted error is {1})", Q, err));
|
||||
}
|
||||
|
||||
return Q;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/* 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace Concentus
|
||||
{
|
||||
public static class OpusConstants
|
||||
{
|
||||
/// <summary>
|
||||
/// Auto/default setting
|
||||
/// </summary>
|
||||
public const int OPUS_AUTO = -1000;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum bitrate
|
||||
/// </summary>
|
||||
public const int OPUS_BITRATE_MAX = -1;
|
||||
|
||||
// from analysis.c
|
||||
public const int NB_FRAMES = 8;
|
||||
public const int NB_TBANDS = 18;
|
||||
public const int NB_TOT_BANDS = 21;
|
||||
public const int NB_TONAL_SKIP_BANDS = 9;
|
||||
public const int ANALYSIS_BUF_SIZE = 720; /* 15 ms at 48 kHz */
|
||||
public const int DETECT_SIZE = 200;
|
||||
|
||||
public const int MAX_ENCODER_BUFFER = 480;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace Concentus
|
||||
{
|
||||
public class OpusException : Exception
|
||||
{
|
||||
public OpusException() : base() { }
|
||||
public OpusException(string message) : base(message) { }
|
||||
public OpusException(string message, int opus_error_code) : base(message + ": " + CodecHelpers.opus_strerror(opus_error_code)) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/* 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.Structs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace Concentus
|
||||
{
|
||||
internal static class OpusMultistream
|
||||
{
|
||||
internal 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;
|
||||
}
|
||||
|
||||
|
||||
internal 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;
|
||||
}
|
||||
|
||||
internal 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;
|
||||
}
|
||||
|
||||
internal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/* 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.Common.CPlusPlus;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace Concentus.Structs
|
||||
{
|
||||
internal class ChannelLayout
|
||||
{
|
||||
internal int nb_channels;
|
||||
internal int nb_streams;
|
||||
internal int nb_coupled_streams;
|
||||
internal readonly byte[] mapping = new byte[256];
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
nb_channels = 0;
|
||||
nb_streams = 0;
|
||||
nb_coupled_streams = 0;
|
||||
Arrays.MemSetByte(mapping, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/* Copyright (c) 2008-2011 Octasic Inc.
|
||||
Originally written by Jean-Marc Valin
|
||||
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.Common.CPlusPlus;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace Concentus.Structs
|
||||
{
|
||||
/// <summary>
|
||||
/// state object for multi-layer perceptron
|
||||
/// </summary>
|
||||
internal class MLP
|
||||
{
|
||||
internal int layers;
|
||||
internal int[] topo;
|
||||
internal float[] weights;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,933 @@
|
||||
/* 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.Enums;
|
||||
using Concentus.Silk;
|
||||
using Concentus.Silk.Structs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace Concentus.Structs
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public class OpusDecoder
|
||||
{
|
||||
internal int channels;
|
||||
internal int Fs; /** Sampling rate (at the API level) */
|
||||
internal readonly DecControlState DecControl = new DecControlState();
|
||||
internal int decode_gain;
|
||||
|
||||
/* Everything beyond this point gets cleared on a reset */
|
||||
internal int stream_channels;
|
||||
internal OpusBandwidth bandwidth;
|
||||
internal OpusMode mode;
|
||||
internal OpusMode prev_mode;
|
||||
internal int frame_size;
|
||||
internal int prev_redundancy;
|
||||
internal int last_packet_duration;
|
||||
internal uint rangeFinal;
|
||||
internal SilkDecoder SilkDecoder = new SilkDecoder();
|
||||
internal CeltDecoder Celt_Decoder = new CeltDecoder();
|
||||
|
||||
// Used by multistream decoder
|
||||
internal OpusDecoder() { }
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
channels = 0;
|
||||
Fs = 0; /* Sampling rate (at the API level) */
|
||||
DecControl.Reset();
|
||||
decode_gain = 0;
|
||||
PartialReset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OPUS_DECODER_RESET_START
|
||||
/// </summary>
|
||||
internal void PartialReset()
|
||||
{
|
||||
stream_channels = 0;
|
||||
bandwidth = 0;
|
||||
mode = 0;
|
||||
prev_mode = 0;
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deprecated. Just use the regular constructor
|
||||
/// </summary>
|
||||
public static OpusDecoder Create(int Fs, int channels)
|
||||
{
|
||||
return new OpusDecoder(Fs, channels);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="Fs">Sample rate to decode at (Hz). This must be one of 8000, 12000, 16000, 24000, or 48000.</param>
|
||||
/// <param name="channels">Number of channels (1 or 2) to decode</param>
|
||||
/// <returns>The created encoder</returns>
|
||||
public OpusDecoder(int Fs, int channels)
|
||||
{
|
||||
int ret;
|
||||
if ((Fs != 48000 && Fs != 24000 && Fs != 16000 && Fs != 12000 && Fs != 8000))
|
||||
{
|
||||
throw new ArgumentException("Sample rate is invalid (must be 8/12/16/24/48 Khz)");
|
||||
}
|
||||
if (channels != 1 && channels != 2)
|
||||
{
|
||||
throw new ArgumentException("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 ArgumentException("OPUS_BAD_ARG when creating decoder");
|
||||
throw new OpusException("Error while initializing decoder", ret);
|
||||
}
|
||||
}
|
||||
|
||||
/** Initializes a previously allocated decoder state.
|
||||
* The state must be at least the size returned by opus_decoder_get_size().
|
||||
* This is intended for applications which use their own allocator instead of malloc. @see opus_decoder_create,opus_decoder_get_size
|
||||
* To reset a previously initialized state, use the #OPUS_RESET_STATE CTL.
|
||||
* @param [in] st <tt>OpusDecoder*</tt>: Decoder state.
|
||||
* @param [in] Fs <tt>opus_int32</tt>: Sampling rate to decode to (Hz).
|
||||
* This must be one of 8000, 12000, 16000,
|
||||
* 24000, or 48000.
|
||||
* @param [in] channels <tt>int</tt>: Number of channels (1 or 2) to decode
|
||||
* @retval #OPUS_OK Success or @ref opus_errorcodes
|
||||
*/
|
||||
internal 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 = 0;
|
||||
this.frame_size = Fs / 400;
|
||||
return OpusError.OPUS_OK;
|
||||
}
|
||||
|
||||
private static readonly byte[] SILENCE = { 0xFF, 0xFF };
|
||||
|
||||
internal 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;
|
||||
uint 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, (uint)len);
|
||||
}
|
||||
else {
|
||||
audiosize = frame_size;
|
||||
mode = this.prev_mode;
|
||||
|
||||
if (mode == 0)
|
||||
{
|
||||
/* 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 > 0 && (
|
||||
(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;
|
||||
silk_ret = DecodeAPI.silk_Decode(silk_dec, this.DecControl,
|
||||
lost_flag, first_frame, dec, pcm_ptr2, pcm_ptr2_ptr, out silk_frame_size);
|
||||
if (silk_ret != 0)
|
||||
{
|
||||
if (lost_flag != 0)
|
||||
{
|
||||
/* PLC failure should not be fatal */
|
||||
silk_frame_size = frame_size;
|
||||
Arrays.MemSetWithOffset<short>(pcm_ptr2, 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 = (uint)(dec.storage - redundancy_bytes);
|
||||
}
|
||||
}
|
||||
if (mode != OpusMode.MODE_CELT_ONLY)
|
||||
start_band = 17;
|
||||
|
||||
{
|
||||
int endband = 21;
|
||||
|
||||
switch (this.bandwidth)
|
||||
{
|
||||
case OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND:
|
||||
endband = 13;
|
||||
break;
|
||||
case OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND:
|
||||
case OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND:
|
||||
endband = 17;
|
||||
break;
|
||||
case OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND:
|
||||
endband = 19;
|
||||
break;
|
||||
case OpusBandwidth.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 > 0 && 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) * (((int)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 = 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;
|
||||
}
|
||||
|
||||
internal 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, out int packet_offset, int soft_clip)
|
||||
{
|
||||
int i, nb_samples;
|
||||
int count, offset;
|
||||
byte toc;
|
||||
int packet_frame_size, packet_stream_channels;
|
||||
packet_offset = 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);
|
||||
|
||||
count = OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, self_delimited, out toc, null, null, 0,
|
||||
size, 0, out offset, out packet_offset);
|
||||
|
||||
if (count < 0)
|
||||
return count;
|
||||
|
||||
data_ptr += offset;
|
||||
|
||||
if (decode_fec != 0)
|
||||
{
|
||||
int dummy;
|
||||
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, out 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, out 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes an Opus packet.
|
||||
/// </summary>
|
||||
/// <param name="in_data">The input payload. This may be NULL if that previous packet was lost in transit (when PLC is enabled)</param>
|
||||
/// <param name="in_data_offset">The offset to use when reading the input payload. Usually 0</param>
|
||||
/// <param name="len">The number of bytes in the payload</param>
|
||||
/// <param name="out_pcm">A buffer to put the output PCM. 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. Otherwise, the minimum safe buffer size is 5760 samples</param>
|
||||
/// <param name="out_pcm_offset">The offset to use when writing to the output buffer</param>
|
||||
/// <param name="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 10 ms.</param>
|
||||
/// <param name="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.</param>
|
||||
/// <returns>The number of decoded samples</returns>
|
||||
public int Decode(byte[] in_data, int in_data_offset,
|
||||
int len, short[] out_pcm, int out_pcm_offset, int frame_size, bool decode_fec = false)
|
||||
{
|
||||
if (frame_size <= 0)
|
||||
{
|
||||
throw new ArgumentException("Frame size must be > 0");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
int dummy;
|
||||
int ret = opus_decode_native(in_data, in_data_offset, len, out_pcm, out_pcm_offset, frame_size, decode_fec ? 1 : 0, 0, out dummy, 0);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
// An error happened; report it
|
||||
if (ret == OpusError.OPUS_BAD_ARG)
|
||||
throw new ArgumentException("OPUS_BAD_ARG while decoding");
|
||||
throw new OpusException("An error occurred during decoding", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
catch (ArgumentException e)
|
||||
{
|
||||
throw new OpusException("Internal error during decoding: " + e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes an Opus packet, putting the output data into a floating-point buffer.
|
||||
/// </summary>
|
||||
/// <param name="in_data">The input payload. This may be NULL if that previous packet was lost in transit (when PLC is enabled)</param>
|
||||
/// <param name="in_data_offset">The offset to use when reading the input payload. Usually 0</param>
|
||||
/// <param name="len">The number of bytes in the payload</param>
|
||||
/// <param name="out_pcm">A buffer to put the output PCM. 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. Otherwise, the minimum safe buffer size is 5760 samples</param>
|
||||
/// <param name="out_pcm_offset">The offset to use when writing to the output buffer</param>
|
||||
/// <param name="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 10 ms.</param>
|
||||
/// <param name="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.</param>
|
||||
/// <returns>The number of decoded samples (per channel)</returns>
|
||||
public int Decode(byte[] in_data, int in_data_offset,
|
||||
int len, float[] out_pcm, int out_pcm_offset, int frame_size, bool decode_fec = false)
|
||||
{
|
||||
short[] output;
|
||||
int ret, i;
|
||||
int nb_samples;
|
||||
|
||||
if (frame_size <= 0)
|
||||
{
|
||||
throw new ArgumentException("Frame size must be > 0");
|
||||
}
|
||||
if (in_data != null && len > 0 && !decode_fec)
|
||||
{
|
||||
nb_samples = OpusPacketInfo.GetNumSamples(this, in_data, in_data_offset, len);
|
||||
if (nb_samples > 0)
|
||||
frame_size = Inlines.IMIN(frame_size, nb_samples);
|
||||
else
|
||||
throw new OpusException("An invalid packet was provided (unable to parse # of samples)");
|
||||
}
|
||||
output = new short[frame_size * this.channels];
|
||||
|
||||
try
|
||||
{
|
||||
int dummy;
|
||||
ret = opus_decode_native(in_data, in_data_offset, len, output, 0, frame_size, decode_fec ? 1 : 0, 0, out dummy, 0);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
// An error happened; report it
|
||||
if (ret == OpusError.OPUS_BAD_ARG)
|
||||
throw new ArgumentException("OPUS_BAD_ARG when decoding");
|
||||
throw new OpusException("An error occurred during decoding", ret);
|
||||
}
|
||||
|
||||
if (ret > 0)
|
||||
{
|
||||
for (i = 0; i < ret * this.channels; i++)
|
||||
out_pcm[out_pcm_offset + i] = (1.0f / 32768.0f) * (output[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
catch (ArgumentException e)
|
||||
{
|
||||
throw new OpusException("Internal error during decoding: " + e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the encoded bandwidth of the last packet decoded. This may be lower than the actual decoding sample rate,
|
||||
/// and is only an indicator of the encoded audio's quality
|
||||
/// </summary>
|
||||
public OpusBandwidth Bandwidth
|
||||
{
|
||||
get
|
||||
{
|
||||
return bandwidth;
|
||||
}
|
||||
}
|
||||
|
||||
public uint FinalRange
|
||||
{
|
||||
get
|
||||
{
|
||||
return rangeFinal;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sample rate that this decoder decodes to. Always constant for the lifetime of the decoder
|
||||
/// </summary>
|
||||
public int SampleRate
|
||||
{
|
||||
get
|
||||
{
|
||||
return Fs;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of channels that this decoder decodes to. Always constant for the lifetime of the decoder.
|
||||
/// </summary>
|
||||
public int NumChannels
|
||||
{
|
||||
get
|
||||
{
|
||||
return channels;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last estimated pitch value of the decoded audio
|
||||
/// </summary>
|
||||
public int Pitch
|
||||
{
|
||||
get
|
||||
{
|
||||
if (prev_mode == OpusMode.MODE_CELT_ONLY)
|
||||
{
|
||||
return Celt_Decoder.GetPitch();
|
||||
}
|
||||
else
|
||||
return DecControl.prevPitchLag;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the gain (Q8) to use in decoding
|
||||
/// </summary>
|
||||
public int Gain
|
||||
{
|
||||
get
|
||||
{
|
||||
return decode_gain;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value < -32768 || value > 32767)
|
||||
{
|
||||
throw new ArgumentException("Gain must be within the range of a signed int16");
|
||||
}
|
||||
|
||||
decode_gain = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the duration of the last packet, in PCM samples per channel
|
||||
/// </summary>
|
||||
public int LastPacketDuration
|
||||
{
|
||||
get
|
||||
{
|
||||
return last_packet_duration;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets all buffers and prepares this decoder to process a fresh (unrelated) stream
|
||||
/// </summary>
|
||||
public void ResetState()
|
||||
{
|
||||
PartialReset();
|
||||
Celt_Decoder.ResetState();
|
||||
DecodeAPI.silk_InitDecoder(SilkDecoder);
|
||||
stream_channels = channels;
|
||||
frame_size = Fs / 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,438 @@
|
||||
/* 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.Common;
|
||||
using Concentus.Common.CPlusPlus;
|
||||
using Concentus.Enums;
|
||||
using Concentus.Structs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace Concentus.Structs
|
||||
{
|
||||
public class OpusMSDecoder
|
||||
{
|
||||
internal ChannelLayout layout = new ChannelLayout();
|
||||
internal 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();
|
||||
}
|
||||
|
||||
#region API functions
|
||||
|
||||
internal int opus_multistream_decoder_init(
|
||||
int Fs,
|
||||
int channels,
|
||||
int streams,
|
||||
int coupled_streams,
|
||||
byte[] 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 ArgumentException("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 ArgumentException("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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new multichannel decoder
|
||||
/// </summary>
|
||||
/// <param name="Fs"></param>
|
||||
/// <param name="channels"></param>
|
||||
/// <param name="streams"></param>
|
||||
/// <param name="coupled_streams"></param>
|
||||
/// <param name="mapping">A mapping family (just use { 0, 1, 255 })</param>
|
||||
/// <returns></returns>
|
||||
public OpusMSDecoder(
|
||||
int Fs,
|
||||
int channels,
|
||||
int streams,
|
||||
int coupled_streams,
|
||||
byte[] mapping) : this(streams, coupled_streams)
|
||||
{
|
||||
int ret;
|
||||
if ((channels > 255) || (channels < 1) || (coupled_streams > streams) ||
|
||||
(streams < 1) || (coupled_streams < 0) || (streams > 255 - coupled_streams))
|
||||
{
|
||||
throw new ArgumentException("Invalid channel / stream configuration");
|
||||
}
|
||||
|
||||
ret = this.opus_multistream_decoder_init(Fs, channels, streams, coupled_streams, mapping);
|
||||
if (ret != OpusError.OPUS_OK)
|
||||
{
|
||||
if (ret == OpusError.OPUS_BAD_ARG)
|
||||
throw new ArgumentException("Bad argument while creating MS decoder");
|
||||
throw new OpusException("Could not create MS decoder", ret);
|
||||
}
|
||||
}
|
||||
|
||||
internal delegate void opus_copy_channel_out_func<T>(
|
||||
T[] dst,
|
||||
int dst_ptr,
|
||||
int dst_stride,
|
||||
int dst_channel,
|
||||
short[] src,
|
||||
int src_ptr,
|
||||
int src_stride,
|
||||
int frame_size
|
||||
);
|
||||
|
||||
internal static int opus_multistream_packet_validate(byte[] data, int data_ptr,
|
||||
int len, int nb_streams, int Fs)
|
||||
{
|
||||
int s;
|
||||
int count;
|
||||
byte toc;
|
||||
short[] size = new short[48];
|
||||
int samples = 0;
|
||||
int packet_offset;
|
||||
int dummy;
|
||||
|
||||
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, out toc, null, null, 0,
|
||||
size, 0, out dummy, out packet_offset);
|
||||
if (count < 0)
|
||||
return count;
|
||||
|
||||
tmp_samples = OpusPacketInfo.GetNumSamples(data, data_ptr, packet_offset, Fs);
|
||||
if (s != 0 && samples != tmp_samples)
|
||||
return OpusError.OPUS_INVALID_PACKET;
|
||||
samples = tmp_samples;
|
||||
data_ptr += packet_offset;
|
||||
len -= packet_offset;
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
internal int opus_multistream_decode_native<T>(
|
||||
byte[] data,
|
||||
int data_ptr,
|
||||
int len,
|
||||
T[] pcm,
|
||||
int pcm_ptr,
|
||||
opus_copy_channel_out_func<T> copy_channel_out,
|
||||
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.SampleRate;
|
||||
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;
|
||||
}
|
||||
int packet_offset;
|
||||
ret = dec.opus_decode_native(
|
||||
data, data_ptr, len, buf, 0, frame_size, decode_fec,
|
||||
(s != this.layout.nb_streams - 1) ? 1 : 0, out packet_offset, soft_clip);
|
||||
data_ptr += packet_offset;
|
||||
len -= packet_offset;
|
||||
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)
|
||||
{
|
||||
copy_channel_out(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)
|
||||
{
|
||||
copy_channel_out(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)
|
||||
{
|
||||
copy_channel_out(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)
|
||||
{
|
||||
copy_channel_out(pcm, pcm_ptr, this.layout.nb_channels, c,
|
||||
null, 0, 0, frame_size);
|
||||
}
|
||||
}
|
||||
|
||||
return frame_size;
|
||||
}
|
||||
|
||||
internal static void opus_copy_channel_out_float(
|
||||
float[] 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] = (1 / 32768.0f) * src[i * src_stride + src_ptr];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < frame_size; i++)
|
||||
dst[i * dst_stride + dst_channel + dst_ptr] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal 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<short>(data, data_offset, len,
|
||||
out_pcm, out_pcm_offset, opus_copy_channel_out_short, frame_size, decode_fec, 0);
|
||||
}
|
||||
|
||||
public int DecodeMultistream(byte[] data, int data_offset,
|
||||
int len, float[] out_pcm, int out_pcm_offset, int frame_size, int decode_fec)
|
||||
{
|
||||
return opus_multistream_decode_native<float>(data, data_offset, len,
|
||||
out_pcm, out_pcm_offset, opus_copy_channel_out_float, frame_size, decode_fec, 0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Getters and setters
|
||||
|
||||
public OpusBandwidth Bandwidth
|
||||
{
|
||||
get
|
||||
{
|
||||
if (decoders == null || decoders.Length == 0)
|
||||
throw new InvalidOperationException("Decoder not initialized");
|
||||
return decoders[0].Bandwidth;
|
||||
}
|
||||
}
|
||||
|
||||
public int SampleRate
|
||||
{
|
||||
get
|
||||
{
|
||||
if (decoders == null || decoders.Length == 0)
|
||||
throw new InvalidOperationException("Decoder not initialized");
|
||||
return decoders[0].SampleRate;
|
||||
}
|
||||
}
|
||||
|
||||
public int Gain
|
||||
{
|
||||
get
|
||||
{
|
||||
if (decoders == null || decoders.Length == 0)
|
||||
return OpusError.OPUS_INVALID_STATE;
|
||||
return decoders[0].Gain;
|
||||
}
|
||||
set
|
||||
{
|
||||
for (int s = 0; s < layout.nb_streams; s++)
|
||||
{
|
||||
decoders[s].Gain = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int LastPacketDuration
|
||||
{
|
||||
get
|
||||
{
|
||||
if (decoders == null || decoders.Length == 0)
|
||||
return OpusError.OPUS_INVALID_STATE;
|
||||
return decoders[0].LastPacketDuration;
|
||||
}
|
||||
}
|
||||
|
||||
public uint FinalRange
|
||||
{
|
||||
get
|
||||
{
|
||||
uint value = 0;
|
||||
for (int s = 0; s < layout.nb_streams; s++)
|
||||
{
|
||||
value ^= decoders[s].FinalRange;
|
||||
}
|
||||
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];
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,470 @@
|
||||
/* 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.Common;
|
||||
using Concentus.Common.CPlusPlus;
|
||||
using Concentus.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace Concentus.Structs
|
||||
{
|
||||
public class OpusPacketInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The Table of Contents byte for this packet. Contains info about modes, frame length, etc.
|
||||
/// </summary>
|
||||
public readonly byte TOCByte;
|
||||
|
||||
/// <summary>
|
||||
/// The list of subframes in this packet
|
||||
/// </summary>
|
||||
public readonly IList<byte[]> Frames;
|
||||
|
||||
/// <summary>
|
||||
/// The index of the start of the payload within the packet
|
||||
/// </summary>
|
||||
public readonly int PayloadOffset;
|
||||
|
||||
private OpusPacketInfo(byte toc, IList<byte[]> frames, int payloadOffset)
|
||||
{
|
||||
TOCByte = toc;
|
||||
Frames = frames;
|
||||
PayloadOffset = payloadOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="packet">The packet data to be parsed</param>
|
||||
/// <param name="packet_offset">The index of the beginning of the packet in the data array (usually 0)</param>
|
||||
/// <param name="len">The packet's length</param>
|
||||
/// <returns>A parsed packet info struct</returns>
|
||||
public static OpusPacketInfo ParseOpusPacket(byte[] packet, int packet_offset, int len)
|
||||
{
|
||||
// Find the number of frames first
|
||||
int numFrames = GetNumFrames(packet, packet_offset, len);
|
||||
|
||||
int payload_offset;
|
||||
byte out_toc;
|
||||
byte[][] frames = new byte[numFrames][];
|
||||
int[] frames_ptrs = new int[numFrames];
|
||||
short[] size = new short[numFrames];
|
||||
int packetOffset;
|
||||
int error = opus_packet_parse_impl(packet, packet_offset, len, 0, out out_toc, frames, frames_ptrs, 0, size, 0, out payload_offset, out packetOffset);
|
||||
if (error < 0)
|
||||
{
|
||||
throw new OpusException("An error occurred while parsing the packet", error);
|
||||
}
|
||||
|
||||
IList<byte[]> copiedFrames = new List<byte[]>();
|
||||
|
||||
for (int c = 0; c < numFrames; c++)
|
||||
{
|
||||
byte[] nextFrame = new byte[size[c]];
|
||||
Array.Copy(frames[c], frames_ptrs[c], nextFrame, 0, nextFrame.Length);
|
||||
copiedFrames.Add(nextFrame);
|
||||
}
|
||||
|
||||
return new OpusPacketInfo(out_toc, copiedFrames, payload_offset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of samples per frame from an Opus packet.
|
||||
/// </summary>
|
||||
/// <param name="packet">Opus packet. This must contain at least one byte of data</param>
|
||||
/// <param name="Fs">Sampling rate in Hz. This must be a multiple of 400, or inaccurate results will be returned.</param>
|
||||
/// <returns>Number of samples per frame</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the encoded bandwidth of an Opus packet. Note that you are not forced to decode at this bandwidth
|
||||
/// </summary>
|
||||
/// <param name="packet">An Opus packet (must be at least 1 byte)</param>.
|
||||
/// <returns>An OpusBandwidth value</returns>
|
||||
public static OpusBandwidth GetBandwidth(byte[] packet, int packet_offset)
|
||||
{
|
||||
OpusBandwidth bandwidth;
|
||||
if ((packet[packet_offset] & 0x80) != 0)
|
||||
{
|
||||
bandwidth = 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 = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND + ((packet[packet_offset] >> 5) & 0x3);
|
||||
}
|
||||
return bandwidth;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of encoded channels of an Opus packet. Note that you are not forced to decode with this channel count.
|
||||
/// </summary>
|
||||
/// <param name="packet">An opus packet (must be at least 1 byte)</param>
|
||||
/// <returns>The number of channels</returns>
|
||||
public static int GetNumEncodedChannels(byte[] packet, int packet_offset)
|
||||
{
|
||||
return ((packet[packet_offset] & 0x4) != 0) ? 2 : 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of frames in an Opus packet.
|
||||
/// </summary>
|
||||
/// <param name="packet">An Opus packet</param>
|
||||
/// <param name="len">The packet's length (must be at least 1)</param>
|
||||
/// <returns>The number of frames in the packet</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of samples of an Opus packet.
|
||||
/// </summary>
|
||||
/// <param name="packet">An Opus packet</param>
|
||||
/// <param name="len">The packet's length</param>
|
||||
/// <param name="Fs">The decoder's sampling rate in Hz. This must be a multiple of 400</param>
|
||||
/// <returns>The size of the PCM samples that this packet will be decoded to at the specified sample rate</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of samples of an Opus packet.
|
||||
/// </summary>
|
||||
/// <param name="dec">Your current decoder state</param>
|
||||
/// <param name="packet">An Opus packet</param>
|
||||
/// <param name="len">The packet's length</param>
|
||||
/// <returns>The size of the PCM samples that this packet will be decoded to by the specified decoder</returns>
|
||||
public static int GetNumSamples(OpusDecoder dec,
|
||||
byte[] packet, int packet_offset, int len)
|
||||
{
|
||||
return GetNumSamples(packet, packet_offset, len, dec.Fs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mode that was used to encode this packet.
|
||||
/// Normally there is nothing you can really do with this, other than debugging.
|
||||
/// </summary>
|
||||
/// <param name="packet">An Opus packet</param>
|
||||
/// <returns>The OpusMode used by the encoder</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
internal static int encode_size(int size, byte[] data, int data_ptr)
|
||||
{
|
||||
if (size < 252)
|
||||
{
|
||||
data[data_ptr] = (byte)size;
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
data[data_ptr] = (byte)(252 + (size & 0x3));
|
||||
data[data_ptr + 1] = (byte)((size - (int)data[data_ptr]) >> 2);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
internal static int parse_size(byte[] data, int data_ptr, int len, BoxedValueShort size)
|
||||
{
|
||||
if (len < 1)
|
||||
{
|
||||
size.Val = -1;
|
||||
return -1;
|
||||
}
|
||||
else if (data[data_ptr] < 252)
|
||||
{
|
||||
size.Val = data[data_ptr];
|
||||
return 1;
|
||||
}
|
||||
else if (len < 2)
|
||||
{
|
||||
size.Val = -1;
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
size.Val = (short)(4 * data[data_ptr + 1] + data[data_ptr]);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
internal static int opus_packet_parse_impl(byte[] data, int data_ptr, int len,
|
||||
int self_delimited, out byte out_toc,
|
||||
byte[][] frames, int[] frames_ptrs, int frames_ptr, short[] sizes, int sizes_ptr,
|
||||
out int payload_offset, out int packet_offset)
|
||||
{
|
||||
int i, bytes;
|
||||
int count;
|
||||
int cbr;
|
||||
byte ch, toc;
|
||||
int framesize;
|
||||
int last_size;
|
||||
int pad = 0;
|
||||
int data0 = data_ptr;
|
||||
out_toc = 0;
|
||||
payload_offset = 0;
|
||||
packet_offset = 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 = 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 = 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 = (int)(data_ptr - data0);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (frames != null)
|
||||
frames[frames_ptr + i] = data;
|
||||
if (frames_ptrs != null)
|
||||
frames_ptrs[frames_ptr + i] = data_ptr;
|
||||
data_ptr += sizes[sizes_ptr + i];
|
||||
}
|
||||
|
||||
packet_offset = pad + (int)(data_ptr - data0);
|
||||
|
||||
out_toc = toc;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// used internally
|
||||
//internal static int opus_packet_parse(byte[] data, int data_ptr, int len,
|
||||
// out byte out_toc, Pointer<Pointer<byte>> frames,
|
||||
// short[] size, int size_ptr, out int payload_offset)
|
||||
//{
|
||||
// int dummy;
|
||||
// return OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, 0, out out_toc,
|
||||
// frames, size, size_ptr, out payload_offset, out dummy);
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,573 @@
|
||||
/* 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.Common;
|
||||
using Concentus.Common.CPlusPlus;
|
||||
using Concentus.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace Concentus.Structs
|
||||
{
|
||||
public class OpusRepacketizer
|
||||
{
|
||||
internal byte toc = 0;
|
||||
internal int nb_frames = 0;
|
||||
internal readonly byte[][] frames = new byte[48][];
|
||||
internal readonly int[] frames_ptrs = new int[48];
|
||||
internal readonly short[] len = new short[48];
|
||||
internal 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 <tt>OpusRepacketizer*</tt>: The repacketizer state to
|
||||
* (re)initialize.
|
||||
*/
|
||||
public void Reset()
|
||||
{
|
||||
this.nb_frames = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new repacketizer
|
||||
/// </summary>
|
||||
public OpusRepacketizer()
|
||||
{
|
||||
this.Reset();
|
||||
}
|
||||
|
||||
internal int opus_repacketizer_cat_impl(byte[] data, int data_ptr, int len, int self_delimited)
|
||||
{
|
||||
byte dummy_toc;
|
||||
int dummy_offset;
|
||||
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, out dummy_toc, this.frames, this.frames_ptrs, this.nb_frames, this.len, this.nb_frames, out dummy_offset, out 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 rp <tt>OpusRepacketizer*</tt>: The repacketizer state to which to
|
||||
* add the packet.
|
||||
* @param[in] data <tt>const unsigned char*</tt>: 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 <tt>opus_int32</tt>: 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 <tt>OpusRepacketizer*</tt>: 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;
|
||||
}
|
||||
|
||||
internal 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] |= 0x40;
|
||||
nb_255s = (pad_amount - 1) / 255;
|
||||
for (i = 0; i < nb_255s; i++)
|
||||
{
|
||||
data[ptr++] = 255;
|
||||
}
|
||||
|
||||
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 (this.frames[i] == data)
|
||||
{
|
||||
/* 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<byte>(data, frames_ptrs[i], ptr, this.len[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(this.frames[i], frames_ptrs[i], data, ptr, this.len[i]);
|
||||
}
|
||||
ptr += this.len[i];
|
||||
}
|
||||
|
||||
if (pad != 0)
|
||||
{
|
||||
/* Fill padding with zeros. */
|
||||
//Arrays.MemSetWithOffset<byte>(ptr.Data, 0, ptr.Offset, data.Offset + maxlen - ptr.Offset);
|
||||
// FIXME why did they not just use a MemSet(0) here?
|
||||
while (ptr < data_ptr + maxlen)
|
||||
{
|
||||
data[ptr++] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return tot_size;
|
||||
}
|
||||
|
||||
/** Construct a new packet from data previously submitted to the repacketizer
|
||||
* state via opus_repacketizer_cat().
|
||||
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state from which to
|
||||
* construct the new packet.
|
||||
* @param begin <tt>int</tt>: The index of the first frame in the current
|
||||
* repacketizer state to include in the output.
|
||||
* @param end <tt>int</tt>: One past the index of the last frame in the
|
||||
* current repacketizer state to include in the
|
||||
* output.
|
||||
* @param[out] data <tt>const unsigned char*</tt>: The buffer in which to
|
||||
* store the output packet.
|
||||
* @param maxlen <tt>opus_int32</tt>: The maximum number of bytes to store in
|
||||
* the output buffer. In order to guarantee
|
||||
* success, this should be at least
|
||||
* <code>1276</code> for a single frame,
|
||||
* or for multiple frames,
|
||||
* <code>1277*(end-begin)</code>.
|
||||
* However, <code>1*(end-begin)</code> 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 <code>[begin,end)</code> 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 <tt>OpusRepacketizer*</tt>: The repacketizer state from which to
|
||||
* construct the new packet.
|
||||
* @param[out] data <tt>const unsigned char*</tt>: The buffer in which to
|
||||
* store the output packet.
|
||||
* @param maxlen <tt>opus_int32</tt>: The maximum number of bytes to store in
|
||||
* the output buffer. In order to guarantee
|
||||
* success, this should be at least
|
||||
* <code>1277*opus_repacketizer_get_nb_frames(rp)</code>.
|
||||
* However,
|
||||
* <code>1*opus_repacketizer_get_nb_frames(rp)</code>
|
||||
* 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 <tt>const unsigned char*</tt>: The buffer containing the
|
||||
* packet to pad.
|
||||
* @param len <tt>opus_int32</tt>: The size of the packet.
|
||||
* This must be at least 1.
|
||||
* @param new_len <tt>opus_int32</tt>: 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<byte>(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 <tt>const unsigned char*</tt>: The buffer containing the
|
||||
* packet to strip.
|
||||
* @param len <tt>opus_int32</tt>: 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 <tt>const unsigned char*</tt>: The buffer containing the
|
||||
* packet to pad.
|
||||
* @param len <tt>opus_int32</tt>: The size of the packet.
|
||||
* This must be at least 1.
|
||||
* @param new_len <tt>opus_int32</tt>: The desired size of the packet after padding.
|
||||
* This must be at least 1.
|
||||
* @param nb_streams <tt>opus_int32</tt>: 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;
|
||||
byte dummy_toc;
|
||||
short[] size = new short[48];
|
||||
int packet_offset;
|
||||
int dummy_offset;
|
||||
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, out dummy_toc, null, null, 0,
|
||||
size, 0, out dummy_offset, out packet_offset);
|
||||
if (count < 0)
|
||||
return count;
|
||||
data_offset += packet_offset;
|
||||
len -= packet_offset;
|
||||
}
|
||||
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 <tt>const unsigned char*</tt>: The buffer containing the
|
||||
* packet to strip.
|
||||
* @param len <tt>opus_int32</tt>: The size of the packet.
|
||||
* This must be at least 1.
|
||||
* @param nb_streams <tt>opus_int32</tt>: 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;
|
||||
byte dummy_toc;
|
||||
short[] size = new short[48];
|
||||
int packet_offset;
|
||||
int dummy_offset;
|
||||
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, out dummy_toc, null, null, 0,
|
||||
size, 0, out dummy_offset, out packet_offset);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = rp.opus_repacketizer_cat_impl(data, data_offset, packet_offset, 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;
|
||||
len -= packet_offset;
|
||||
}
|
||||
return dst_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/* 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace Concentus.Structs
|
||||
{
|
||||
internal class StereoWidthState
|
||||
{
|
||||
internal int XX;
|
||||
internal int XY;
|
||||
internal int YY;
|
||||
internal int smoothed_width;
|
||||
internal int max_follower;
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
XX = 0;
|
||||
XY = 0;
|
||||
YY = 0;
|
||||
smoothed_width = 0;
|
||||
max_follower = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/* 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.Structs;
|
||||
using Concentus.Common;
|
||||
using Concentus.Common.CPlusPlus;
|
||||
using Concentus;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace Concentus.Structs
|
||||
{
|
||||
internal class TonalityAnalysisState
|
||||
{
|
||||
internal bool enabled = false;
|
||||
internal readonly float[] angle = new float[240];
|
||||
internal readonly float[] d_angle = new float[240];
|
||||
internal readonly float[] d2_angle = new float[240];
|
||||
internal readonly int[] inmem = new int[OpusConstants.ANALYSIS_BUF_SIZE];
|
||||
internal int mem_fill; /* number of usable samples in the buffer */
|
||||
internal readonly float[] prev_band_tonality = new float[OpusConstants.NB_TBANDS];
|
||||
internal float prev_tonality;
|
||||
internal readonly float[][] E = Arrays.InitTwoDimensionalArray<float>(OpusConstants.NB_FRAMES, OpusConstants.NB_TBANDS);
|
||||
internal readonly float[] lowE = new float[OpusConstants.NB_TBANDS];
|
||||
internal readonly float[] highE = new float[OpusConstants.NB_TBANDS];
|
||||
internal readonly float[] meanE = new float[OpusConstants.NB_TOT_BANDS];
|
||||
internal readonly float[] mem = new float[32];
|
||||
internal readonly float[] cmean = new float[8];
|
||||
internal readonly float[] std = new float[9];
|
||||
internal float music_prob;
|
||||
internal float Etracker;
|
||||
internal float lowECount;
|
||||
internal int E_count;
|
||||
internal int last_music;
|
||||
internal int last_transition;
|
||||
internal int count;
|
||||
internal readonly float[] subframe_mem = new float[3];
|
||||
internal 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. */
|
||||
internal readonly 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. */
|
||||
internal readonly float[] pmusic = new float[OpusConstants.DETECT_SIZE];
|
||||
internal float speech_confidence;
|
||||
internal float music_confidence;
|
||||
internal int speech_confidence_count;
|
||||
internal int music_confidence_count;
|
||||
internal int write_pos;
|
||||
internal int read_pos;
|
||||
internal int read_subframe;
|
||||
internal readonly AnalysisInfo[] info = new AnalysisInfo[OpusConstants.DETECT_SIZE];
|
||||
|
||||
internal TonalityAnalysisState()
|
||||
{
|
||||
for (int c = 0; c < OpusConstants.DETECT_SIZE; c++)
|
||||
{
|
||||
info[c] = new AnalysisInfo();
|
||||
}
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
Arrays.MemSetFloat(angle,0, 240);
|
||||
Arrays.MemSetFloat(d_angle,0, 240);
|
||||
Arrays.MemSetFloat(d2_angle,0, 240);
|
||||
Arrays.MemSetInt(inmem, 0, OpusConstants.ANALYSIS_BUF_SIZE);
|
||||
mem_fill = 0;
|
||||
Arrays.MemSetFloat(prev_band_tonality,0, OpusConstants.NB_TBANDS);
|
||||
prev_tonality = 0;
|
||||
for (int c = 0; c < OpusConstants.NB_FRAMES; c++)
|
||||
{
|
||||
Arrays.MemSetFloat(E[c], 0, OpusConstants.NB_TBANDS);
|
||||
}
|
||||
Arrays.MemSetFloat(lowE,0, OpusConstants.NB_TBANDS);
|
||||
Arrays.MemSetFloat(highE,0, OpusConstants.NB_TBANDS);
|
||||
Arrays.MemSetFloat(meanE,0, OpusConstants.NB_TOT_BANDS);
|
||||
Arrays.MemSetFloat(mem,0, 32);
|
||||
Arrays.MemSetFloat(cmean,0, 8);
|
||||
Arrays.MemSetFloat(std,0, 9);
|
||||
music_prob = 0;
|
||||
Etracker = 0;
|
||||
lowECount = 0;
|
||||
E_count = 0;
|
||||
last_music = 0;
|
||||
last_transition = 0;
|
||||
count = 0;
|
||||
Arrays.MemSetFloat(subframe_mem,0, 3);
|
||||
analysis_offset = 0;
|
||||
Arrays.MemSetFloat(pspeech,0, OpusConstants.DETECT_SIZE);
|
||||
Arrays.MemSetFloat(pmusic,0, 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/* 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace Concentus.Structs
|
||||
{
|
||||
internal class VorbisLayout
|
||||
{
|
||||
internal VorbisLayout(int streams, int coupled_streams, byte[] map)
|
||||
{
|
||||
nb_streams = streams;
|
||||
nb_coupled_streams = coupled_streams;
|
||||
mapping = map;
|
||||
}
|
||||
|
||||
internal int nb_streams;
|
||||
internal int nb_coupled_streams;
|
||||
internal byte[] mapping;
|
||||
|
||||
/* Index is nb_channel-1*/
|
||||
internal static readonly VorbisLayout[] vorbis_mappings = {
|
||||
new VorbisLayout(1, 0, new byte[] {0}), /* 1: mono */
|
||||
new VorbisLayout(1, 1, new byte[] {0, 1}), /* 2: stereo */
|
||||
new VorbisLayout(2, 1, new byte[] {0, 2, 1}), /* 3: 1-d surround */
|
||||
new VorbisLayout(2, 2, new byte[] {0, 1, 2, 3}), /* 4: quadraphonic surround */
|
||||
new VorbisLayout(3, 2, new byte[] {0, 4, 1, 2, 3}), /* 5: 5-channel surround */
|
||||
new VorbisLayout(4, 2, new byte[] {0, 4, 1, 2, 3, 5}), /* 6: 5.1 surround */
|
||||
new VorbisLayout(4, 3, new byte[] {0, 4, 1, 2, 3, 5, 6}), /* 7: 6.1 surround */
|
||||
new VorbisLayout(5, 3, new byte[] {0, 6, 1, 2, 3, 4, 5, 7}), /* 8: 7.1 surround */
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
/* 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.Common.CPlusPlus;
|
||||
using Concentus.Structs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace Concentus
|
||||
{
|
||||
internal static class Tables
|
||||
{
|
||||
internal static readonly 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,
|
||||
};
|
||||
|
||||
internal static readonly 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,
|
||||
};
|
||||
|
||||
internal static readonly int[] tbands/*[NB_TBANDS + 1]*/ = {
|
||||
2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 68, 80, 96, 120
|
||||
};
|
||||
|
||||
internal static readonly 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 const 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 */
|
||||
|
||||
internal static readonly 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};
|
||||
|
||||
internal static readonly int[] topo = { 25, 15, 2 };
|
||||
|
||||
// fixme: move this into an MLP class singleton or something?
|
||||
internal static readonly MLP net = new MLP()
|
||||
{
|
||||
layers = 3,
|
||||
topo = topo,
|
||||
weights = weights
|
||||
};
|
||||
|
||||
internal static readonly 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) */
|
||||
internal static readonly int[] mono_voice_bandwidth_thresholds = {
|
||||
11000, 1000, /* NB<->MB */
|
||||
14000, 1000, /* MB<->WB */
|
||||
17000, 1000, /* WB<->SWB */
|
||||
21000, 2000, /* SWB<->FB */
|
||||
};
|
||||
internal static readonly int[] mono_music_bandwidth_thresholds = {
|
||||
12000, 1000, /* NB<->MB */
|
||||
15000, 1000, /* MB<->WB */
|
||||
18000, 2000, /* WB<->SWB */
|
||||
22000, 2000, /* SWB<->FB */
|
||||
};
|
||||
internal static readonly int[] stereo_voice_bandwidth_thresholds = {
|
||||
11000, 1000, /* NB<->MB */
|
||||
14000, 1000, /* MB<->WB */
|
||||
21000, 2000, /* WB<->SWB */
|
||||
28000, 2000, /* SWB<->FB */
|
||||
};
|
||||
internal static readonly 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 const int stereo_voice_threshold = 30000;
|
||||
public const int stereo_music_threshold = 30000;
|
||||
|
||||
/* Threshold bit-rate for switching between SILK/hybrid and CELT-only */
|
||||
internal static readonly int[][] mode_thresholds = {
|
||||
/* voice */ /* music */
|
||||
new int[]{ 64000, 16000}, /* mono */
|
||||
new int[]{ 36000, 16000}, /* stereo */
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user