(caf7e6a2e) Replaced Concentus NuGet package with csproj (ensures correct System.Runtime references)

This commit is contained in:
Joonas Rikkonen
2019-04-29 21:11:59 +03:00
parent f10adc2612
commit 0b1b39d70a
149 changed files with 47171 additions and 70 deletions
@@ -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 &lt; 0, begin &gt;= end, or end &gt;
* 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 */
};
}
}