use crate::silk::apply_sine_window::apply_sine_window;
use crate::silk::autocorr::autocorr;
use crate::silk::bwexpander_32::bwexpander_32;
use crate::silk::encoder::control::EncoderControl;
use crate::silk::encoder::state::{EncoderChannelState, SHAPE_LPC_WIN_MAX};
use crate::silk::k2a_q16::k2a_q16;
use crate::silk::lin2log::lin2log;
use crate::silk::log2lin::log2lin;
use crate::silk::lpc_fit::lpc_fit;
use crate::silk::lpc_inv_pred_gain::inverse32_varq;
use crate::silk::schur64::schur64;
use crate::silk::sum_sqr_shift::sum_sqr_shift;
use crate::silk::tuning_parameters::{
BANDWIDTH_EXPANSION, BG_SNR_DECR_DB, ENERGY_VARIATION_THRESHOLD_QNT_OFFSET,
FIND_PITCH_WHITE_NOISE_FRACTION, HARM_HP_NOISE_COEF, HARM_SNR_INCR_DB, HARMONIC_SHAPING,
HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING, HP_NOISE_COEF, LOW_FREQ_SHAPING,
LOW_QUALITY_LOW_FREQ_SHAPING_DECR, SHAPE_WHITE_NOISE_FRACTION, SUBFR_SMTH_COEF,
};
use crate::silk::warped_autocorrelation::{MAX_SHAPE_LPC_ORDER, warped_autocorrelation};
use crate::silk::{FrameQuantizationOffsetType, FrameSignalType, MAX_NB_SUBFR};
const MIN_QGAIN_DB: i32 = 2;
const USE_HARM_SHAPING: bool = true;
const BG_SNR_DECR_DB_Q7: i32 = (BG_SNR_DECR_DB * (1 << 7) as f32 + 0.5) as i32;
const HARM_SNR_INCR_DB_Q8: i32 = (HARM_SNR_INCR_DB * (1 << 8) as f32 + 0.5) as i32;
const ENERGY_VARIATION_THRESHOLD_Q7: i32 =
(ENERGY_VARIATION_THRESHOLD_QNT_OFFSET * (1 << 7) as f32 + 0.5) as i32;
const FIND_PITCH_WHITE_NOISE_FRACTION_Q16: i32 =
(FIND_PITCH_WHITE_NOISE_FRACTION * (1 << 16) as f32 + 0.5) as i32;
const BANDWIDTH_EXPANSION_Q16: i32 = (BANDWIDTH_EXPANSION * (1 << 16) as f32 + 0.5) as i32;
const SHAPE_WHITE_NOISE_FRACTION_Q20: i32 =
(SHAPE_WHITE_NOISE_FRACTION * (1 << 20) as f32 + 0.5) as i32;
const LOW_FREQ_SHAPING_Q4: i32 = (LOW_FREQ_SHAPING * (1 << 4) as f32 + 0.5) as i32;
const LOW_QUALITY_LOW_FREQ_SHAPING_DECR_Q13: i32 =
(LOW_QUALITY_LOW_FREQ_SHAPING_DECR * (1 << 13) as f32 + 0.5) as i32;
const HP_NOISE_COEF_Q16: i32 = (HP_NOISE_COEF * (1 << 16) as f32 + 0.5) as i32;
const HARM_HP_NOISE_COEF_Q24: i32 = (HARM_HP_NOISE_COEF * (1 << 24) as f32 + 0.5) as i32;
const HARMONIC_SHAPING_Q16: i32 = (HARMONIC_SHAPING * (1 << 16) as f32 + 0.5) as i32;
const HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING_Q16: i32 =
(HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING * (1 << 16) as f32 + 0.5) as i32;
const SUBFR_SMTH_COEF_Q16: i32 = (SUBFR_SMTH_COEF * (1 << 16) as f32 + 0.5) as i32;
const ONE_Q16: i32 = 1 << 16;
const ONE_Q14: i32 = 1 << 14;
#[allow(clippy::too_many_arguments)]
pub fn noise_shape_analysis(
encoder: &mut EncoderChannelState,
control: &mut EncoderControl,
pitch_res: &[i16],
x: &[i16],
arch: i32,
) {
let nb_subframes = encoder.common.nb_subfr;
assert!(
nb_subframes == MAX_NB_SUBFR || nb_subframes == MAX_NB_SUBFR / 2,
"noise-shape analysis expects 2 or 4 subframes"
);
let lpc_order = encoder.common.shaping_lpc_order as usize;
assert!(
(1..=MAX_SHAPE_LPC_ORDER).contains(&lpc_order),
"shaping_lpc_order must be between 1 and {MAX_SHAPE_LPC_ORDER}"
);
assert!(
encoder.common.shape_win_length <= SHAPE_LPC_WIN_MAX,
"shape window exceeds maximum"
);
let la_shape = encoder.common.la_shape as usize;
let expected_signal_len = encoder.common.frame_length + 2 * la_shape;
assert!(
x.len() >= expected_signal_len,
"x must include la_shape lookahead on both sides of the frame"
);
assert!(
pitch_res.len() >= encoder.common.frame_length,
"pitch residual must span the frame"
);
let mut snr_adj_db_q7 = encoder.common.snr_db_q7;
control.input_quality_q14 = rshift(
encoder.common.input_quality_bands_q15[0] + encoder.common.input_quality_bands_q15[1],
2,
);
control.coding_quality_q14 = rshift(sigm_q15(rshift_round(snr_adj_db_q7 - (20 << 7), 4)), 1);
if !encoder.common.use_cbr {
let mut b_q8 = ONE_Q8 - encoder.common.speech_activity_q8;
b_q8 = smulwb(b_q8 << 8, b_q8);
let quality_mul = smulwb(
ONE_Q14 + control.input_quality_q14,
control.coding_quality_q14,
);
let snr_term = smulbb(-(BG_SNR_DECR_DB_Q7) >> 5, b_q8);
snr_adj_db_q7 = smlawb(snr_adj_db_q7, snr_term, quality_mul);
}
if encoder.common.indices.signal_type == FrameSignalType::Voiced {
snr_adj_db_q7 = smlawb(snr_adj_db_q7, HARM_SNR_INCR_DB_Q8, encoder.ltp_corr_q15);
encoder.common.indices.quant_offset_type = FrameQuantizationOffsetType::Low;
} else {
let tmp = smlawb(
(6.0_f32 * (1 << 9) as f32 + 0.5) as i32,
-(0.4_f32 * (1 << 18) as f32 + 0.5) as i32,
encoder.common.snr_db_q7,
);
snr_adj_db_q7 = smlawb(snr_adj_db_q7, tmp, ONE_Q14 - control.input_quality_q14);
let n_samples = (encoder.common.fs_khz << 1) as usize;
let n_segs = nb_subframes * crate::silk::encoder::state::SUB_FRAME_LENGTH_MS / 2;
let mut pitch_res_ptr = 0usize;
let mut energy_variation_q7 = 0;
let mut log_energy_prev_q7 = 0;
for _ in 0..n_segs {
let segment = &pitch_res[pitch_res_ptr..pitch_res_ptr + n_samples];
let (mut nrg, scale) = sum_sqr_shift(segment);
nrg += n_samples as i32 >> scale;
let log_energy_q7 = lin2log(nrg);
if pitch_res_ptr > 0 {
energy_variation_q7 += (log_energy_q7 - log_energy_prev_q7).abs();
}
log_energy_prev_q7 = log_energy_q7;
pitch_res_ptr += n_samples;
}
if energy_variation_q7 > ENERGY_VARIATION_THRESHOLD_Q7 * (n_segs as i32 - 1) {
encoder.common.indices.quant_offset_type = FrameQuantizationOffsetType::Low;
} else {
encoder.common.indices.quant_offset_type = FrameQuantizationOffsetType::High;
}
}
let strength_q16 = smulwb(control.pred_gain_q16, FIND_PITCH_WHITE_NOISE_FRACTION_Q16);
let bwexp_q16 = div32_varq(
BANDWIDTH_EXPANSION_Q16,
smlaww(ONE_Q16, strength_q16, strength_q16),
16,
);
let warping_q16 = if encoder.common.warping_q16 > 0 {
smlawb(
encoder.common.warping_q16,
control.coding_quality_q14,
(0.01_f32 * (1 << 18) as f32 + 0.5) as i32,
)
} else {
0
};
let mut x_ptr = 0usize;
let mut ar_q24 = [0i32; MAX_SHAPE_LPC_ORDER];
let mut auto_corr = [0i32; MAX_SHAPE_LPC_ORDER + 1];
let mut refl_coef_q16 = [0i32; MAX_SHAPE_LPC_ORDER];
let mut autocorr_scratch = [0i16; SHAPE_LPC_WIN_MAX as usize];
let mut x_windowed = [0i16; SHAPE_LPC_WIN_MAX as usize];
for k in 0..nb_subframes {
let flat_part = encoder.common.fs_khz * 3;
let slope_part = (encoder.common.shape_win_length - flat_part) >> 1;
let slope = slope_part as usize;
apply_sine_window(&mut x_windowed[..slope], &x[x_ptr..x_ptr + slope], 1);
let mut shift = slope;
let flat = flat_part as usize;
x_windowed[shift..shift + flat].copy_from_slice(&x[x_ptr + shift..x_ptr + shift + flat]);
shift += flat;
apply_sine_window(
&mut x_windowed[shift..shift + slope],
&x[x_ptr + shift..x_ptr + shift + slope],
2,
);
x_ptr += encoder.common.subfr_length;
let scale = if warping_q16 > 0 {
warped_autocorrelation(
&mut auto_corr,
&x_windowed[..encoder.common.shape_win_length as usize],
warping_q16,
lpc_order,
)
} else {
autocorr(
&mut auto_corr,
&x_windowed[..encoder.common.shape_win_length as usize],
lpc_order + 1,
arch,
&mut autocorr_scratch[..encoder.common.shape_win_length as usize],
)
};
auto_corr[0] = auto_corr[0]
.wrapping_add(smulwb(auto_corr[0] >> 4, SHAPE_WHITE_NOISE_FRACTION_Q20).max(1));
let mut nrg = schur64(&mut refl_coef_q16[..lpc_order], &auto_corr, lpc_order);
k2a_q16(&mut ar_q24, &refl_coef_q16[..lpc_order]);
let mut qnrg = -scale;
if (qnrg & 1) != 0 {
qnrg -= 1;
nrg >>= 1;
}
let tmp32 = sqrt_approx(nrg);
qnrg >>= 1;
control.gains_q16[k] = lshift_sat32(tmp32, 16 - qnrg);
if warping_q16 > 0 {
let gain_mult_q16 = warped_gain(&ar_q24[..lpc_order], warping_q16, lpc_order);
assert!(
control.gains_q16[k] > 0,
"warped gain adjustment requires positive subframe gain"
);
if control.gains_q16[k] < ONE_Q16 / 4 {
control.gains_q16[k] = smulww(control.gains_q16[k], gain_mult_q16);
} else {
control.gains_q16[k] = smulww(rshift_round(control.gains_q16[k], 1), gain_mult_q16);
control.gains_q16[k] = if control.gains_q16[k] >= (i32::MAX >> 1) {
i32::MAX
} else {
control.gains_q16[k] << 1
};
}
assert!(
control.gains_q16[k] > 0,
"warped gain adjustment must keep gains positive"
);
}
bwexpander_32(&mut ar_q24[..lpc_order], bwexp_q16);
if warping_q16 > 0 {
limit_warped_coefs(
&mut ar_q24[..lpc_order],
warping_q16,
(3.999_f32 * (1 << 24) as f32 + 0.5) as i32,
lpc_order,
);
for (dst, &coef) in control.ar_q13
[k * MAX_SHAPE_LPC_ORDER..k * MAX_SHAPE_LPC_ORDER + lpc_order]
.iter_mut()
.zip(&ar_q24[..lpc_order])
{
*dst = sat16(rshift_round(coef, 11));
}
} else {
lpc_fit(
&mut control.ar_q13[k * MAX_SHAPE_LPC_ORDER..k * MAX_SHAPE_LPC_ORDER + lpc_order],
&mut ar_q24[..lpc_order],
13,
24,
);
}
}
let gain_mult_q16 = log2lin(-smlawb(
-(16 << 7),
snr_adj_db_q7,
(0.16_f32 * (1 << 16) as f32 + 0.5) as i32,
));
assert!(gain_mult_q16 > 0, "gain multiplier must be positive");
let gain_add_q16 = log2lin(smlawb(
16 << 7,
MIN_QGAIN_DB << 7,
(0.16_f32 * (1 << 16) as f32 + 0.5) as i32,
));
for gain in control.gains_q16.iter_mut().take(nb_subframes) {
*gain = smulww(*gain, gain_mult_q16);
assert!(*gain >= 0, "gain multiplier must not yield negative gain");
*gain = add_pos_sat32(*gain, gain_add_q16);
}
let mut strength_q16 = mul(
LOW_FREQ_SHAPING_Q4,
smlawb(
1 << 12,
LOW_QUALITY_LOW_FREQ_SHAPING_DECR_Q13,
encoder.common.input_quality_bands_q15[0] - (1 << 15),
),
);
strength_q16 = rshift(strength_q16 * encoder.common.speech_activity_q8, 8);
let (tilt_q16, lf_shp_q14) = if encoder.common.indices.signal_type == FrameSignalType::Voiced {
let fs_khz_inv = div32_16(
(0.2_f32 * (1 << 14) as f32 + 0.5) as i32,
encoder.common.fs_khz,
);
let mut lf = [0i32; MAX_NB_SUBFR];
for (k, lf_slot) in lf.iter_mut().take(nb_subframes).enumerate() {
let b_q14 = fs_khz_inv
+ div32_16(
(3.0_f32 * (1 << 14) as f32 + 0.5) as i32,
control.pitch_l[k],
);
let packed = ((ONE_Q14
- b_q14
- smulwb(
strength_q16,
smulwb((0.6_f32 * (1 << 16) as f32 + 0.5) as i32, b_q14),
))
<< 16)
| ((b_q14 - ONE_Q14) & 0xFFFF);
*lf_slot = packed;
}
let tilt = -HP_NOISE_COEF_Q16
- smulwb(
ONE_Q16 - HP_NOISE_COEF_Q16,
smulwb(HARM_HP_NOISE_COEF_Q24, encoder.common.speech_activity_q8),
);
(tilt, lf)
} else {
let b_q14 = div32_16(21299, encoder.common.fs_khz);
let packed = ((ONE_Q14
- b_q14
- smulwb(
strength_q16,
smulwb((0.6_f32 * (1 << 16) as f32 + 0.5) as i32, b_q14),
))
<< 16)
| ((b_q14 - ONE_Q14) & 0xFFFF);
let lf = [packed; MAX_NB_SUBFR];
(-HP_NOISE_COEF_Q16, lf)
};
let mut harm_shape_gain_q16 = 0;
if USE_HARM_SHAPING && encoder.common.indices.signal_type == FrameSignalType::Voiced {
harm_shape_gain_q16 = smlawb(
HARMONIC_SHAPING_Q16,
ONE_Q16
- smulwb(
(1 << 18) - (control.coding_quality_q14 << 4),
control.input_quality_q14,
),
HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING_Q16,
);
harm_shape_gain_q16 = smulwb(
harm_shape_gain_q16 << 1,
sqrt_approx(encoder.ltp_corr_q15 << 15),
);
}
for (k, lf) in lf_shp_q14.iter().enumerate().take(MAX_NB_SUBFR) {
encoder.shape_state.harm_shape_gain_smth_q16 = smlawb(
encoder.shape_state.harm_shape_gain_smth_q16,
harm_shape_gain_q16 - encoder.shape_state.harm_shape_gain_smth_q16,
SUBFR_SMTH_COEF_Q16,
);
encoder.shape_state.tilt_smth_q16 = smlawb(
encoder.shape_state.tilt_smth_q16,
tilt_q16 - encoder.shape_state.tilt_smth_q16,
SUBFR_SMTH_COEF_Q16,
);
control.harm_shape_gain_q14[k] =
rshift_round(encoder.shape_state.harm_shape_gain_smth_q16, 2);
control.tilt_q14[k] = rshift_round(encoder.shape_state.tilt_smth_q16, 2);
control.lf_shp_q14[k] = *lf;
}
}
fn warped_gain(coefs_q24: &[i32], lambda_q16: i32, order: usize) -> i32 {
let mut gain_q24 = coefs_q24[order - 1];
let lambda = -lambda_q16;
for &coef in coefs_q24[..order - 1].iter().rev() {
gain_q24 = smlawb(coef, gain_q24, lambda);
}
gain_q24 = smlawb(1 << 24, gain_q24, -lambda);
inverse32_varq(gain_q24, 40)
}
fn limit_warped_coefs(coefs_q24: &mut [i32], lambda_q16: i32, limit_q24: i32, order: usize) {
let mut lambda = -lambda_q16;
for i in (1..order).rev() {
coefs_q24[i - 1] = smlawb(coefs_q24[i - 1], coefs_q24[i], lambda);
}
lambda = -lambda;
let mut nom_q16 = smlawb(ONE_Q16, -lambda, lambda);
let mut den_q24 = smlawb(1 << 24, coefs_q24[0], lambda);
let mut gain_q16 = div32_varq(nom_q16, den_q24, 24);
for coef in coefs_q24.iter_mut().take(order) {
*coef = smulww(gain_q16, *coef);
}
let limit_q20 = limit_q24 >> 4;
for iter in 0..10 {
let (maxabs_q24, idx) = coefs_q24
.iter()
.take(order)
.enumerate()
.map(|(i, &c)| (c.abs(), i))
.max_by(|a, b| a.0.cmp(&b.0))
.unwrap();
let maxabs_q20 = maxabs_q24 >> 4;
if maxabs_q20 <= limit_q20 {
return;
}
for i in 1..order {
coefs_q24[i - 1] = smlawb(coefs_q24[i - 1], coefs_q24[i], lambda);
}
gain_q16 = inverse32_varq(gain_q16, 32);
for coef in coefs_q24.iter_mut().take(order) {
*coef = smulww(gain_q16, *coef);
}
let chirp_q16 = ((0.99_f32 * (1 << 16) as f32 + 0.5) as i32)
- div32_varq(
smulwb(
maxabs_q20 - limit_q20,
smlabb(
(0.8_f32 * (1 << 10) as f32 + 0.5) as i32,
(0.1_f32 * (1 << 10) as f32 + 0.5) as i32,
iter,
),
),
mul(maxabs_q20, (idx + 1) as i32),
22,
);
bwexpander_32(&mut coefs_q24[..order], chirp_q16);
lambda = -lambda;
for i in (1..order).rev() {
coefs_q24[i - 1] = smlawb(coefs_q24[i - 1], coefs_q24[i], lambda);
}
lambda = -lambda;
nom_q16 = smlawb(ONE_Q16, -lambda, lambda);
den_q24 = smlawb(1 << 24, coefs_q24[0], lambda);
gain_q16 = div32_varq(nom_q16, den_q24, 24);
for coef in coefs_q24.iter_mut().take(order) {
*coef = smulww(gain_q16, *coef);
}
}
panic!("limit_warped_coefs failed to converge within 10 iterations");
}
fn smulbb(a: i32, b: i32) -> i32 {
i32::from((a as i16).wrapping_mul(b as i16))
}
fn smulwb(a: i32, b: i32) -> i32 {
((i64::from(a) * i64::from(b as i16)) >> 16) as i32
}
fn smulww(a: i32, b: i32) -> i32 {
((i64::from(a) * i64::from(b)) >> 16) as i32
}
fn smlabb(a: i32, b: i32, c: i32) -> i32 {
a.wrapping_add(i32::from((b as i16).wrapping_mul(c as i16)))
}
fn smlawb(a: i32, b: i32, c: i32) -> i32 {
a.wrapping_add(((i64::from(b) * i64::from(c as i16)) >> 16) as i32)
}
fn smlaww(a: i32, b: i32, c: i32) -> i32 {
a.wrapping_add(((i64::from(b) * i64::from(c)) >> 16) as i32)
}
fn div32_varq(a32: i32, b32: i32, q_res: i32) -> i32 {
crate::silk::stereo_find_predictor::div32_varq(a32, b32, q_res)
}
fn rshift(value: i32, shift: i32) -> i32 {
if shift <= 0 { value } else { value >> shift }
}
fn rshift_round(value: i32, shift: i32) -> i32 {
if shift <= 0 {
value
} else if shift == 1 {
(value >> 1).wrapping_add(value & 1)
} else {
((value >> (shift - 1)) + 1) >> 1
}
}
fn lshift_sat32(value: i32, shift: i32) -> i32 {
if shift <= 0 {
rshift_round(value, -shift)
} else {
let shifted = (i64::from(value)) << shift;
if shifted > i64::from(i32::MAX) {
i32::MAX
} else if shifted < i64::from(i32::MIN) {
i32::MIN
} else {
shifted as i32
}
}
}
fn sat16(value: i32) -> i16 {
value.clamp(i32::from(i16::MIN), i32::from(i16::MAX)) as i16
}
fn div32_16(a: i32, b: i32) -> i32 {
assert_ne!(b, 0, "division by zero in div32_16");
a / b
}
fn add_pos_sat32(a: i32, b: i32) -> i32 {
let sum = i64::from(a) + i64::from(b);
if sum > i64::from(i32::MAX) {
i32::MAX
} else {
sum as i32
}
}
fn mul(a: i32, b: i32) -> i32 {
(i64::from(a) * i64::from(b)) as i32
}
fn sigm_q15(input_q5: i32) -> i32 {
crate::silk::sigm_q15::sigm_q15(input_q5)
}
fn sqrt_approx(x: i32) -> i32 {
if x <= 0 {
return 0;
}
let (lz, frac_q7) = clz_frac(x);
let mut y = if lz & 1 != 0 { 32_768 } else { 46_214 };
y >>= lz >> 1;
smlawb(y, y, smulbb(213, frac_q7))
}
fn clz_frac(x: i32) -> (i32, i32) {
let ux = x as u32;
let lz = ux.leading_zeros() as i32;
let rotate = ((24 - lz) & 31) as u32;
let frac = (ux.rotate_right(rotate) & 0x7f) as i32;
(lz, frac)
}
const ONE_Q8: i32 = 1 << 8;
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
fn with_basic_encoder(signal_type: FrameSignalType) -> (EncoderChannelState, EncoderControl) {
let mut encoder = EncoderChannelState::default();
encoder.common.indices.signal_type = signal_type;
encoder.common.shaping_lpc_order = 16;
encoder.common.shape_win_length = 240;
encoder.common.la_shape = 80;
encoder.common.subfr_length = 80;
encoder.common.frame_length = 320;
encoder.ltp_corr_q15 = 0;
let mut control = EncoderControl::default();
control.pitch_l.fill(40);
(encoder, control)
}
#[test]
fn quant_offset_stays_low_for_voiced_frames() {
let (mut encoder, mut control) = with_basic_encoder(FrameSignalType::Voiced);
let pitch_res = vec![0i16; encoder.common.frame_length];
let x = vec![0i16; encoder.common.frame_length + 2 * encoder.common.la_shape as usize];
noise_shape_analysis(&mut encoder, &mut control, &pitch_res, &x, 0);
assert_eq!(
encoder.common.indices.quant_offset_type,
FrameQuantizationOffsetType::Low
);
}
#[test]
fn sparse_unvoiced_frames_pick_high_quant_offset() {
let (mut encoder, mut control) = with_basic_encoder(FrameSignalType::Unvoiced);
let pitch_res = vec![0i16; encoder.common.frame_length];
let x = vec![0i16; encoder.common.frame_length + 2 * encoder.common.la_shape as usize];
noise_shape_analysis(&mut encoder, &mut control, &pitch_res, &x, 0);
assert_eq!(
encoder.common.indices.quant_offset_type,
FrameQuantizationOffsetType::High
);
}
#[test]
fn warped_path_populates_shaping_coefs() {
let (mut encoder, mut control) = with_basic_encoder(FrameSignalType::Unvoiced);
encoder.common.warping_q16 = 1 << 14;
let mut pitch_res = vec![0i16; encoder.common.frame_length];
for (i, slot) in pitch_res.iter_mut().enumerate() {
*slot = (i as i16).wrapping_mul(3);
}
let mut x = vec![0i16; encoder.common.frame_length + 2 * encoder.common.la_shape as usize];
for (i, slot) in x.iter_mut().enumerate() {
*slot = (i as i16).wrapping_sub(50);
}
noise_shape_analysis(&mut encoder, &mut control, &pitch_res, &x, 0);
let first_band = &control.ar_q13[..encoder.common.shaping_lpc_order as usize];
assert!(
first_band.iter().any(|&c| c != 0),
"warped analysis should generate non-zero AR coefficients"
);
assert!(
first_band.iter().all(|&c| c != i16::MAX && c != i16::MIN),
"AR coefficients should stay away from saturation"
);
}
}