Files
LuaCsForBarotraumaEP/Libraries/Concentus/CSharp/Concentus/Celt/Structs/CeltEncoder.cs

1301 lines
55 KiB
C#

/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2011 Xiph.Org Foundation
Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
Timothy B. Terriberry, and the Opus open-source contributors
Ported to C# by Logan Stromberg
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Concentus.Celt.Structs
{
using Concentus.Celt.Enums;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Enums;
using System;
internal class CeltEncoder
{
#region Encoder state
internal CeltMode mode = null; /*< Mode used by the encoder. Without custom modes, this always refers to the same predefined struct */
internal int channels = 0;
internal int stream_channels = 0;
internal int force_intra = 0;
internal int clip = 0;
internal int disable_pf = 0;
internal int complexity = 0;
internal int upsample = 0;
internal int start = 0;
internal int end = 0;
internal int bitrate = 0;
internal int vbr = 0;
internal int signalling = 0;
/* If zero, VBR can do whatever it likes with the rate */
internal int constrained_vbr = 0;
internal int loss_rate = 0;
internal int lsb_depth = 0;
internal OpusFramesize variable_duration = 0;
internal int lfe = 0;
/* Everything beyond this point gets cleared on a reset */
internal uint rng = 0;
internal int spread_decision = 0;
internal int delayedIntra = 0;
internal int tonal_average = 0;
internal int lastCodedBands = 0;
internal int hf_average = 0;
internal int tapset_decision = 0;
internal int prefilter_period = 0;
internal int prefilter_gain = 0;
internal int prefilter_tapset = 0;
internal int consec_transient = 0;
internal AnalysisInfo analysis = new AnalysisInfo();
internal readonly int[] preemph_memE = new int[2];
internal readonly int[] preemph_memD = new int[2];
/* VBR-related parameters */
internal int vbr_reservoir = 0;
internal int vbr_drift = 0;
internal int vbr_offset = 0;
internal int vbr_count = 0;
internal int overlap_max = 0;
internal int stereo_saving = 0;
internal int intensity = 0;
internal int[] energy_mask = null;
internal int spec_avg = 0;
/// <summary>
/// The original C++ defined in_mem as a single float[1] which was the "caboose"
/// to the overall encoder struct, containing 5 separate variable-sized buffer
/// spaces of heterogeneous datatypes. I have laid them out into separate variables here,
/// but these were the original definitions:
/// val32 in_mem[], Size = channels*mode.overlap
/// val32 prefilter_mem[], Size = channels*COMBFILTER_MAXPERIOD
/// val16 oldBandE[], Size = channels*mode.nbEBands
/// val16 oldLogE[], Size = channels*mode.nbEBands
/// val16 oldLogE2[], Size = channels*mode.nbEBands
/// </summary>
internal int[][] in_mem = null;
internal int[][] prefilter_mem = null;
internal int[][] oldBandE = null;
internal int[][] oldLogE = null;
internal int[][] oldLogE2 = null;
private void Reset()
{
mode = null;
channels = 0;
stream_channels = 0;
force_intra = 0;
clip = 0;
disable_pf = 0;
complexity = 0;
upsample = 0;
start = 0;
end = 0;
bitrate = 0;
vbr = 0;
signalling = 0;
constrained_vbr = 0;
loss_rate = 0;
lsb_depth = 0;
variable_duration = 0;
lfe = 0;
PartialReset();
}
private void PartialReset()
{
rng = 0;
spread_decision = 0;
delayedIntra = 0;
tonal_average = 0;
lastCodedBands = 0;
hf_average = 0;
tapset_decision = 0;
prefilter_period = 0;
prefilter_gain = 0;
prefilter_tapset = 0;
consec_transient = 0;
analysis.Reset();
preemph_memE[0] = 0;
preemph_memE[1] = 0;
preemph_memD[0] = 0;
preemph_memD[1] = 0;
vbr_reservoir = 0;
vbr_drift = 0;
vbr_offset = 0;
vbr_count = 0;
overlap_max = 0;
stereo_saving = 0;
intensity = 0;
energy_mask = null;
spec_avg = 0;
in_mem = null;
prefilter_mem = null;
oldBandE = null;
oldLogE = null;
oldLogE2 = null;
}
#endregion
#region API functions
internal void ResetState()
{
int i;
this.PartialReset();
// We have to reconstitute the dynamic buffers here.
this.in_mem = Arrays.InitTwoDimensionalArray<int>(this.channels, this.mode.overlap);
this.prefilter_mem = Arrays.InitTwoDimensionalArray<int>(this.channels, CeltConstants.COMBFILTER_MAXPERIOD);
this.oldBandE = Arrays.InitTwoDimensionalArray<int>(this.channels, this.mode.nbEBands);
this.oldLogE = Arrays.InitTwoDimensionalArray<int>(this.channels, this.mode.nbEBands);
this.oldLogE2 = Arrays.InitTwoDimensionalArray<int>(this.channels, this.mode.nbEBands);
for (i = 0; i < this.mode.nbEBands; i++)
{
this.oldLogE[0][i] = this.oldLogE2[0][i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/;
}
if (this.channels == 2)
{
for (i = 0; i < this.mode.nbEBands; i++)
{
this.oldLogE[1][i] = this.oldLogE2[1][i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/;
}
}
this.vbr_offset = 0;
this.delayedIntra = 1;
this.spread_decision = Spread.SPREAD_NORMAL;
this.tonal_average = 256;
this.hf_average = 0;
this.tapset_decision = 0;
}
internal int opus_custom_encoder_init_arch(CeltMode mode,
int channels)
{
if (channels < 0 || channels > 2)
return OpusError.OPUS_BAD_ARG;
if (this == null || mode == null)
return OpusError.OPUS_ALLOC_FAIL;
this.Reset();
this.mode = mode;
this.stream_channels = this.channels = channels;
this.upsample = 1;
this.start = 0;
this.end = this.mode.effEBands;
this.signalling = 1;
this.constrained_vbr = 1;
this.clip = 1;
this.bitrate = OpusConstants.OPUS_BITRATE_MAX;
this.vbr = 0;
this.force_intra = 0;
this.complexity = 5;
this.lsb_depth = 24;
//this.in_mem = new int[channels * mode.overlap);
//this.prefilter_mem = new int[channels * CeltConstants.COMBFILTER_MAXPERIOD);
//this.oldBandE = new int[channels * mode.nbEBands);
//this.oldLogE = new int[channels * mode.nbEBands);
//this.oldLogE2 = new int[channels * mode.nbEBands);
this.ResetState();
return OpusError.OPUS_OK;
}
internal int celt_encoder_init(int sampling_rate, int channels)
{
int ret;
ret = this.opus_custom_encoder_init_arch(CeltMode.mode48000_960_120, channels);
if (ret != OpusError.OPUS_OK)
return ret;
this.upsample = CeltCommon.resampling_factor(sampling_rate);
return OpusError.OPUS_OK;
}
internal int run_prefilter(int[][] input, int[][] prefilter_mem, int CC, int N,
int prefilter_tapset, out int pitch, out int gain, out int qgain, int enabled, int nbAvailableBytes)
{
int c;
int[][] pre = new int[CC][];
CeltMode mode; // [porting note] pointer
int pitch_index;
int gain1;
int pf_threshold;
int pf_on;
int qg;
int overlap;
mode = this.mode;
overlap = mode.overlap;
for (int z = 0; z < CC; z++)
{
pre[z] = new int[N + CeltConstants.COMBFILTER_MAXPERIOD];
}
c = 0;
do
{
Array.Copy(prefilter_mem[c], 0, pre[c], 0, CeltConstants.COMBFILTER_MAXPERIOD);
Array.Copy(input[c], overlap, pre[c], CeltConstants.COMBFILTER_MAXPERIOD, N);
} while (++c < CC);
if (enabled != 0)
{
int[] pitch_buf = new int[(CeltConstants.COMBFILTER_MAXPERIOD + N) >> 1];
Concentus.Celt.Pitch.pitch_downsample(pre, pitch_buf, CeltConstants.COMBFILTER_MAXPERIOD + N, CC);
/* Don't search for the fir last 1.5 octave of the range because
there's too many false-positives due to short-term correlation */
Concentus.Celt.Pitch.pitch_search(pitch_buf, CeltConstants.COMBFILTER_MAXPERIOD >> 1, pitch_buf, N,
CeltConstants.COMBFILTER_MAXPERIOD - 3 * CeltConstants.COMBFILTER_MINPERIOD, out pitch_index);
pitch_index = CeltConstants.COMBFILTER_MAXPERIOD - pitch_index;
gain1 = Concentus.Celt.Pitch.remove_doubling(pitch_buf, CeltConstants.COMBFILTER_MAXPERIOD, CeltConstants.COMBFILTER_MINPERIOD,
N, ref pitch_index, this.prefilter_period, this.prefilter_gain);
if (pitch_index > CeltConstants.COMBFILTER_MAXPERIOD - 2)
pitch_index = CeltConstants.COMBFILTER_MAXPERIOD - 2;
gain1 = Inlines.MULT16_16_Q15(((short)(0.5 + (.7f) * (((int)1) << (15))))/*Inlines.QCONST16(.7f, 15)*/, gain1);
/*printf("%d %d %f %f\n", pitch_change, pitch_index, gain1, st.analysis.tonality);*/
if (this.loss_rate > 2)
gain1 = Inlines.HALF32(gain1);
if (this.loss_rate > 4)
gain1 = Inlines.HALF32(gain1);
if (this.loss_rate > 8)
gain1 = 0;
}
else {
gain1 = 0;
pitch_index = CeltConstants.COMBFILTER_MINPERIOD;
}
/* Gain threshold for enabling the prefilter/postfilter */
pf_threshold = ((short)(0.5 + (.2f) * (((int)1) << (15))))/*Inlines.QCONST16(.2f, 15)*/;
/* Adjusting the threshold based on rate and continuity */
if (Inlines.abs(pitch_index - this.prefilter_period) * 10 > pitch_index)
pf_threshold += ((short)(0.5 + (.2f) * (((int)1) << (15))))/*Inlines.QCONST16(.2f, 15)*/;
if (nbAvailableBytes < 25)
pf_threshold += ((short)(0.5 + (.1f) * (((int)1) << (15))))/*Inlines.QCONST16(.1f, 15)*/;
if (nbAvailableBytes < 35)
pf_threshold += ((short)(0.5 + (.1f) * (((int)1) << (15))))/*Inlines.QCONST16(.1f, 15)*/;
if (this.prefilter_gain > ((short)(0.5 + (.4f) * (((int)1) << (15))))/*Inlines.QCONST16(.4f, 15)*/)
pf_threshold -= ((short)(0.5 + (.1f) * (((int)1) << (15))))/*Inlines.QCONST16(.1f, 15)*/;
if (this.prefilter_gain > ((short)(0.5 + (.55f) * (((int)1) << (15))))/*Inlines.QCONST16(.55f, 15)*/)
pf_threshold -= ((short)(0.5 + (.1f) * (((int)1) << (15))))/*Inlines.QCONST16(.1f, 15)*/;
/* Hard threshold at 0.2 */
pf_threshold = Inlines.MAX16(pf_threshold, ((short)(0.5 + (.2f) * (((int)1) << (15))))/*Inlines.QCONST16(.2f, 15)*/);
if (gain1 < pf_threshold)
{
gain1 = 0;
pf_on = 0;
qg = 0;
}
else
{
/*This block is not gated by a total bits check only because
of the nbAvailableBytes check above.*/
if (Inlines.ABS32(gain1 - this.prefilter_gain) < ((short)(0.5 + (.1f) * (((int)1) << (15))))/*Inlines.QCONST16(.1f, 15)*/)
gain1 = this.prefilter_gain;
qg = ((gain1 + 1536) >> 10) / 3 - 1;
qg = Inlines.IMAX(0, Inlines.IMIN(7, qg));
gain1 = ((short)(0.5 + (0.09375f) * (((int)1) << (15))))/*Inlines.QCONST16(0.09375f, 15)*/ * (qg + 1);
pf_on = 1;
}
/*printf("%d %f\n", pitch_index, gain1);*/
c = 0;
do
{
int offset = mode.shortMdctSize - overlap;
this.prefilter_period = Inlines.IMAX(this.prefilter_period, CeltConstants.COMBFILTER_MINPERIOD);
Array.Copy(this.in_mem[c], 0, input[c], 0, overlap);
if (offset != 0)
{
CeltCommon.comb_filter(input[c], (overlap), pre[c], (CeltConstants.COMBFILTER_MAXPERIOD),
this.prefilter_period, this.prefilter_period, offset, -this.prefilter_gain, -this.prefilter_gain,
this.prefilter_tapset, this.prefilter_tapset, null, 0); // opt: lots of pointer allocations here
}
CeltCommon.comb_filter(input[c], (overlap + offset), pre[c], (CeltConstants.COMBFILTER_MAXPERIOD + offset),
this.prefilter_period, pitch_index, N - offset, -this.prefilter_gain, -gain1,
this.prefilter_tapset, prefilter_tapset, mode.window, overlap);
Array.Copy(input[c], N, this.in_mem[c], 0, overlap);
if (N > CeltConstants.COMBFILTER_MAXPERIOD)
{
Array.Copy(pre[c], N, prefilter_mem[c], 0, CeltConstants.COMBFILTER_MAXPERIOD);
}
else
{
Arrays.MemMove(prefilter_mem[c], N, 0, CeltConstants.COMBFILTER_MAXPERIOD - N);
Array.Copy(pre[c], CeltConstants.COMBFILTER_MAXPERIOD, prefilter_mem[c], CeltConstants.COMBFILTER_MAXPERIOD - N, N);
}
} while (++c < CC);
gain = gain1;
pitch = pitch_index;
qgain = qg;
return pf_on;
}
internal int celt_encode_with_ec(short[] pcm, int pcm_ptr, int frame_size, byte[] compressed, int compressed_ptr, int nbCompressedBytes, EntropyCoder enc)
{
int i, c, N;
int bits;
int[][] input;
int[][] freq;
int[][] X;
int[][] bandE;
int[][] bandLogE;
int[][] bandLogE2;
int[] fine_quant;
int[][] error;
int[] pulses;
int[] cap;
int[] offsets;
int[] fine_priority;
int[] tf_res;
byte[] collapse_masks;
int shortBlocks = 0;
int isTransient = 0;
int CC = this.channels;
int C = this.stream_channels;
int LM, M;
int tf_select;
int nbFilledBytes, nbAvailableBytes;
int start;
int end;
int effEnd;
int codedBands;
int tf_sum;
int alloc_trim;
int pitch_index = CeltConstants.COMBFILTER_MINPERIOD;
int gain1 = 0;
int dual_stereo = 0;
int effectiveBytes;
int dynalloc_logp;
int vbr_rate;
int total_bits;
int total_boost;
int balance;
int tell;
int prefilter_tapset = 0;
int pf_on;
int anti_collapse_rsv;
int anti_collapse_on = 0;
int silence = 0;
int tf_chan = 0;
int tf_estimate;
int pitch_change = 0;
int tot_boost;
int sample_max;
int maxDepth;
CeltMode mode;
int nbEBands;
int overlap;
short[] eBands;
int secondMdct;
int signalBandwidth;
int transient_got_disabled = 0;
int surround_masking = 0;
int temporal_vbr = 0;
int surround_trim = 0;
int equiv_rate = 510000;
int[] surround_dynalloc;
mode = this.mode;
nbEBands = mode.nbEBands;
overlap = mode.overlap;
eBands = mode.eBands;
start = this.start;
end = this.end;
tf_estimate = 0;
if (nbCompressedBytes < 2 || pcm == null)
{
return OpusError.OPUS_BAD_ARG;
}
frame_size *= this.upsample;
for (LM = 0; LM <= mode.maxLM; LM++)
if (mode.shortMdctSize << LM == frame_size)
break;
if (LM > mode.maxLM)
{
return OpusError.OPUS_BAD_ARG;
}
M = 1 << LM;
N = M * mode.shortMdctSize;
if (enc == null)
{
tell = 1;
nbFilledBytes = 0;
}
else {
tell = enc.tell();
nbFilledBytes = (tell + 4) >> 3;
}
Inlines.OpusAssert(this.signalling == 0);
/* Can't produce more than 1275 output bytes */
nbCompressedBytes = Inlines.IMIN(nbCompressedBytes, 1275);
nbAvailableBytes = nbCompressedBytes - nbFilledBytes;
if (this.vbr != 0 && this.bitrate != OpusConstants.OPUS_BITRATE_MAX)
{
int den = mode.Fs >> EntropyCoder.BITRES;
vbr_rate = (this.bitrate * frame_size + (den >> 1)) / den;
effectiveBytes = vbr_rate >> (3 + EntropyCoder.BITRES);
}
else {
int tmp;
vbr_rate = 0;
tmp = this.bitrate * frame_size;
if (tell > 1)
tmp += tell;
if (this.bitrate != OpusConstants.OPUS_BITRATE_MAX)
nbCompressedBytes = Inlines.IMAX(2, Inlines.IMIN(nbCompressedBytes,
(tmp + 4 * mode.Fs) / (8 * mode.Fs) - (this.signalling != 0 ? 1 : 0)));
effectiveBytes = nbCompressedBytes;
}
if (this.bitrate != OpusConstants.OPUS_BITRATE_MAX)
equiv_rate = this.bitrate - (40 * C + 20) * ((400 >> LM) - 50);
if (enc == null)
{
enc = new EntropyCoder();
enc.enc_init(compressed, compressed_ptr, (uint)nbCompressedBytes);
}
if (vbr_rate > 0)
{
/* Computes the max bit-rate allowed in VBR mode to avoid violating the
target rate and buffering.
We must do this up front so that bust-prevention logic triggers
correctly if we don't have enough bits. */
if (this.constrained_vbr != 0)
{
int vbr_bound;
int max_allowed;
/* We could use any multiple of vbr_rate as bound (depending on the
delay).
This is clamped to ensure we use at least two bytes if the encoder
was entirely empty, but to allow 0 in hybrid mode. */
vbr_bound = vbr_rate;
max_allowed = Inlines.IMIN(Inlines.IMAX(tell == 1 ? 2 : 0,
(vbr_rate + vbr_bound - this.vbr_reservoir) >> (EntropyCoder.BITRES + 3)),
nbAvailableBytes);
if (max_allowed < nbAvailableBytes)
{
nbCompressedBytes = nbFilledBytes + max_allowed;
nbAvailableBytes = max_allowed;
enc.enc_shrink((uint)nbCompressedBytes);
}
}
}
total_bits = nbCompressedBytes * 8;
effEnd = end;
if (effEnd > mode.effEBands)
effEnd = mode.effEBands;
input = Arrays.InitTwoDimensionalArray<int>(CC, N + overlap);
sample_max = Inlines.MAX32(this.overlap_max, Inlines.celt_maxabs32(pcm, pcm_ptr, C * (N - overlap) / this.upsample));
this.overlap_max = Inlines.celt_maxabs32(pcm, pcm_ptr + (C * (N - overlap) / this.upsample), C * overlap / this.upsample);
sample_max = Inlines.MAX32(sample_max, this.overlap_max);
silence = (sample_max == 0) ? 1 : 0;
#if FUZZING
if ((new Random().Next() & 0x3F) == 0)
silence = 1;
#endif
if (tell == 1)
enc.enc_bit_logp(silence, 15);
else
silence = 0;
if (silence != 0)
{
/*In VBR mode there is no need to send more than the minimum. */
if (vbr_rate > 0)
{
effectiveBytes = nbCompressedBytes = Inlines.IMIN(nbCompressedBytes, nbFilledBytes + 2);
total_bits = nbCompressedBytes * 8;
nbAvailableBytes = 2;
enc.enc_shrink((uint)nbCompressedBytes);
}
/* Pretend we've filled all the remaining bits with zeros
(that's what the initialiser did anyway) */
tell = nbCompressedBytes * 8;
enc.nbits_total += tell - enc.tell();
}
c = 0;
do
{
int need_clip = 0;
CeltCommon.celt_preemphasis(pcm, pcm_ptr + c, input[c], overlap, N, CC, this.upsample,
mode.preemph, ref this.preemph_memE[c], need_clip);
} while (++c < CC);
/* Find pitch period and gain */
{
int enabled;
int qg;
enabled = (((this.lfe != 0 && nbAvailableBytes > 3) || nbAvailableBytes > 12 * C) && start == 0 && silence == 0 && this.disable_pf == 0
&& this.complexity >= 5 && !(this.consec_transient != 0 && LM != 3 && this.variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE)) ? 1 : 0;
prefilter_tapset = this.tapset_decision;
pf_on = this.run_prefilter(input, this.prefilter_mem, CC, N, prefilter_tapset, out pitch_index, out gain1, out qg, enabled, nbAvailableBytes);
if ((gain1 > ((short)(0.5 + (.4f) * (((int)1) << (15))))/*Inlines.QCONST16(.4f, 15)*/ || this.prefilter_gain > ((short)(0.5 + (.4f) * (((int)1) << (15))))/*Inlines.QCONST16(.4f, 15)*/) && (this.analysis.valid == 0 || this.analysis.tonality > .3)
&& (pitch_index > 1.26 * this.prefilter_period || pitch_index < .79 * this.prefilter_period))
pitch_change = 1;
if (pf_on == 0)
{
if (start == 0 && tell + 16 <= total_bits)
enc.enc_bit_logp(0, 1);
}
else {
/*This block is not gated by a total bits check only because
of the nbAvailableBytes check above.*/
int octave;
enc.enc_bit_logp(1, 1);
pitch_index += 1;
octave = Inlines.EC_ILOG((uint)pitch_index) - 5;
enc.enc_uint((uint)octave, 6);
enc.enc_bits((uint)(pitch_index - (16 << octave)), (uint)(4 + octave));
pitch_index -= 1;
enc.enc_bits((uint)qg, 3);
enc.enc_icdf(prefilter_tapset, Tables.tapset_icdf, 2);
}
}
isTransient = 0;
shortBlocks = 0;
if (this.complexity >= 1 && this.lfe == 0)
{
isTransient = CeltCommon.transient_analysis(input, N + overlap, CC,
out tf_estimate, out tf_chan);
}
if (LM > 0 && enc.tell() + 3 <= total_bits)
{
if (isTransient != 0)
shortBlocks = M;
}
else {
isTransient = 0;
transient_got_disabled = 1;
}
freq = Arrays.InitTwoDimensionalArray<int>(CC, N); /*< Interleaved signal MDCTs */
bandE = Arrays.InitTwoDimensionalArray<int>(CC, nbEBands);
bandLogE = Arrays.InitTwoDimensionalArray<int>(CC, nbEBands);
secondMdct = (shortBlocks != 0 && this.complexity >= 8) ? 1 : 0;
bandLogE2 = Arrays.InitTwoDimensionalArray<int>(CC, nbEBands);
//Arrays.MemSetInt(bandLogE2, 0, C * nbEBands); // not explicitly needed
if (secondMdct != 0)
{
CeltCommon.compute_mdcts(mode, 0, input, freq, C, CC, LM, this.upsample);
Bands.compute_band_energies(mode, freq, bandE, effEnd, C, LM);
QuantizeBands.amp2Log2(mode, effEnd, end, bandE, bandLogE2, C);
for (i = 0; i < nbEBands; i++)
{
bandLogE2[0][i] += Inlines.HALF16(Inlines.SHL16(LM, CeltConstants.DB_SHIFT));
}
if (C == 2)
{
for (i = 0; i < nbEBands; i++)
{
bandLogE2[1][i] += Inlines.HALF16(Inlines.SHL16(LM, CeltConstants.DB_SHIFT));
}
}
}
CeltCommon.compute_mdcts(mode, shortBlocks, input, freq, C, CC, LM, this.upsample);
if (CC == 2 && C == 1)
tf_chan = 0;
Bands.compute_band_energies(mode, freq, bandE, effEnd, C, LM);
if (this.lfe != 0)
{
for (i = 2; i < end; i++)
{
bandE[0][i] = Inlines.IMIN(bandE[0][i], Inlines.MULT16_32_Q15(((short)(0.5 + (1e-4f) * (((int)1) << (15))))/*Inlines.QCONST16(1e-4f, 15)*/, bandE[0][0]));
bandE[0][i] = Inlines.MAX32(bandE[0][i], CeltConstants.EPSILON);
}
}
QuantizeBands.amp2Log2(mode, effEnd, end, bandE, bandLogE, C);
surround_dynalloc = new int[C * nbEBands];
//Arrays.MemSetInt(surround_dynalloc, 0, end); // not strictly needed
/* This computes how much masking takes place between surround channels */
if (start == 0 && this.energy_mask != null && this.lfe == 0)
{
int mask_end;
int midband;
int count_dynalloc;
int mask_avg = 0;
int diff = 0;
int count = 0;
mask_end = Inlines.IMAX(2, this.lastCodedBands);
for (c = 0; c < C; c++)
{
for (i = 0; i < mask_end; i++)
{
int mask;
mask = Inlines.MAX16(Inlines.MIN16(this.energy_mask[nbEBands * c + i],
((short)(0.5 + (.25f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.25f, CeltConstants.DB_SHIFT)*/), -((short)(0.5 + (2.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(2.0f, CeltConstants.DB_SHIFT)*/);
if (mask > 0)
mask = Inlines.HALF16(mask);
mask_avg += Inlines.MULT16_16(mask, eBands[i + 1] - eBands[i]);
count += eBands[i + 1] - eBands[i];
diff += Inlines.MULT16_16(mask, 1 + 2 * i - mask_end);
}
}
Inlines.OpusAssert(count > 0);
mask_avg = Inlines.DIV32_16(mask_avg, count);
mask_avg += ((short)(0.5 + (.2f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.2f, CeltConstants.DB_SHIFT)*/;
diff = diff * 6 / (C * (mask_end - 1) * (mask_end + 1) * mask_end);
/* Again, being conservative */
diff = Inlines.HALF32(diff);
diff = Inlines.MAX32(Inlines.MIN32(diff, ((int)(0.5 + (.031f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST32(.031f, CeltConstants.DB_SHIFT)*/), 0 - ((int)(0.5 + (.031f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST32(.031f, CeltConstants.DB_SHIFT)*/);
/* Find the band that's in the middle of the coded spectrum */
for (midband = 0; eBands[midband + 1] < eBands[mask_end] / 2; midband++) ;
count_dynalloc = 0;
for (i = 0; i < mask_end; i++)
{
int lin;
int unmask;
lin = mask_avg + diff * (i - midband);
if (C == 2)
unmask = Inlines.MAX16(this.energy_mask[i], this.energy_mask[nbEBands + i]);
else
unmask = this.energy_mask[i];
unmask = Inlines.MIN16(unmask, ((short)(0.5 + (.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.0f, CeltConstants.DB_SHIFT)*/);
unmask -= lin;
if (unmask > ((short)(0.5 + (.25f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.25f, CeltConstants.DB_SHIFT)*/)
{
surround_dynalloc[i] = unmask - ((short)(0.5 + (.25f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.25f, CeltConstants.DB_SHIFT)*/;
count_dynalloc++;
}
}
if (count_dynalloc >= 3)
{
/* If we need dynalloc in many bands, it's probably because our
initial masking rate was too low. */
mask_avg += ((short)(0.5 + (.25f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.25f, CeltConstants.DB_SHIFT)*/;
if (mask_avg > 0)
{
/* Something went really wrong in the original calculations,
disabling masking. */
mask_avg = 0;
diff = 0;
Arrays.MemSetInt(surround_dynalloc, 0, mask_end);
}
else {
for (i = 0; i < mask_end; i++)
surround_dynalloc[i] = Inlines.MAX16(0, surround_dynalloc[i] - ((short)(0.5 + (.25f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.25f, CeltConstants.DB_SHIFT)*/);
}
}
mask_avg += ((short)(0.5 + (.2f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(.2f, CeltConstants.DB_SHIFT)*/;
/* Convert to 1/64th units used for the trim */
surround_trim = 64 * diff;
/*printf("%d %d ", mask_avg, surround_trim);*/
surround_masking = mask_avg;
}
/* Temporal VBR (but not for LFE) */
if (this.lfe == 0)
{
int follow = -((short)(0.5 + (10.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(10.0f, CeltConstants.DB_SHIFT)*/;
int frame_avg = 0;
int offset = shortBlocks != 0 ? Inlines.HALF16(Inlines.SHL16(LM, CeltConstants.DB_SHIFT)) : 0;
for (i = start; i < end; i++)
{
follow = Inlines.MAX16(follow - ((short)(0.5 + (1.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/, bandLogE[0][i] - offset);
if (C == 2)
follow = Inlines.MAX16(follow, bandLogE[1][i] - offset);
frame_avg += follow;
}
frame_avg /= (end - start);
temporal_vbr = Inlines.SUB16(frame_avg, this.spec_avg);
temporal_vbr = Inlines.MIN16(((short)(0.5 + (3.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(3.0f, CeltConstants.DB_SHIFT)*/, Inlines.MAX16(-((short)(0.5 + (1.5f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(1.5f, CeltConstants.DB_SHIFT)*/, temporal_vbr));
this.spec_avg += (short)(Inlines.MULT16_16_Q15(((short)(0.5 + (.02f) * (((int)1) << (15))))/*Inlines.QCONST16(.02f, 15)*/, temporal_vbr));
}
/*for (i=0;i<21;i++)
printf("%f ", bandLogE[i]);
printf("\n");*/
if (secondMdct == 0)
{
Array.Copy(bandLogE[0], bandLogE2[0], nbEBands);
if (C == 2)
Array.Copy(bandLogE[1], bandLogE2[1], nbEBands);
}
/* Last chance to catch any transient we might have missed in the
time-domain analysis */
if (LM > 0 && enc.tell() + 3 <= total_bits && isTransient == 0 && this.complexity >= 5 && this.lfe == 0)
{
if (CeltCommon.patch_transient_decision(bandLogE, this.oldBandE, nbEBands, start, end, C) != 0)
{
isTransient = 1;
shortBlocks = M;
CeltCommon.compute_mdcts(mode, shortBlocks, input, freq, C, CC, LM, this.upsample);
Bands.compute_band_energies(mode, freq, bandE, effEnd, C, LM);
QuantizeBands.amp2Log2(mode, effEnd, end, bandE, bandLogE, C);
/* Compensate for the scaling of short vs long mdcts */
for (i = 0; i < nbEBands; i++)
bandLogE2[0][i] += Inlines.HALF16(Inlines.SHL16(LM, CeltConstants.DB_SHIFT));
if (C == 2)
{
for (i = 0; i < nbEBands; i++)
bandLogE2[1][i] += Inlines.HALF16(Inlines.SHL16(LM, CeltConstants.DB_SHIFT));
}
tf_estimate = ((short)(0.5 + (.2f) * (((int)1) << (14))))/*Inlines.QCONST16(.2f, 14)*/;
}
}
if (LM > 0 && enc.tell() + 3 <= total_bits)
enc.enc_bit_logp(isTransient, 3);
X = Arrays.InitTwoDimensionalArray<int>(C, N); /*< Interleaved normalised MDCTs */
/* Band normalisation */
Bands.normalise_bands(mode, freq, X, bandE, effEnd, C, M);
tf_res = new int[nbEBands];
/* Disable variable tf resolution for hybrid and at very low bitrate */
if (effectiveBytes >= 15 * C && start == 0 && this.complexity >= 2 && this.lfe == 0)
{
int lambda;
if (effectiveBytes < 40)
lambda = 12;
else if (effectiveBytes < 60)
lambda = 6;
else if (effectiveBytes < 100)
lambda = 4;
else
lambda = 3;
lambda *= 2;
tf_select = CeltCommon.tf_analysis(mode, effEnd, isTransient, tf_res, lambda, X, N, LM, out tf_sum, tf_estimate, tf_chan);
for (i = effEnd; i < end; i++)
tf_res[i] = tf_res[effEnd - 1];
}
else {
tf_sum = 0;
for (i = 0; i < end; i++)
tf_res[i] = isTransient;
tf_select = 0;
}
error = Arrays.InitTwoDimensionalArray<int>(C, nbEBands);
QuantizeBands.quant_coarse_energy(mode, start, end, effEnd, bandLogE,
this.oldBandE, (uint)total_bits, error, enc,
C, LM, nbAvailableBytes, this.force_intra,
ref this.delayedIntra, this.complexity >= 4 ? 1 : 0, this.loss_rate, this.lfe);
CeltCommon.tf_encode(start, end, isTransient, tf_res, LM, tf_select, enc);
if (enc.tell() + 4 <= total_bits)
{
if (this.lfe != 0)
{
this.tapset_decision = 0;
this.spread_decision = Spread.SPREAD_NORMAL;
}
else if (shortBlocks != 0 || this.complexity < 3 || nbAvailableBytes < 10 * C || start != 0)
{
if (this.complexity == 0)
this.spread_decision = Spread.SPREAD_NONE;
else
this.spread_decision = Spread.SPREAD_NORMAL;
}
else
{
this.spread_decision = Bands.spreading_decision(mode, X,
ref this.tonal_average, this.spread_decision, ref this.hf_average,
ref this.tapset_decision, (pf_on != 0 && shortBlocks == 0) ? 1 : 0, effEnd, C, M);
/*printf("%d %d\n", st.tapset_decision, st.spread_decision);*/
/*printf("%f %d %f %d\n\n", st.analysis.tonality, st.spread_decision, st.analysis.tonality_slope, st.tapset_decision);*/
}
enc.enc_icdf(this.spread_decision, Tables.spread_icdf, 5);
}
offsets = new int[nbEBands];
maxDepth = CeltCommon.dynalloc_analysis(bandLogE, bandLogE2, nbEBands, start, end, C, offsets,
this.lsb_depth, mode.logN, isTransient, this.vbr, this.constrained_vbr,
eBands, LM, effectiveBytes, out tot_boost, this.lfe, surround_dynalloc);
/* For LFE, everything interesting is in the first band */
if (this.lfe != 0)
offsets[0] = Inlines.IMIN(8, effectiveBytes / 3);
cap = new int[nbEBands];
CeltCommon.init_caps(mode, cap, LM, C);
dynalloc_logp = 6;
total_bits <<= EntropyCoder.BITRES;
total_boost = 0;
tell = (int)enc.tell_frac();
for (i = start; i < end; i++)
{
int width, quanta;
int dynalloc_loop_logp;
int boost;
int j;
width = C * (eBands[i + 1] - eBands[i]) << LM;
/* quanta is 6 bits, but no more than 1 bit/sample
and no less than 1/8 bit/sample */
quanta = Inlines.IMIN(width << EntropyCoder.BITRES, Inlines.IMAX(6 << EntropyCoder.BITRES, width));
dynalloc_loop_logp = dynalloc_logp;
boost = 0;
for (j = 0; tell + (dynalloc_loop_logp << EntropyCoder.BITRES) < total_bits - total_boost
&& boost < cap[i]; j++)
{
int flag;
flag = j < offsets[i] ? 1 : 0;
enc.enc_bit_logp(flag, (uint)dynalloc_loop_logp);
tell = (int)enc.tell_frac();
if (flag == 0)
break;
boost += quanta;
total_boost += quanta;
dynalloc_loop_logp = 1;
}
/* Making dynalloc more likely */
if (j != 0)
dynalloc_logp = Inlines.IMAX(2, dynalloc_logp - 1);
offsets[i] = boost;
}
if (C == 2)
{
/* Always use MS for 2.5 ms frames until we can do a better analysis */
if (LM != 0)
dual_stereo = CeltCommon.stereo_analysis(mode, X, LM);
this.intensity = Bands.hysteresis_decision((int)(equiv_rate / 1000),
Tables.intensity_thresholds, Tables.intensity_histeresis, 21, this.intensity);
this.intensity = Inlines.IMIN(end, Inlines.IMAX(start, this.intensity));
}
alloc_trim = 5;
if (tell + (6 << EntropyCoder.BITRES) <= total_bits - total_boost)
{
if (this.lfe != 0)
{
alloc_trim = 5;
}
else
{
alloc_trim = CeltCommon.alloc_trim_analysis(mode, X, bandLogE,
end, LM, C, this.analysis, ref this.stereo_saving, tf_estimate,
this.intensity, surround_trim);
}
enc.enc_icdf(alloc_trim, Tables.trim_icdf, 7);
tell = (int)enc.tell_frac();
}
/* Variable bitrate */
if (vbr_rate > 0)
{
int alpha;
int delta;
/* The target rate in 8th bits per frame */
int target, base_target;
int min_allowed;
int lm_diff = mode.maxLM - LM;
/* Don't attempt to use more than 510 kb/s, even for frames smaller than 20 ms.
The CELT allocator will just not be able to use more than that anyway. */
nbCompressedBytes = Inlines.IMIN(nbCompressedBytes, 1275 >> (3 - LM));
base_target = vbr_rate - ((40 * C + 20) << EntropyCoder.BITRES);
if (this.constrained_vbr != 0)
base_target += (this.vbr_offset >> lm_diff);
target = CeltCommon.compute_vbr(mode, this.analysis, base_target, LM, equiv_rate,
this.lastCodedBands, C, this.intensity, this.constrained_vbr,
this.stereo_saving, tot_boost, tf_estimate, pitch_change, maxDepth,
this.variable_duration, this.lfe, this.energy_mask != null ? 1 : 0, surround_masking,
temporal_vbr);
/* The current offset is removed from the target and the space used
so far is added*/
target = target + tell;
/* In VBR mode the frame size must not be reduced so much that it would
result in the encoder running out of bits.
The margin of 2 bytes ensures that none of the bust-prevention logic
in the decoder will have triggered so far. */
min_allowed = ((tell + total_boost + (1 << (EntropyCoder.BITRES + 3)) - 1) >> (EntropyCoder.BITRES + 3)) + 2 - nbFilledBytes;
nbAvailableBytes = (target + (1 << (EntropyCoder.BITRES + 2))) >> (EntropyCoder.BITRES + 3);
nbAvailableBytes = Inlines.IMAX(min_allowed, nbAvailableBytes);
nbAvailableBytes = Inlines.IMIN(nbCompressedBytes, nbAvailableBytes + nbFilledBytes) - nbFilledBytes;
/* By how much did we "miss" the target on that frame */
delta = target - vbr_rate;
target = nbAvailableBytes << (EntropyCoder.BITRES + 3);
/*If the frame is silent we don't adjust our drift, otherwise
the encoder will shoot to very high rates after hitting a
span of silence, but we do allow the EntropyCoder.BITRES to refill.
This means that we'll undershoot our target in CVBR/VBR modes
on files with lots of silence. */
if (silence != 0)
{
nbAvailableBytes = 2;
target = 2 * 8 << EntropyCoder.BITRES;
delta = 0;
}
if (this.vbr_count < 970)
{
this.vbr_count++;
alpha = Inlines.celt_rcp(Inlines.SHL32((this.vbr_count + 20), 16));
}
else
alpha = ((short)(0.5 + (.001f) * (((int)1) << (15))))/*Inlines.QCONST16(.001f, 15)*/;
/* How many bits have we used in excess of what we're allowed */
if (this.constrained_vbr != 0)
this.vbr_reservoir += target - vbr_rate;
/*printf ("%d\n", st.vbr_reservoir);*/
/* Compute the offset we need to apply in order to reach the target */
if (this.constrained_vbr != 0)
{
this.vbr_drift += (int)Inlines.MULT16_32_Q15(alpha, (delta * (1 << lm_diff)) - this.vbr_offset - this.vbr_drift);
this.vbr_offset = -this.vbr_drift;
}
/*printf ("%d\n", st.vbr_drift);*/
if (this.constrained_vbr != 0 && this.vbr_reservoir < 0)
{
/* We're under the min value -- increase rate */
int adjust = (-this.vbr_reservoir) / (8 << EntropyCoder.BITRES);
/* Unless we're just coding silence */
nbAvailableBytes += silence != 0 ? 0 : adjust;
this.vbr_reservoir = 0;
/*printf ("+%d\n", adjust);*/
}
nbCompressedBytes = Inlines.IMIN(nbCompressedBytes, nbAvailableBytes + nbFilledBytes);
/*printf("%d\n", nbCompressedBytes*50*8);*/
/* This moves the raw bits to take into account the new compressed size */
enc.enc_shrink((uint)nbCompressedBytes);
}
/* Bit allocation */
fine_quant = new int[nbEBands];
pulses = new int[nbEBands];
fine_priority = new int[nbEBands];
/* bits = packet size - where we are - safety*/
bits = (((int)nbCompressedBytes * 8) << EntropyCoder.BITRES) - (int)enc.tell_frac() - 1;
anti_collapse_rsv = isTransient != 0 && LM >= 2 && bits >= ((LM + 2) << EntropyCoder.BITRES) ? (1 << EntropyCoder.BITRES) : 0;
bits -= anti_collapse_rsv;
signalBandwidth = end - 1;
if (this.analysis.valid != 0)
{
int min_bandwidth;
if (equiv_rate < (int)32000 * C)
min_bandwidth = 13;
else if (equiv_rate < (int)48000 * C)
min_bandwidth = 16;
else if (equiv_rate < (int)60000 * C)
min_bandwidth = 18;
else if (equiv_rate < (int)80000 * C)
min_bandwidth = 19;
else
min_bandwidth = 20;
signalBandwidth = Inlines.IMAX(this.analysis.bandwidth, min_bandwidth);
}
if (this.lfe != 0)
{
signalBandwidth = 1;
}
codedBands = Rate.compute_allocation(mode, start, end, offsets, cap,
alloc_trim, ref this.intensity, ref dual_stereo, bits, out balance, pulses,
fine_quant, fine_priority, C, LM, enc, 1, this.lastCodedBands, signalBandwidth);
if (this.lastCodedBands != 0)
this.lastCodedBands = Inlines.IMIN(this.lastCodedBands + 1, Inlines.IMAX(this.lastCodedBands - 1, codedBands));
else
this.lastCodedBands = codedBands;
QuantizeBands.quant_fine_energy(mode, start, end, this.oldBandE, error, fine_quant, enc, C);
/* Residual quantisation */
collapse_masks = new byte[C * nbEBands];
Bands.quant_all_bands(1, mode, start, end, X[0], C == 2 ? X[1] : null, collapse_masks,
bandE, pulses, shortBlocks, this.spread_decision,
dual_stereo, this.intensity, tf_res, nbCompressedBytes * (8 << EntropyCoder.BITRES) - anti_collapse_rsv,
balance, enc, LM, codedBands, ref this.rng);
if (anti_collapse_rsv > 0)
{
anti_collapse_on = (this.consec_transient < 2) ? 1 : 0;
#if FUZZING
anti_collapse_on = new Random().Next() & 0x1;
#endif
enc.enc_bits((uint)anti_collapse_on, 1);
}
QuantizeBands.quant_energy_finalise(mode, start, end, this.oldBandE, error, fine_quant, fine_priority, nbCompressedBytes * 8 - (int)enc.tell(), enc, C);
if (silence != 0)
{
for (i = 0; i < nbEBands; i++)
{
this.oldBandE[0][i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/;
}
if (C == 2)
{
for (i = 0; i < nbEBands; i++)
{
this.oldBandE[1][i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/;
}
}
}
this.prefilter_period = pitch_index;
this.prefilter_gain = gain1;
this.prefilter_tapset = prefilter_tapset;
if (CC == 2 && C == 1)
{
Array.Copy(oldBandE[0], 0, oldBandE[1], 0, nbEBands);
}
if (isTransient == 0)
{
Array.Copy(oldLogE[0], 0, oldLogE2[0], 0, nbEBands);
Array.Copy(oldBandE[0], 0, oldLogE[0], 0, nbEBands);
if (CC == 2)
{
Array.Copy(oldLogE[1], 0, oldLogE2[1], 0, nbEBands);
Array.Copy(oldBandE[1], 0, oldLogE[1], 0, nbEBands);
}
}
else
{
for (i = 0; i < nbEBands; i++)
{
oldLogE[0][i] = Inlines.MIN16(oldLogE[0][i], oldBandE[0][i]);
}
if (CC == 2)
{
for (i = 0; i < nbEBands; i++)
{
oldLogE[1][i] = Inlines.MIN16(oldLogE[1][i], oldBandE[1][i]);
}
}
}
/* In case start or end were to change */
c = 0;
do
{
for (i = 0; i < start; i++)
{
oldBandE[c][i] = 0;
oldLogE[c][i] = oldLogE2[c][i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/;
}
for (i = end; i < nbEBands; i++)
{
oldBandE[c][i] = 0;
oldLogE[c][i] = oldLogE2[c][i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT))))/*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/;
}
} while (++c < CC);
if (isTransient != 0 || transient_got_disabled != 0)
this.consec_transient++;
else
this.consec_transient = 0;
this.rng = enc.rng;
/* If there's any room left (can only happen for very high rates),
it's already filled with zeros */
enc.enc_done();
if (enc.get_error() != 0)
return OpusError.OPUS_INTERNAL_ERROR;
else
return nbCompressedBytes;
}
#endregion
#region Getters and Setters
internal void SetComplexity(int value)
{
if (value < 0 || value > 10)
throw new ArgumentException("Complexity must be between 0 and 10 inclusive");
this.complexity = value;
}
internal void SetStartBand(int value)
{
if (value < 0 || value >= this.mode.nbEBands)
throw new ArgumentException("Start band above max number of ebands (or negative)");
this.start = value;
}
internal void SetEndBand(int value)
{
if (value < 1 || value > this.mode.nbEBands)
throw new ArgumentException("End band above max number of ebands (or less than 1)");
this.end = value;
}
internal void SetPacketLossPercent(int value)
{
if (value < 0 || value > 100)
throw new ArgumentException("Packet loss must be between 0 and 100");
this.loss_rate = value;
}
internal void SetPrediction(int value)
{
if (value < 0 || value > 2)
throw new ArgumentException("CELT prediction mode must be 0, 1, or 2");
this.disable_pf = (value <= 1) ? 1 : 0;
this.force_intra = (value == 0) ? 1 : 0;
}
internal void SetVBRConstraint(bool value)
{
this.constrained_vbr = value ? 1 : 0;
}
internal void SetVBR(bool value)
{
this.vbr = value ? 1 : 0;
}
internal void SetBitrate(int value)
{
if (value <= 500 && value != OpusConstants.OPUS_BITRATE_MAX)
throw new ArgumentException("Bitrate out of range");
value = Inlines.IMIN(value, 260000 * this.channels);
this.bitrate = value;
}
internal void SetChannels(int value)
{
if (value < 1 || value > 2)
throw new ArgumentException("Channel count must be 1 or 2");
this.stream_channels = value;
}
internal void SetLSBDepth(int value)
{
if (value < 8 || value > 24)
throw new ArgumentException("Bit depth must be between 8 and 24");
this.lsb_depth = value;
}
internal int GetLSBDepth()
{
return this.lsb_depth;
}
internal void SetExpertFrameDuration(OpusFramesize value)
{
this.variable_duration = value;
}
internal void SetSignalling(int value)
{
this.signalling = value;
}
internal void SetAnalysis(AnalysisInfo value)
{
if (value == null)
throw new ArgumentNullException("AnalysisInfo");
this.analysis.Assign(value);
}
internal CeltMode GetMode()
{
return this.mode;
}
internal uint GetFinalRange()
{
return this.rng;
}
internal void SetLFE(int value)
{
this.lfe = value;
}
internal void SetEnergyMask(int[] value)
{
this.energy_mask = value;
}
#endregion
}
}