add VivoActive (imperfect) format family support
authorKostya Shishkov <kostya.shishkov@gmail.com>
Tue, 5 May 2020 14:21:47 +0000 (16:21 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Tue, 5 May 2020 14:21:47 +0000 (16:21 +0200)
nihav-registry/src/detect.rs
nihav-registry/src/register.rs
nihav-vivo/Cargo.toml [new file with mode: 0644]
nihav-vivo/src/codecs/g723_1.rs [new file with mode: 0644]
nihav-vivo/src/codecs/mod.rs [new file with mode: 0644]
nihav-vivo/src/codecs/siren.rs [new file with mode: 0644]
nihav-vivo/src/codecs/vivo.rs [new file with mode: 0644]
nihav-vivo/src/demuxers/mod.rs [new file with mode: 0644]
nihav-vivo/src/demuxers/vivo.rs [new file with mode: 0644]
nihav-vivo/src/lib.rs [new file with mode: 0644]

index aeaa06d408be4996865e7e75732f17825a1aa3f5..7d0633a7630d4f2ee4db12ef34cd955539b3dc10 100644 (file)
@@ -247,6 +247,18 @@ const DETECTORS: &[DetectConditions] = &[
         extensions: ".smk",
         conditions: &[CheckItem{offs: 0, cond: &CC::Or(&CC::Str(b"SMK2"), &CC::Str(b"SMK4"))}],
     },
         extensions: ".smk",
         conditions: &[CheckItem{offs: 0, cond: &CC::Or(&CC::Str(b"SMK2"), &CC::Str(b"SMK4"))}],
     },
+    DetectConditions {
+        demux_name: "vivo",
+        extensions: ".viv",
+        conditions: &[CheckItem{offs: 0, cond: &CC::In(Arg::U16BE(1), Arg::U16BE(0xFF))},
+                      CheckItem{offs: 2, cond: &CC::Str(b"\x0D\x0AVersion:Vivo/")}],
+    },
+    DetectConditions {
+        demux_name: "vivo",
+        extensions: ".viv",
+        conditions: &[CheckItem{offs: 0, cond: &CC::In(Arg::U16BE(1), Arg::U16BE(0xFF))},
+                      CheckItem{offs: 3, cond: &CC::Str(b"\x0D\x0AVersion:Vivo/")}],
+    },
     DetectConditions {
         demux_name: "bmv",
         extensions: ".bmv",
     DetectConditions {
         demux_name: "bmv",
         extensions: ".bmv",
index cba6e2d8688c4f322eba1f1a00a36f3803903771..628cc6b87af5271f99b5b76d5e4c0f6a104ce0ed 100644 (file)
@@ -212,6 +212,11 @@ static CODEC_REGISTER: &'static [CodecDescription] = &[
     desc!(audio;    "lhst250f11",    "L&H StreamTalk 25kbps at 11 kHz"),
     desc!(audio;    "lhst500f22",    "L&H StreamTalk 50kpbs at 22 kHz"),
     desc!(audio;    "lhst48",        "L&H StreamTalk CELP Codec 4.8kbps at 8 kHz"),
     desc!(audio;    "lhst250f11",    "L&H StreamTalk 25kbps at 11 kHz"),
     desc!(audio;    "lhst500f22",    "L&H StreamTalk 50kpbs at 22 kHz"),
     desc!(audio;    "lhst48",        "L&H StreamTalk CELP Codec 4.8kbps at 8 kHz"),
+
+    desc!(video;    "vivo1",         "VivoActive Video 1.0"),
+    desc!(video;    "vivo2",         "VivoActive Video 2.0", CODEC_CAP_REORDER),
+    desc!(audio;    "g723.1",        "ITU G.723.1"),
+    desc!(audio;    "siren",         "Polycom Siren"),
 ];
 
 static AVI_VIDEO_CODEC_REGISTER: &'static [(&[u8;4], &str)] = &[
 ];
 
 static AVI_VIDEO_CODEC_REGISTER: &'static [(&[u8;4], &str)] = &[
diff --git a/nihav-vivo/Cargo.toml b/nihav-vivo/Cargo.toml
new file mode 100644 (file)
index 0000000..5aa02d6
--- /dev/null
@@ -0,0 +1,25 @@
+[package]
+name = "nihav_vivo"
+version = "0.1.0"
+authors = ["Kostya Shishkov <kostya.shishkov@gmail.com>"]
+edition = "2018"
+
+[dependencies.nihav_core]
+path = "../nihav-core"
+
+[dependencies.nihav_codec_support]
+path = "../nihav-codec-support"
+features = ["dsp_window", "mdct", "dct", "h263"]
+
+[features]
+default = ["all_decoders", "demuxer_vivo"]
+
+all_decoders = ["decoder_vivo1", "decoder_vivo2", "decoder_siren", "decoder_g723_1"]
+decoders = []
+
+decoder_vivo1 = ["decoders"]
+decoder_vivo2 = ["decoders"]
+decoder_g723_1 = ["decoders"]
+decoder_siren = ["decoders"]
+
+demuxer_vivo = []
diff --git a/nihav-vivo/src/codecs/g723_1.rs b/nihav-vivo/src/codecs/g723_1.rs
new file mode 100644 (file)
index 0000000..840bb94
--- /dev/null
@@ -0,0 +1,2203 @@
+use nihav_core::formats::SND_S16_FORMAT;
+use nihav_core::frame::*;
+use nihav_core::codecs::*;
+use nihav_core::io::bitreader::*;
+use std::str::FromStr;
+
+const SAMPLES:          usize = 240;
+const SUBFRAMES:        usize = 4;
+const SUBFRAME_LEN:     usize = SAMPLES / SUBFRAMES;
+const LPC_ORDER:        usize = 10;
+const MIN_PITCH_LAG:    usize = 18;
+const MAX_PITCH:        usize = MIN_PITCH_LAG + 127;
+const MAX_ERASED_FRAMES:u8    = 3;
+const MAX_PULSES:       usize = 6;
+
+#[derive(Clone,Copy,PartialEq)]
+enum G7231FrameType {
+    Active,
+    SID,
+    Untransmitted,
+}
+
+#[derive(Clone,Copy,Default)]
+struct Subframe {
+    pitch_lag:      usize,
+    acb_lag:        usize,
+    acb_gain:       usize,
+    dirac_train:    bool,
+    pulse_sign:     u8,
+    grid_index:     usize,
+    amp_index:      usize,
+    pulse_pos:      usize,
+
+    ppf_index:      usize,
+    ppf_opt_gain:   i32,
+    ppf_sc_gain:    i32,
+}
+
+impl Subframe {
+    fn read_acb_params(&mut self, br: &mut BitReader, is_6300: bool) -> DecoderResult<()> {
+        let mut val                     = br.read(12)? as usize;
+        let acb_len;
+        if is_6300 && (self.pitch_lag < SUBFRAME_LEN - 2) {
+            self.dirac_train = (val >> 11) != 0;
+            val &= 0x7FF;
+            acb_len = 85;
+        } else {
+            self.dirac_train = false;
+            acb_len = 170;
+        }
+        self.acb_gain  = val / 24;
+        self.amp_index = val % 24;
+        validate!(self.acb_gain < acb_len);
+
+        Ok(())
+    }
+    fn gen_fcb_excitation(&self, dst: &mut [i16], is_6300: bool, max_pos: usize, npulses: usize) {
+        for el in dst.iter_mut().take(SUBFRAME_LEN) {
+            *el = 0;
+        }
+        let fcb_gain = FIXED_CB_GAIN[self.amp_index];
+        if is_6300 {
+            if self.pulse_pos >= max_pos {
+                return;
+            }
+            let mut pulse = MAX_PULSES - npulses;
+            let mut ppos = self.pulse_pos;
+            for i in 0..SUBFRAME_LEN/2 {
+                let ret = ppos.checked_sub(PULSE_POSITIONS[pulse][i] as usize);
+                if let Some(new_ppos) = ret {
+                    ppos = new_ppos;
+                    continue;
+                }
+                pulse += 1;
+                let sign = (self.pulse_sign & (1 << (MAX_PULSES - pulse))) != 0;
+                dst[i * 2 + self.grid_index] = if !sign { fcb_gain } else { -fcb_gain };
+                if pulse == MAX_PULSES {
+                    break;
+                }
+            }
+            if self.dirac_train {
+                let mut orig = [0; SUBFRAME_LEN];
+                orig.copy_from_slice(&dst[..SUBFRAME_LEN]);
+                for i in (self.pitch_lag..SUBFRAME_LEN).step_by(self.pitch_lag) {
+                    for j in 0..SUBFRAME_LEN - i {
+                        dst[i + j] += orig[j];
+                    }
+                }
+            }
+        } else {
+            let mut cb_pos  = self.pulse_pos;
+            let mut cb_sign = self.pulse_sign;
+            for i in (0..8).step_by(2) {
+                let off = (cb_pos & 7) * 8 + i + self.grid_index;
+                dst[off] = if (cb_sign & 1) != 0 { fcb_gain } else { -fcb_gain };
+                cb_pos  >>= 3;
+                cb_sign >>= 1;
+            }
+
+            let lag = (PITCH_CONTRIBUTION[self.acb_gain * 2] as isize + self.pitch_lag as isize + self.acb_lag as isize - 1) as usize;
+            let beta = PITCH_CONTRIBUTION[self.acb_gain * 2 + 1];
+            if lag < SUBFRAME_LEN - 2 {
+                for i in lag..SUBFRAME_LEN {
+                    dst[i] += ((beta * i32::from(dst[i - lag])) >> 15) as i16;
+                }
+            }
+        }
+    }
+    fn gen_acb_excitation(&mut self, acb_vector: &mut [i16; SUBFRAME_LEN], excitation: &[i16], is_6300: bool) {
+        let lag = self.pitch_lag + self.acb_lag - 1;
+
+        let mut residual = [0; SUBFRAME_LEN + 5 - 1];
+        residual[0] = excitation[MAX_PITCH - 2 - lag];
+        residual[1] = excitation[MAX_PITCH - 1 - lag];
+        for (i, dst) in residual.iter_mut().skip(2).enumerate() {
+            *dst = excitation[MAX_PITCH - lag + i % lag];
+        }
+
+        let codebook = if is_6300 && self.pitch_lag < SUBFRAME_LEN - 2 {
+                &ACB_GAIN85[self.acb_gain]
+            } else {
+                &ACB_GAIN170[self.acb_gain]
+            };
+        for i in 0..SUBFRAME_LEN {
+            let sum = dot_product(&residual[i..], codebook, 5);
+            acb_vector[i] = (sum.saturating_add(sum).saturating_add(1 << 15) >> 16) as i16;
+        }
+    }
+    fn compute_ppf_coeffs(&mut self, src: &[i16], offset: usize, is_6300: bool) {
+        self.ppf_index    = offset;
+        self.ppf_opt_gain = 0;
+        self.ppf_sc_gain  = 0x7FFF;
+
+        let (fwd_lag, fwd_energy) = autocorr_max(src, offset, SUBFRAME_LEN, self.pitch_lag, true);
+        let (bwd_lag, bwd_energy) = autocorr_max(src, offset, SUBFRAME_LEN, self.pitch_lag, false);
+        if fwd_lag == 0 && bwd_lag == 0 {
+            return;
+        }
+
+        let tgt_energy = dot_product(&src[offset..], &src[offset..], SUBFRAME_LEN);
+        let fwd_res_energy = if fwd_lag != 0 {
+                dot_product(&src[offset + fwd_lag..], &src[offset + fwd_lag..], SUBFRAME_LEN)
+            } else { 0 };
+        let bwd_res_energy = if bwd_lag != 0 {
+                dot_product(&src[offset - bwd_lag..], &src[offset - bwd_lag..], SUBFRAME_LEN)
+            } else { 0 };
+
+        let max_energy = tgt_energy.max(fwd_energy).max(fwd_res_energy).max(bwd_energy).max(bwd_res_energy);
+        let scale = norm_bits(max_energy, 31);
+        let tgt_energy     = tgt_energy     << scale >> 16; 
+        let fwd_energy     = fwd_energy     << scale >> 16;
+        let fwd_res_energy = fwd_res_energy << scale >> 16;
+        let bwd_energy     = bwd_energy     << scale >> 16;
+        let bwd_res_energy = bwd_res_energy << scale >> 16;
+
+        let use_fwd = if fwd_lag != 0 && bwd_lag == 0 {
+                true
+            } else if fwd_lag == 0 {
+                false
+            } else {
+                let tmp1 = bwd_res_energy * ((fwd_energy * fwd_energy + (1 << 14)) >> 15);
+                let tmp2 = fwd_res_energy * ((bwd_energy * bwd_energy + (1 << 14)) >> 15);
+                tmp1 >= tmp2
+            };
+        if use_fwd {
+            self.compute_ppf_gains(offset + fwd_lag, is_6300, tgt_energy, fwd_energy, fwd_res_energy);
+        } else {
+            self.compute_ppf_gains(offset - bwd_lag, is_6300, tgt_energy, bwd_energy, bwd_res_energy);
+        }
+    }
+    fn compute_ppf_gains(&mut self, offset: usize, is_6300: bool, tgt_energy: i32, corr_energy: i32, res_energy: i32) {
+        self.ppf_index = offset;
+
+        let tmp1 = tgt_energy * res_energy >> 1;
+        let tmp2 = corr_energy * corr_energy * 2;
+        if tmp1 >= tmp2 {
+            return;
+        }
+        let gain_weight = if is_6300 { 0x1800 } else { 0x2000 };
+        self.ppf_opt_gain = if corr_energy >= res_energy {
+                gain_weight
+            } else {
+                (corr_energy << 15) / res_energy * gain_weight >> 15
+            };
+
+        let tmp1 = (tgt_energy << 15) + (corr_energy * self.ppf_opt_gain * 2);
+        let tmp2 = (self.ppf_opt_gain * self.ppf_opt_gain >> 15) * res_energy;
+
+        let residual = tmp1.saturating_add(tmp2).saturating_add(1 << 15) >> 16;
+
+        if tgt_energy >= residual * 2 {
+            self.ppf_sc_gain = square_root_i32(0x7FFF0000);
+        } else {
+            let val = (tgt_energy << 14) / residual;
+            self.ppf_sc_gain = square_root_i32(val << 16);
+        }
+
+        self.ppf_opt_gain = i32::from(clip16(self.ppf_opt_gain * self.ppf_sc_gain >> 15));
+    }
+}
+
+fn square_root_i32(val: i32) -> i32 {
+    let mut res = 0;
+    let mut exp = 1 << 14;
+
+    for _ in 0..14 {
+        let res_exp = res + exp;
+        if val >= res_exp * res_exp * 2 {
+            res += exp;
+        }
+        exp >>= 1;
+    }
+
+    res
+}
+
+struct PRNG {
+    seed:   i32,
+}
+
+const CNG_RND_SEED: i32 = 12345;
+impl PRNG {
+    fn new()     -> Self { Self { seed: 0 } }
+    fn new_cng() -> Self { Self { seed: CNG_RND_SEED } }
+    fn reset(&mut self) { self.seed = CNG_RND_SEED; }
+    fn next(&mut self) -> i32 {
+        self.seed = self.seed.wrapping_mul(521).wrapping_add(259);
+        self.seed
+    }
+    fn next_range(&mut self, range: usize) -> usize {
+        let val = (self.next() & 0x7FFF) as usize;
+        val * range >> 15
+    }
+}
+
+struct G7231Decoder {
+    chmap:              NAChannelMap,
+    ainfo:              NAAudioInfo,
+    info:               NACodecInfoRef,
+
+    is_6300:            bool,
+    prev_lsp:           [i16; LPC_ORDER],
+    sid_lsp:            [i16; LPC_ORDER],
+    prev_ftype:         G7231FrameType,
+    cur_ftype:          G7231FrameType,
+    lsp_index:          [usize; 3],
+    subframe:           [Subframe; SUBFRAMES],
+    excitation:         [i16; MAX_PITCH + SAMPLES + 4],
+    prev_excitation:    [i16; MAX_PITCH],
+    lpc:                [[i16; LPC_ORDER]; SUBFRAMES],
+
+    filt_mem:           [i16; LPC_ORDER],
+
+    interp_index:       usize,
+    interp_gain:        i16,
+    sid_gain:           i32,
+    cur_gain:           i32,
+
+    fir_mem:            [i16; LPC_ORDER],
+    iir_mem:            [i32; LPC_ORDER],
+    reflection_coef:    i32,
+    pf_gain:            i32,
+
+    cng_rnd:            PRNG,
+    rnd:                PRNG,
+    erased_frames:      u8,
+
+    synth_buf:          [i16; SAMPLES + LPC_ORDER + MAX_PITCH + 4],
+}
+
+macro_rules! weighted_sum {
+    ($dst: expr, $src1: expr, $weight1: expr, $src2: expr, $weight2: expr, $shift: expr) => {
+        let bias = 1 << ($shift - 1);
+        for (dst, (src1, src2)) in $dst.iter_mut().zip($src1.iter().zip($src2.iter())) {
+            let val = (i32::from(*src1) * $weight1 + i32::from(*src2) * $weight2 + bias) >> $shift;
+            *dst = clip16(val);
+        }
+    }
+}
+
+impl G7231Decoder {
+    fn new() -> Self {
+        let prev_lsp = DC_LSP;
+        let sid_lsp  = DC_LSP;
+        G7231Decoder {
+            chmap:              NAChannelMap::from_str("C").unwrap(),
+            ainfo:              NAAudioInfo::new(8000, 1, SND_S16_FORMAT, SAMPLES),
+            info:               NACodecInfo::new_dummy(),
+
+            is_6300:            true,
+            prev_lsp, sid_lsp,
+            prev_ftype:         G7231FrameType::SID,
+            cur_ftype:          G7231FrameType::Untransmitted,
+            lsp_index:          [0; 3],
+            subframe:           [Subframe::default(); SUBFRAMES],
+            excitation:         [0; MAX_PITCH + SAMPLES + 4],
+            prev_excitation:    [0; MAX_PITCH],
+            lpc:                [[0; LPC_ORDER]; SUBFRAMES],
+
+            filt_mem:           [0; LPC_ORDER],
+
+            interp_index:       0,
+            interp_gain:        0,
+            sid_gain:           0,
+            cur_gain:           0,
+
+            fir_mem:            [0; LPC_ORDER],
+            iir_mem:            [0; LPC_ORDER],
+            reflection_coef:    0,
+            pf_gain:            1 << 12,
+
+            cng_rnd:            PRNG::new_cng(),
+            rnd:                PRNG::new(),
+            erased_frames:      0,
+
+            synth_buf:          [0; SAMPLES + LPC_ORDER + MAX_PITCH + 4],
+        }
+    }
+    fn unpack_frame(&mut self, br: &mut BitReader) -> DecoderResult<()> {
+        const FRAME_SIZE: [isize; 4] = [ 24, 20, 4, 1 ];
+        let ftype                       = br.read(2)? as usize;
+        validate!(br.left() + 2 >= FRAME_SIZE[ftype] * 8);
+        if ftype == 3 {
+            self.cur_ftype = G7231FrameType::Untransmitted;
+            return Ok(());
+        }
+
+        for lsp_index in self.lsp_index.iter_mut().rev() {
+            *lsp_index                  = br.read(8)? as usize;
+        }
+
+        if ftype == 2 {
+            self.cur_ftype = G7231FrameType::Untransmitted;
+            self.subframe[0].amp_index  = br.read(8)? as usize;
+            return Ok(());
+        }
+
+        self.cur_ftype = G7231FrameType::Active;
+        self.is_6300 = ftype == 0;
+
+        for i in (0..4).step_by(2) {
+            self.subframe[i].pitch_lag  = (br.read(7)? as usize) + MIN_PITCH_LAG;
+            validate!(self.subframe[i].pitch_lag < MIN_PITCH_LAG + 124);
+            self.subframe[i + 1].pitch_lag = self.subframe[i].pitch_lag;
+            self.subframe[i].acb_lag = 1;
+            self.subframe[i + 1].acb_lag = br.read(2)? as usize;
+        }
+
+        for subframe in self.subframe.iter_mut() {
+            subframe.read_acb_params(br, self.is_6300)?;
+        }
+        for subframe in self.subframe.iter_mut() {
+            subframe.grid_index         = br.read(1)? as usize;
+        }
+        if self.is_6300 {
+                                          br.read(1)?;
+            let mut ppos                = br.read(13)? as usize;
+            self.subframe[0].pulse_pos = ppos / 810;
+            ppos = ppos % 810;
+            self.subframe[1].pulse_pos = ppos / 90;
+            ppos = ppos % 90;
+            self.subframe[2].pulse_pos = ppos / 9;
+            self.subframe[3].pulse_pos = ppos % 9;
+
+            for (i, subframe) in self.subframe.iter_mut().enumerate() {
+                let bits = if (i & 1) == 0 { 16 } else { 14 };
+                let val                 = br.read(bits)? as usize;
+                subframe.pulse_pos = (subframe.pulse_pos << bits) | val;
+            }
+            for (i, subframe) in self.subframe.iter_mut().enumerate() {
+                let bits = if (i & 1) == 0 { 6 } else { 5 };
+                subframe.pulse_sign     = br.read(bits)? as u8;
+            }
+        } else {
+            for subframe in self.subframe.iter_mut() {
+                subframe.pulse_pos      = br.read(12)? as usize;
+            }
+            for subframe in self.subframe.iter_mut() {
+                subframe.pulse_sign     = br.read(4)? as u8;
+            }
+        }
+
+        Ok(())
+    }
+    fn synth_frame_active(&mut self, dst: &mut [i16], bad_frame: bool) {
+        const FCB_MAX_POS: [usize; 4] = [ 593775, 142506, 593775, 142506 ];
+        if !bad_frame {
+            self.erased_frames = 0;
+        } else {
+            self.erased_frames = (self.erased_frames + 1).min(MAX_ERASED_FRAMES);
+        }
+        if bad_frame {
+            self.lsp_index = [0; 3];
+        }
+        let mut cur_lsp = [0; LPC_ORDER];
+        Self::inverse_quant(&self.prev_lsp, &mut cur_lsp, &self.lsp_index, bad_frame);
+        Self::interpolate_lsp(&mut self.lpc, &cur_lsp, &self.prev_lsp);
+        self.prev_lsp.copy_from_slice(&cur_lsp);
+        (&mut self.excitation[..MAX_PITCH]).copy_from_slice(&self.prev_excitation);
+        if self.erased_frames == 0 {
+            let mut acb_vector = [0; SUBFRAME_LEN];
+            self.interp_gain = FIXED_CB_GAIN[(self.subframe[2].amp_index + self.subframe[3].amp_index) >> 1];
+            let mut exc_start = MAX_PITCH;
+            for (i, subframe) in self.subframe.iter_mut().enumerate() {
+                subframe.gen_fcb_excitation(&mut self.excitation[exc_start..], self.is_6300, FCB_MAX_POS[i], if (i & 1) == 0 { 6 } else { 5 });
+                subframe.gen_acb_excitation(&mut acb_vector, &self.excitation[SUBFRAME_LEN * i..], self.is_6300);
+                for i in 0..SUBFRAME_LEN {
+                    let val = self.excitation[exc_start + i];
+                    self.excitation[exc_start + i] = val.saturating_add(val).saturating_add(acb_vector[i]);
+                }
+                exc_start += SUBFRAME_LEN;
+            }
+            self.compute_interp_index();
+            let mut offset = MAX_PITCH;
+            for subframe in self.subframe.iter_mut() {
+                subframe.compute_ppf_coeffs(&self.synth_buf[LPC_ORDER..], offset, self.is_6300);
+                offset += SUBFRAME_LEN;
+            }
+            for i in 0..SUBFRAMES {
+                let src1 = &self.excitation[MAX_PITCH + i * SUBFRAME_LEN..][..SUBFRAME_LEN];
+                let src2 = &self.excitation[self.subframe[i].ppf_index..][..SUBFRAME_LEN];
+                let dst = &mut self.synth_buf[LPC_ORDER + i * SUBFRAME_LEN..][..SUBFRAME_LEN];
+                weighted_sum!(dst, src1, self.subframe[i].ppf_sc_gain, src2, self.subframe[i].ppf_opt_gain, 15);
+            }
+            self.prev_excitation.copy_from_slice(&self.excitation[SAMPLES..][..MAX_PITCH]);
+        } else {
+            self.interp_gain = (self.interp_gain * 3 + 2) >> 2;
+            if self.erased_frames == MAX_ERASED_FRAMES {
+                for el in self.excitation.iter_mut() {
+                    *el = 0;
+                }
+                for el in self.prev_excitation.iter_mut() {
+                    *el = 0;
+                }
+                for el in dst.iter_mut() {
+                    *el = 0;
+                }
+            } else {
+                if self.interp_index != 0 {
+                    for i in 0..self.interp_index {
+                        let sample = i32::from(self.excitation[MAX_PITCH + i]) * 3 >> 2;
+                        self.synth_buf[LPC_ORDER + i] = sample as i16;
+                    }
+                    for i in self.interp_index..SAMPLES {
+                        self.synth_buf[LPC_ORDER + i] = self.synth_buf[LPC_ORDER + i - self.interp_index];
+                    }
+                } else {
+                    for i in 0..SAMPLES {
+                        let sample = self.rnd.next().wrapping_mul(i32::from(self.interp_gain)) >> 15;
+                        self.synth_buf[LPC_ORDER + i] = sample as i16;
+                    }
+                }
+                self.prev_excitation.copy_from_slice(&self.synth_buf[LPC_ORDER + SAMPLES - MAX_PITCH..][..MAX_PITCH]);
+            }
+        }
+        self.cng_rnd.reset();
+    }
+    fn amp_index_to_sid_gain(val: usize) -> i32 {
+        if val < 0x10 {
+            (val as i32) << 6
+        } else if val < 0x20 {
+            ((val as i32) - 8) << 7
+        } else {
+            ((val as i32) - 20) << 8
+        }
+    }
+    fn estimate_sid_gain(cur_gain: i32, sid_gain: i32) -> i32 {
+        const CNG_BSEG: [i32; 3] = [ 2048, 18432, 231233 ];
+        let shift = 16 - cur_gain * 2;
+        let t = if shift > 0 { sid_gain << shift } else { sid_gain >> -shift };
+        let x = t * 273 >> 16;
+        if x >= CNG_BSEG[2] {
+            return 63;
+        }
+        let (shift, seg) = if x >= CNG_BSEG[1] { (4, 3) } else { (3, if x >= CNG_BSEG[0] { 1 } else { 0 }) };
+        let seg2 = seg.min(3);
+
+        let mut val = 1 << shift;
+        let mut val_add = val >> 1;
+        for _ in 0..shift {
+            let t = seg * 32 + (val << seg2);
+            let t2 = t * t;
+            if x >= t2 {
+                val += val_add;
+            } else {
+                val -= val_add;
+            }
+            val_add >>= 1;
+        }
+
+        let t = seg * 32 + (val << seg2);
+        let y = t * t - x;
+        if y <= 0 {
+            let t = seg * 32 + ((val + 1) << seg2);
+            let t2 = t * t - x;
+            let val = (seg2 - 1) * 16 + val;
+            if t2 >= y {
+                val + 1
+            } else {
+                val
+            }
+        } else {
+            let t = seg * 32 + ((val - 1) << seg2);
+            let t2 = t * t - x;
+            let val = (seg2 - 1) * 16 + val;
+            if t2 >= y {
+                val - 1
+            } else {
+                val
+            }
+        }
+    }
+    fn synth_frame_other(&mut self) {
+        if self.cur_ftype == G7231FrameType::SID {
+            self.sid_gain = Self::amp_index_to_sid_gain(self.subframe[0].amp_index);
+            Self::inverse_quant(&self.prev_lsp, &mut self.sid_lsp, &self.lsp_index, false);
+        } else if self.prev_ftype == G7231FrameType::Active {
+            self.sid_gain = Self::estimate_sid_gain(self.cur_gain, self.sid_gain);
+        }
+        if self.prev_ftype == G7231FrameType::Active {
+            self.cur_gain = self.sid_gain;
+        } else {
+            self.cur_gain = (self.cur_gain * 7 + self.sid_gain) >> 3;
+        }
+        self.generate_noise();
+        Self::interpolate_lsp(&mut self.lpc, &self.sid_lsp, &self.prev_lsp);
+        self.prev_lsp = self.sid_lsp;
+    }
+    fn generate_noise(&mut self) {
+        const ADAPTIVE_LAG: [usize; SUBFRAMES] = [1, 0, 1, 3];
+        self.subframe[0].pitch_lag = self.cng_rnd.next_range(21) + 123;
+        self.subframe[1].pitch_lag = self.subframe[0].pitch_lag;
+        self.subframe[2].pitch_lag = self.cng_rnd.next_range(19) + 123;
+        self.subframe[3].pitch_lag = self.subframe[2].pitch_lag;
+
+        for (i, subframe) in self.subframe.iter_mut().enumerate() {
+            subframe.acb_gain = self.cng_rnd.next_range(50) + 1;
+            subframe.acb_lag  = ADAPTIVE_LAG[i];
+        }
+
+        let mut off = [0; 4];
+        let mut signs = [[0; 11]; 2];
+        for i in (0..4).step_by(2) {
+            let t = self.cng_rnd.next_range(1 << 13);
+            off[i]     = t & 1;
+            off[i + 1] = ((t >> 1) & 1) + SUBFRAME_LEN;
+            for j in 0..11 {
+                signs[i/2][j] = ((((t >> (j + 2)) & 1) * 2 - 1) << 14) as i32;
+            }
+        }
+
+        let mut pos = [0; 11 * SUBFRAMES];
+        let mut pidx = 0;
+        let mut tmp = [0; SUBFRAME_LEN / 2];
+        for i in 0..SUBFRAMES {
+            let npulses = if (i & 1) == 0 { 6 } else { 5 };
+            for j in 0..npulses {
+                let idx = self.cng_rnd.next_range(SUBFRAME_LEN / 2 - j);
+                pos[pidx] = tmp[idx] * 2 + off[i];
+                pidx += 1;
+                tmp[idx] = tmp[SUBFRAME_LEN / 2 - 1 - j];
+            }
+        }
+
+        (&mut self.synth_buf[LPC_ORDER..][..MAX_PITCH]).copy_from_slice(&self.prev_excitation);
+        let mut acb_vec = [0; SUBFRAME_LEN];
+        let mut tmp = [0; SUBFRAME_LEN * 2];
+        for i in (0..SUBFRAMES).step_by(2) {
+            let buf = &mut self.synth_buf[LPC_ORDER + SUBFRAME_LEN * i..];
+            self.subframe[i].gen_acb_excitation(&mut acb_vec, buf, self.is_6300);
+            (&mut buf[..SUBFRAME_LEN]).copy_from_slice(&acb_vec);
+            self.subframe[i + 1].gen_acb_excitation(&mut acb_vec, &buf[SUBFRAME_LEN..], self.is_6300);
+            (&mut buf[SUBFRAME_LEN..][..SUBFRAME_LEN]).copy_from_slice(&acb_vec);
+
+            let mut max = 0;
+            for j in 0..SUBFRAME_LEN*2 {
+                max |= i32::from(buf[j]).abs();
+            }
+            let shift = if max == 0 { 0 } else {
+                    (-10 + (32 - max.min(0x7FFF).leading_zeros()) as i32).max(-2)
+                };
+            let mut sum = 0;
+            if shift < 0 {
+                for j in 0..SUBFRAME_LEN*2 {
+                    let val = buf[j] << -shift;
+                    sum += i64::from(val) * i64::from(val);
+                    tmp[j] = val;
+                }
+            } else {
+                for j in 0..SUBFRAME_LEN*2 {
+                    let val = buf[j] >> shift;
+                    sum += i64::from(val) * i64::from(val);
+                    tmp[j] = val;
+                }
+            }
+
+            let mut b0 = 0;
+            for j in 0..11 {
+                b0 += i32::from(tmp[pos[i / 2 * 11 + j]]) * signs[i / 2][j];
+            }
+            b0 = ((i64::from(b0) * 2 * 2979 + (1 << 29)) >> 30) as i32;
+
+            let mut c = self.cur_gain * (self.cur_gain * (SUBFRAME_LEN as i32) >> 5);
+            if shift * 2 + 3 >= 0 {
+                c >>= shift * 2 + 3;
+            } else {
+                c <<= -(shift * 2 + 3);
+            }
+            c = (i64::from(clip32(sum * 2) - c) * 2979 >> 15) as i32;
+
+            let delta = b0 * b0 * 2 - c;
+            let x = if delta <= 0 {
+                    -b0
+                } else {
+                    let d0 = i32::from(square_root_i32(delta));
+                    let x0 = d0 - b0;
+                    let t  = d0 + b0;
+                    if t.abs() < x0.abs() {
+                        -t
+                    } else {
+                        x0
+                    }
+                };
+            let shift = shift + 1;
+            let x = (if shift >= 0 { x << shift } else { x >> -shift }).min(10000).max(-10000);
+            for j in 0..11 {
+                let val = x * signs[i / 2][j] >> 15;
+                buf[pos[i / 2 * 11 + j]] = buf[pos[i / 2 * 11 + j]].saturating_add(val as i16);
+            }
+
+            for j in 0..SUBFRAME_LEN*2 {
+                buf[MAX_PITCH + j] = buf[j];
+            }
+        }
+        self.prev_excitation.copy_from_slice(&self.synth_buf[LPC_ORDER + SAMPLES..][..MAX_PITCH]);
+    }
+    fn compute_interp_index(&mut self) {
+        let pitch_lag = self.subframe[3].pitch_lag;
+
+        self.cur_gain = Self::scale_vector(&mut self.synth_buf[LPC_ORDER..][..SAMPLES + MAX_PITCH], &self.excitation[..SAMPLES + MAX_PITCH]);
+
+        let (pos, cre) = autocorr_max(&self.synth_buf[LPC_ORDER..], MAX_PITCH + SUBFRAME_LEN * 2, SUBFRAME_LEN * 2, pitch_lag, false);
+        let corr_energy = cre.saturating_add(1 << 15) >> 16;
+
+        let tgt_energy = dot_product(&self.synth_buf[LPC_ORDER + MAX_PITCH + SUBFRAME_LEN * 2..], &self.synth_buf[LPC_ORDER + MAX_PITCH + SUBFRAME_LEN * 2..], SUBFRAME_LEN * 2);
+        self.sid_gain = tgt_energy.saturating_add(1 << 15) >> 16;
+
+        if corr_energy <= 0 {
+            self.interp_index = 0;
+            return;
+        }
+
+        let best_energy = dot_product(&self.synth_buf[LPC_ORDER + MAX_PITCH + SUBFRAME_LEN * 2 - pos..], &self.synth_buf[LPC_ORDER + MAX_PITCH + SUBFRAME_LEN * 2 - pos..], SUBFRAME_LEN * 2);
+        let best_energy = best_energy.saturating_add(1 << 15) >> 16;
+
+        let tmp = best_energy * self.sid_gain >> 3;
+
+        if tmp < corr_energy * corr_energy {
+            self.interp_index = pos;
+        } else {
+            self.interp_index = 0;
+        }
+    }
+    fn scale_vector(dst: &mut [i16], src: &[i16]) -> i32 {
+        let mut max = 0;
+        for el in src.iter() {
+            max = max.max(i32::from(*el).abs());
+        }
+        max = max.min(0x7FFF);
+        let shift = norm_bits(max, 15);
+        for (dst, src) in dst.iter_mut().zip(src.iter()) {
+            *dst = *src << shift >> 3;
+        }
+        i32::from(shift) - 3
+    }
+    fn inverse_quant(prev_lsp: &[i16; LPC_ORDER], lsp: &mut [i16; LPC_ORDER], lsp_index: &[usize; 3], bad_frame: bool) {
+        let (min_dist, pred) = if !bad_frame { (0x100, 12288) } else { (0x200, 23552) };
+        lsp[0] = LSP_CODEBOOK0[lsp_index[0]][0];
+        lsp[1] = LSP_CODEBOOK0[lsp_index[0]][1];
+        lsp[2] = LSP_CODEBOOK0[lsp_index[0]][2];
+        lsp[3] = LSP_CODEBOOK1[lsp_index[1]][0];
+        lsp[4] = LSP_CODEBOOK1[lsp_index[1]][1];
+        lsp[5] = LSP_CODEBOOK1[lsp_index[1]][2];
+        lsp[6] = LSP_CODEBOOK2[lsp_index[2]][0];
+        lsp[7] = LSP_CODEBOOK2[lsp_index[2]][1];
+        lsp[8] = LSP_CODEBOOK2[lsp_index[2]][2];
+        lsp[9] = LSP_CODEBOOK2[lsp_index[2]][3];
+
+        for i in 0..LPC_ORDER {
+            let diff = ((i32::from(prev_lsp[i]) - i32::from(DC_LSP[i])) * pred + (1 << 14)) >> 15;
+            lsp[i] = (i32::from(lsp[i]) + i32::from(DC_LSP[i]) + diff) as i16;
+        }
+
+        let mut stable = false;
+
+        for _ in 0..LPC_ORDER {
+            lsp[0] = lsp[0].max(0x100);
+            lsp[LPC_ORDER - 1] = lsp[LPC_ORDER - 1].min(0x7E00);
+
+            for i in 1..LPC_ORDER {
+                let mut val = min_dist + lsp[i - 1] - lsp[i];
+                if val > 0 {
+                    val >>= 1;
+                    lsp[i - 1] -= val;
+                    lsp[i]     += val;
+                }
+            }
+            stable = true;
+            for i in 1..LPC_ORDER {
+                let val = lsp[i - 1] + min_dist - lsp[i] - 4;
+                if val > 0 {
+                    stable = false;
+                    break;
+                }
+            }
+            if stable {
+                break;
+            }
+        }
+        if !stable {
+            lsp.copy_from_slice(prev_lsp);
+        }
+    }
+    fn interpolate_lsp(lpc: &mut [[i16; LPC_ORDER]; SUBFRAMES], lsp: &[i16; LPC_ORDER], prev_lsp: &[i16; LPC_ORDER]) {
+        weighted_sum!(lpc[0], lsp, 0x1000, prev_lsp, 0x3000, 14);
+        weighted_sum!(lpc[1], lsp, 0x2000, prev_lsp, 0x2000, 14);
+        weighted_sum!(lpc[2], lsp, 0x3000, prev_lsp, 0x1000, 14);
+        lpc[3].copy_from_slice(lsp);
+        for clpc in lpc.iter_mut() {
+            Self::lsp2lpc(clpc);
+        }
+    }
+    fn lsp2lpc(lpc: &mut [i16; LPC_ORDER]) {
+        let mut tmp1 = [0; LPC_ORDER/2 + 1];
+        let mut tmp2 = [0; LPC_ORDER/2 + 1];
+
+        for lpc in lpc.iter_mut() {
+            let index  = ((*lpc >> 7) & 0x1FF) as usize;
+            let offset = (i32::from(*lpc & 0x7F) << 8) + 0x80;
+            let val1 = i32::from(COS_TAB[index]);
+            let val2 = (i32::from(COS_TAB[index + 1]) - val1) * offset * 2;
+            *lpc = -(((val1 << 17) + val2 * 2).saturating_add(1 << 15) >> 16) as i16;
+        }
+
+        tmp1[0] = 1 << 28;
+        tmp1[1] = (i32::from(lpc[0]) << 14) + (i32::from(lpc[2]) << 14);
+        tmp1[2] = i32::from(lpc[0]) * i32::from(lpc[2]) + (2 << 28);
+
+        tmp2[0] = 1 << 28;
+        tmp2[1] = (i32::from(lpc[1]) << 14) + (i32::from(lpc[3]) << 14);
+        tmp2[2] = i32::from(lpc[1]) * i32::from(lpc[3]) + (2 << 28);
+
+        for i in 2..LPC_ORDER/2 {
+            tmp1[i + 1] = tmp1[i - 1] + mul16(tmp1[i], lpc[i * 2]);
+            tmp2[i + 1] = tmp2[i - 1] + mul16(tmp2[i], lpc[i * 2 + 1]);
+
+            for j in (2..=i).rev() {
+                tmp1[j] = mul16(tmp1[j - 1], lpc[i * 2])     + (tmp1[j] >> 1) + (tmp1[j - 2] >> 1);
+                tmp2[j] = mul16(tmp2[j - 1], lpc[i * 2 + 1]) + (tmp2[j] >> 1) + (tmp2[j - 2] >> 1);
+            }
+
+            tmp1[0] >>= 1;
+            tmp2[0] >>= 1;
+            tmp1[1] = (tmp1[1] + (i32::from(lpc[2 * i])     << 16 >> i)) >> 1;
+            tmp2[1] = (tmp2[1] + (i32::from(lpc[2 * i + 1]) << 16 >> i)) >> 1;
+        }
+
+        for i in 0..LPC_ORDER/2 {
+            let c0 = i64::from(tmp1[i + 1]) + i64::from(tmp1[i]);
+            let c1 = i64::from(tmp2[i + 1]) - i64::from(tmp2[i]);
+            lpc[i]                 = (clip32((c0 + c1) * 8 + (1 << 15)) >> 16) as i16;
+            lpc[LPC_ORDER - i - 1] = (clip32((c0 - c1) * 8 + (1 << 15)) >> 16) as i16;
+        }
+    }
+    fn do_lpc(buf: &mut [i16], offset: usize, lpc: &[i16; LPC_ORDER]) {
+        for i in 0..SUBFRAME_LEN {
+            let mut sum = 0i32;
+            for j in 0..LPC_ORDER {
+                sum = sum.wrapping_sub(i32::from(buf[offset + i - j - 1]) * i32::from(lpc[j]));
+            }
+            let pred = (sum + (1 << 12)) >> 12;
+            buf[offset + i] = clip16((i32::from(buf[offset + i]) + pred) >> 1);
+        }
+    }
+    fn formant_postfilter(&mut self, dst: &mut [i16]) {
+        (&mut self.synth_buf[..LPC_ORDER]).copy_from_slice(&self.fir_mem);
+        let mut filter_data = [0; LPC_ORDER + SAMPLES];
+        (&mut filter_data[..LPC_ORDER]).copy_from_slice(&self.iir_mem);
+
+        let mut filter_coef = [[0; LPC_ORDER]; 2];
+        for i in 0..SUBFRAMES {
+            for j in 0..LPC_ORDER {
+                filter_coef[0][j] = (i32::from(-self.lpc[i][j]) * i32::from(POSTFILTER_COEFFS[0][j]) + (1 << 14)) >> 15;
+                filter_coef[1][j] = (i32::from(-self.lpc[i][j]) * i32::from(POSTFILTER_COEFFS[1][j]) + (1 << 14)) >> 15;
+            }
+            Self::iir_filter(&filter_coef, &self.synth_buf, &mut filter_data, LPC_ORDER + i * SUBFRAME_LEN);
+        }
+        self.fir_mem.copy_from_slice(&self.synth_buf[SAMPLES..][..LPC_ORDER]);
+        self.iir_mem.copy_from_slice(&filter_data[SAMPLES..]);
+        let mut offset = 0;
+        for _ in 0..SUBFRAMES {
+            let scale = Self::scale_vector(&mut dst[offset..][..SUBFRAME_LEN], &self.synth_buf[offset + LPC_ORDER..][..SUBFRAME_LEN]);
+            let ac1 = dot_product(&dst[offset..], &dst[offset + 1..], SUBFRAME_LEN - 1);
+            let ac0 = dot_product(&dst[offset..], &dst[offset..], SUBFRAME_LEN);
+            let tmp = if (ac0 >> 16) != 0 { (ac1 >> 2) / (ac0 >> 16) } else { 0 };
+            self.reflection_coef = (3 * self.reflection_coef + tmp + 2) >> 2;
+            let gain = (-self.reflection_coef >> 1) & !3;
+
+            for i in 0..SUBFRAME_LEN {
+                let val = (filter_data[offset + LPC_ORDER + i - 1] >> 16) * gain;
+                dst[offset + i] = (filter_data[offset + LPC_ORDER + i].saturating_add(val).saturating_add(val) >> 16) as i16;
+            }
+
+            let shift = 2 * scale + 4;
+            let energy = if shift < 0 { clip32(i64::from(ac0) << -shift) } else { ac0 >> shift };
+            Self::gain_scale(&mut self.pf_gain, &mut dst[offset..], energy);
+
+            offset += SUBFRAME_LEN;
+        }
+    }
+    fn iir_filter(coef: &[[i32; LPC_ORDER]; 2], src: &[i16], dst: &mut [i32], offset: usize) {
+        for i in 0..SUBFRAME_LEN {
+            let mut sum = 0;
+            for j in 0..LPC_ORDER {
+                sum -= i64::from(coef[0][j]) * i64::from(src[offset - 1 + i - j]) -
+                       i64::from(coef[1][j]) * i64::from(dst[offset - 1 + i - j] >> 16);
+            }
+            dst[offset + i] = clip32((i64::from(src[offset + i]) << 16) + (sum << 3) + (1 << 15));
+        }
+    }
+    fn gain_scale(pf_gain: &mut i32, buf: &mut [i16], energy: i32) {
+        let mut den = 0i32;
+        for i in 0..SUBFRAME_LEN {
+            let val = i32::from(buf[i] >> 2);
+            den = den.saturating_add(val * val).saturating_add(val * val);
+        }
+        let mut num = energy;
+        let gain;
+        if (num != 0) && (den != 0) {
+            let bits1 = norm_bits(num, 31);
+            let bits2 = norm_bits(den, 31);
+            num = num << bits1 >> 1;
+            den <<= bits2;
+            let shift = (5 + bits1 - bits2).max(0);
+
+            gain = square_root_i32(((num >> 1) / (den >> 16)) << 16 >> shift);
+        } else {
+            gain = 1 << 12;
+        }
+
+        for i in 0..SUBFRAME_LEN {
+            *pf_gain = (15 * *pf_gain + gain + (1 << 3)) >> 4;
+            buf[i] = clip16((i32::from(buf[i]) * (*pf_gain + (*pf_gain >> 4)) + (1 << 10)) >> 11);
+        }
+    }
+}
+
+fn clip16(a: i32) -> i16 {
+    a.min(i32::from(std::i16::MAX)).max(i32::from(std::i16::MIN)) as i16
+}
+
+fn clip32(a: i64) -> i32 {
+    a.min(i64::from(std::i32::MAX)).max(i64::from(std::i32::MIN)) as i32
+}
+
+fn mul16(a: i32, b: i16) -> i32 {
+    let b = i32::from(b);
+    (a >> 16) * b * 2 + (((a & 0xFFFF) * b) >> 15)
+}
+
+fn norm_bits(val: i32, target: u8) -> u8 {
+    if val == 0 {
+        target
+    } else {
+        target - (32 - val.leading_zeros()) as u8
+    }
+}
+
+fn autocorr_max(src: &[i16], offset: usize, length: usize, pitch_lag: usize, forward: bool) -> (usize, i32) {
+    let pitch_lag = pitch_lag.min(MAX_PITCH - 3);
+    let mut max_energy = 0;
+    let mut lag = 0;
+
+    if forward {
+        let end = (pitch_lag + 3).min(SAMPLES + MAX_PITCH - offset - length);
+        for i in pitch_lag-3..=end {
+            let energy = dot_product(&src[offset..], &src[offset + i..], length);
+            if max_energy < energy {
+                max_energy = energy;
+                lag = i;
+            }
+        }
+    } else {
+        for i in pitch_lag-3..=pitch_lag+3 {
+            let energy = dot_product(&src[offset..], &src[offset - i..], length);
+            if max_energy < energy {
+                max_energy = energy;
+                lag = i;
+            }
+        }
+    }
+    (lag, max_energy)
+}
+
+fn dot_product(src1: &[i16], src2: &[i16], length: usize) -> i32 {
+    let mut sum = 0;
+    for (a, b) in src1.iter().zip(src2.iter()).take(length) {
+        sum += i64::from(*a) * i64::from(*b)
+    }
+    clip32(sum << 1)
+}
+
+impl NADecoder for G7231Decoder {
+    fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+        if let NACodecTypeInfo::Audio(_ainfo) = info.get_properties() {
+            self.info = info.replace_info(NACodecTypeInfo::Audio(self.ainfo));
+            Ok(())
+        } else {
+            Err(DecoderError::InvalidData)
+        }
+    }
+    fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+        let info = pkt.get_stream().get_info();
+        validate!(info.get_properties().is_audio());
+        let src = pkt.get_buffer();
+
+        let mut bad_frame = false;
+
+        let mut br = BitReader::new(src.as_slice(), BitReaderMode::LE);
+        if let Err(_) = self.unpack_frame(&mut br) {
+            bad_frame = true;
+            self.cur_ftype = if self.prev_ftype == G7231FrameType::Active {
+                    G7231FrameType::Active
+                } else {
+                    G7231FrameType::Untransmitted
+                };
+        }
+
+        let abuf = alloc_audio_buffer(self.ainfo, SAMPLES, self.chmap.clone())?;
+        let mut adata = abuf.get_abuf_i16().unwrap();
+        let mut asamples = adata.get_data_mut().unwrap();
+        if self.cur_ftype == G7231FrameType::Active {
+            self.synth_frame_active(&mut asamples, bad_frame);
+        } else {
+            self.synth_frame_other();
+        }
+        self.prev_ftype = self.cur_ftype;
+        (&mut self.synth_buf[..LPC_ORDER]).copy_from_slice(&self.filt_mem);
+        for i in 0..SUBFRAMES {
+            Self::do_lpc(&mut self.synth_buf, LPC_ORDER + i * SUBFRAME_LEN, &self.lpc[i]);
+        }
+        self.filt_mem.copy_from_slice(&self.synth_buf[SAMPLES..][..LPC_ORDER]);
+        self.formant_postfilter(asamples);
+
+        let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), abuf);
+        frm.set_keyframe(true);
+        Ok(frm.into_ref())
+    }
+    fn flush(&mut self) {
+    }
+}
+
+pub fn get_decoder() -> Box<dyn NADecoder + Send> {
+    Box::new(G7231Decoder::new())
+}
+
+#[cfg(test)]
+mod test {
+    use nihav_core::codecs::RegisteredDecoders;
+    use nihav_core::demuxers::RegisteredDemuxers;
+    use nihav_codec_support::test::dec_video::*;
+    use crate::vivo_register_all_codecs;
+    use crate::vivo_register_all_demuxers;
+    #[test]
+    fn test_g723_1() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        vivo_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        vivo_register_all_codecs(&mut dec_reg);
+
+        let file = "assets/Misc/adalogo.viv";
+        //let file = "assets/Misc/gr_al.viv";
+        //test_decode_audio("vivo", file, Some(1500), None/*Some("g7231")*/, &dmx_reg, &dec_reg);
+        test_decoding("vivo", "g723.1", file, None, &dmx_reg, &dec_reg,
+                      ExpectedTestResult::MD5([0x94e60e2f, 0x578493e7, 0xe7ab1f1c, 0xae155977]));
+    }
+}
+
+const DC_LSP: [i16; LPC_ORDER] = [
+    0x0c3b, 0x1271, 0x1e0a, 0x2a36, 0x3630,
+    0x406f, 0x4d28, 0x56f4, 0x638c, 0x6c46
+];
+
+const LSP_CODEBOOK0: [[i16; 3]; 256] = [
+    [    0,      0,      0], [ -270,  -1372,  -1032], [ -541,  -1650,  -1382],
+    [ -723,  -2011,  -2213], [ -941,  -1122,  -1942], [ -780,  -1145,  -2454],
+    [ -884,  -1309,  -1373], [-1051,  -1523,  -1766], [-1083,  -1622,  -2300],
+    [ -777,  -1377,  -2147], [ -935,  -1467,  -2763], [ -802,  -1327,  -3471],
+    [ -935,  -1959,  -3999], [ -240,    -89,    222], [ -661,   -257,   -160],
+    [ -994,   -466,   -419], [ -188,   -164,   -278], [ -342,   -512,   -415],
+    [ -607,   -511,   -797], [   16,     19,   -716], [  374,    425,   -972],
+    [ -346,    245,   -282], [ -265,    506,   -754], [ -620,   -147,   1955],
+    [ -742,   -860,   2597], [ -150,   -352,   2704], [  305,    880,   1954],
+    [  123,    731,   2766], [ -348,    765,   3327], [  618,    221,   3258],
+    [ -178,    -47,   4219], [  393,   1304,   3842], [  698,   1702,   4801],
+    [   63,   -584,   1229], [ -215,   -732,   1704], [  172,   -335,   1909],
+    [   -2,    216,   1797], [  353,    127,   2205], [-1208,    188,     11],
+    [ -513,    -75,   -683], [ -973,    222,   -646], [ -616,   -843,   -388],
+    [ -950,  -1113,   -359], [-1431,   -623,   -705], [-1398,  -1063,   -178],
+    [  -45,   -461,     35], [   -9,   -657,   -216], [  127,  -1078,     95],
+    [ -950,  -1156,    584], [-1480,  -1494,    449], [ -120,   -705,    516],
+    [ -368,   -961,    727], [ -378,   -526,    973], [ -793,   -614,    676],
+    [ -801,   -755,   1287], [-1476,   -340,   1636], [ -505,  -1254,   1543],
+    [-1243,  -1622,   1532], [ -776,  -1477,   -655], [-1151,  -1296,   -823],
+    [-1153,  -1672,  -1124], [-1291,  -2003,  -1702], [ -622,  -1283,     57],
+    [ -471,  -1611,    509], [-1060,  -1570,   -139], [ -873,  -2156,   -536],
+    [-1716,  -2021,   -364], [-2150,  -3218,  -1291], [-1248,  -1945,  -2904],
+    [-1215,  -2633,  -2855], [  167,   -244,     84], [  349,   -412,   -217],
+    [  -40,   -352,    632], [  227,   -529,    405], [   68,   -383,   -443],
+    [  167,   -558,   -706], [ -275,   -854,    -14], [ -351,  -1089,   -449],
+    [  341,    -72,   -289], [  603,   -106,   -474], [  322,   -219,   -649],
+    [  179,   -317,   -998], [  450,   -291,   -996], [  555,    195,   -525],
+    [  784,    272,   -831], [ -148,   -384,   -849], [   82,   -536,  -1357],
+    [  238,   -172,  -1354], [  422,   -268,  -1841], [  297,   -737,  -2079],
+    [ -111,   -801,   -598], [    1,   -668,   -984], [ -131,   -818,  -1299],
+    [ -329,   -521,  -1310], [ -151,   -778,  -1834], [  -93,   -352,  -1746],
+    [ -568,   -640,  -1821], [ -509,   -941,  -2183], [  464,   -815,  -1250],
+    [   79,  -1133,  -1597], [ -184,  -1353,  -2123], [ -196,   -410,  -2427],
+    [ -192,   -833,  -2810], [ -259,  -1382,  -3045], [ -217,      4,  -1166],
+    [ -800,   -325,  -1219], [ -363,   -830,   -898], [ -661,  -1134,   -960],
+    [ -386,   -980,  -1501], [ -627,  -1159,  -1722], [ -903,   -829,   -855],
+    [ -685,   -829,  -1313], [-1065,   -959,  -1405], [  441,     25,   -847],
+    [  655,    -27,  -1181], [ 1159,   -110,   -705], [  856,    253,  -1671],
+    [  415,    404,     -1], [  322,    903,   -398], [  670,    499,   -292],
+    [  803,    591,   -610], [ 1144,    591,   -814], [  717,    183,    393],
+    [  857,    381,    106], [  609,     62,    -27], [  792,    198,   -325],
+    [  735,    805,     88], [ 1142,    812,     78], [ 1028,    366,   -292],
+    [ 1309,    743,   -237], [ 1615,    589,    -79], [ 1010,    639,   -243],
+    [  999,    964,   -311], [ 1500,   1137,   -615], [  988,    357,    646],
+    [ 1227,    667,    683], [ 1164,   1565,    894], [ 1392,   2015,    477],
+    [ 1138,    533,    250], [ 1437,    896,    391], [ 1765,   1118,     99],
+    [ 1112,   1090,    802], [ 1596,    846,   1134], [  937,   1161,    279],
+    [ 1719,   1254,    683], [ 1338,   1086,     35], [ 1419,   1324,    428],
+    [ 1428,   1524,     40], [ 2108,   1594,     89], [ 1015,    544,   1222],
+    [ 1121,    925,   1263], [ 1030,   1318,   1485], [ 1295,    789,   1817],
+    [ 1323,   1272,   1909], [ 1724,   1237,   1803], [ 1797,   1689,    858],
+    [ 2149,   1367,   1301], [ 2302,   1867,    761], [ 2863,   2351,   1053],
+    [   52,    163,    -76], [  230,    309,   -492], [  -71,    619,     39],
+    [ -218,    856,    499], [ -654,    736,   -207], [ -535,   1259,    155],
+    [ -480,   1476,    643], [  262,   1081,    102], [  309,   1592,   -182],
+    [  627,   1629,    534], [  337,    643,    456], [  758,    670,    713],
+    [  202,   1126,    658], [  612,   1131,    666], [  686,   1223,   1136],
+    [ -131,    377,    525], [   42,    708,    907], [   87,   1488,   1035],
+    [  432,   2117,    904], [  137,    981,   1332], [ -447,   1014,   1136],
+    [ -839,   1793,   1246], [ -559,    297,    198], [ -850,    685,    446],
+    [-1273,    632,    826], [ -401,   -544,    173], [ -753,   -793,    144],
+    [ -436,     -9,    772], [ -115,   -243,   1310], [ -670,   -269,    374],
+    [-1027,    -13,    639], [ -887,    -81,   1137], [-1277,   -455,    158],
+    [-1411,   -720,    736], [  172,     88,    403], [  386,    255,    756],
+    [ -500,    522,    910], [ -958,    659,   1388], [ -395,    301,   1344],
+    [ -356,    768,   1813], [ -613,    841,   2419], [  445,   -122,    252],
+    [  629,    -87,    723], [  283,   -253,    870], [  456,   -116,   1381],
+    [  757,    180,   1059], [  532,    408,   1509], [  947,    288,   1806],
+    [ 1325,    994,   2524], [  892,   1219,   3023], [ 1397,   1596,   3406],
+    [ 1143,   1552,   2546], [ 1850,   1433,   2710], [  -10,    134,   1002],
+    [  154,    499,   1323], [  508,    792,   1117], [  509,   1340,   1616],
+    [  762,    862,   1608], [  787,    740,   2320], [  794,   1727,   1283],
+    [  465,   2108,   1660], [ -120,   1451,   1613], [ -386,   2016,   2169],
+    [  891,   1225,   2050], [  456,   1480,   2185], [ 1493,   1283,   1209],
+    [ 1397,   1636,   1518], [ 1776,   1738,   1552], [ 1572,   1698,   2141],
+    [ 1389,   2126,   1271], [ 1959,   2413,   1119], [ 1365,   2892,   1505],
+    [ 2206,   1971,   1623], [ 2076,   1950,   2280], [ 1717,   2291,   1867],
+    [ 2366,   2515,   1953], [ 2865,   2838,   2522], [ 2535,   3465,   2011],
+    [ 3381,   4127,   2638], [  836,   2667,   2289], [ 1761,   2773,   2337],
+    [ 1415,   3325,   2911], [ 2354,   3138,   3126], [ 2659,   4192,   4010],
+    [ 1048,   1786,   1818], [ 1242,   2111,   2240], [ 1512,   2079,   2780],
+    [ 1573,   2491,   3138], [ 2230,   2377,   2782], [  416,   1773,   2704],
+    [  725,   2336,   3297], [ 1252,   2373,   3978], [ 2094,   2268,   3568],
+    [ 2011,   2712,   4528], [ 1341,   3507,   3876], [ 1216,   3919,   4922],
+    [ 1693,   4793,   6012]
+];
+
+const LSP_CODEBOOK1: [[i16; 3]; 256] = [
+    [    0,      0,      0], [-2114,  -1302,     76], [-2652,  -1278,  -1368],
+    [-2847,   -828,   -349], [-3812,  -2190,   -349], [-3946,   -364,   -449],
+    [-2725,  -4492,  -3607], [-3495,  -4764,  -1744], [  -51,   -756,     84],
+    [ -153,  -1191,    504], [  108,  -1418,   1167], [ -835,   -896,    390],
+    [ -569,  -1702,     87], [-1151,  -1818,    933], [-1826,  -2547,    411],
+    [-1842,  -1818,   1451], [-2438,  -1611,    781], [-2747,  -2477,   1311],
+    [ -940,   1252,    477], [-1629,   1688,    602], [-1202,    617,    280],
+    [-1737,    393,    580], [-1528,   1077,   1199], [-2165,   -161,   1408],
+    [-2504,  -1087,   2371], [-3458,   -175,   1395], [-1397,    -98,   -843],
+    [-2252,   -177,  -1149], [-1489,   -726,  -1283], [-1558,   -265,  -1744],
+    [-1867,   -821,  -1897], [-2062,  -1516,  -2340], [-2595,  -1142,  -2861],
+    [  170,     46,   -819], [ -193,   -204,  -1151], [  326,   -196,  -1532],
+    [  780,    329,   -816], [  201,    369,  -1243], [  650,   -209,  -1060],
+    [ 1144,    -15,  -1216], [ 1203,   -259,  -1867], [ -890,   -564,  -1430],
+    [ -638,   -852,  -1921], [  177,   -739,  -1358], [ -261,   -526,  -1666],
+    [  206,   -407,  -2255], [  338,   -526,   -822], [  421,  -1095,  -1009],
+    [  765,   -607,  -1408], [  825,  -1295,  -2004], [  357,   -905,  -1815],
+    [  -58,  -1248,  -1588], [ -596,  -1436,  -2046], [  -73,  -1159,  -2116],
+    [ -115,  -1382,  -2581], [ -160,  -1723,  -1952], [   -6,  -2196,  -2954],
+    [ -649,  -1705,  -2603], [ -617,  -1453,  -3282], [ -949,  -2019,  -3102],
+    [ -812,   1544,   1937], [-1854,    574,   2000], [-1463,   1140,   2649],
+    [-2683,   1748,   1452], [-2486,   2241,   2523], [  783,   1910,   1435],
+    [  581,   2682,   1376], [  236,   2197,   1885], [ -453,   2943,   2057],
+    [ -682,   2178,   2565], [-1342,   3201,   3328], [ -288,   -184,    262],
+    [  121,   -149,   -183], [  758,   -412,    206], [ 1038,   -204,    853],
+    [ 1577,   -457,    700], [  937,   -640,   -567], [ 1508,   -528,  -1024],
+    [ -225,   -527,   -427], [ -564,  -1095,   -332], [ -742,   -353,   -186],
+    [-1288,   -459,     84], [-1853,   -484,   -274], [-1554,   -731,    825],
+    [-2425,   -234,    382], [-1722,    293,   -271], [-2515,    425,   -564],
+    [-2599,    818,    464], [ -358,    118,   -375], [ -613,    198,   -874],
+    [ -690,    683,   -324], [-1352,   1155,   -168], [-1093,    129,   -324],
+    [-1184,    611,   -858], [  433,    386,   -372], [ -120,    486,   -634],
+    [  234,    851,   -631], [  602,    128,     46], [ 1099,    410,    159],
+    [  715,   -145,   -424], [ 1198,    -85,   -593], [ 1390,    367,   -358],
+    [ 1683,    362,   -964], [ 1711,    622,     45], [ 2033,    833,   -383],
+    [ 2890,    549,   -506], [    7,    401,     52], [   72,    811,    415],
+    [  566,    668,     41], [  467,   1218,    130], [   68,    957,   -187],
+    [  -25,   1649,   -103], [ -661,    260,    214], [ -925,    -94,    612],
+    [ -321,   -422,    965], [ -788,   -672,   1783], [  400,   -673,    779],
+    [  741,   -595,   1635], [ -161,    307,    657], [ -382,    836,    871],
+    [ -814,    400,   1223], [  364,    606,   1247], [   57,     75,   1571],
+    [  151,    471,   2287], [  -81,   1021,   1502], [  227,   1470,   1097],
+    [  658,   1275,   1653], [  664,   1478,   2377], [  263,   -127,    444],
+    [  264,     89,    969], [  794,    171,    576], [  821,    186,   1226],
+    [  404,    462,    517], [  339,    918,    794], [ 1280,   1423,    196],
+    [ 1453,   2019,    365], [ 1615,   1481,    672], [ 2394,   1708,    508],
+    [  806,   1238,    573], [  713,   1158,   1078], [ 1285,   1436,   1232],
+    [ 1790,   1188,   1141], [  765,    643,    864], [ 1032,    797,   1279],
+    [  900,    563,   1827], [ 1514,    673,   2312], [ 1544,   1129,   3240],
+    [ 1469,   1050,   1594], [ 1945,   1318,   1988], [ 2397,   2026,   2060],
+    [ 3538,   2057,   2620], [ 1249,   -118,     74], [ 1727,    194,    421],
+    [ 2078,    -50,   -463], [  970,    688,   -432], [ 1149,    952,   -110],
+    [ 1254,   1275,   -651], [ 1386,    929,    401], [ 1960,   1167,    232],
+    [  407,   -752,   -243], [  859,  -1118,    172], [ -227,   -860,   -992],
+    [ -796,  -1175,  -1380], [    8,  -1282,   -388], [  353,  -1781,  -1037],
+    [ -732,   -397,   -807], [ -853,    -28,  -1342], [-1229,  -1207,  -1959],
+    [-1015,  -1125,  -2543], [-1452,  -1791,  -2725], [-1891,  -2416,  -3269],
+    [ -918,  -1629,   -783], [ -580,  -2155,   -698], [-1097,  -2364,    -96],
+    [-1387,  -1513,      7], [-1588,  -2076,   -664], [-1473,  -2740,   -784],
+    [-2378,  -3149,    -56], [-2856,  -2092,   -169], [-3391,  -3708,    316],
+    [-1176,   -890,   -614], [-1944,  -1061,   -800], [ -299,  -1517,  -1000],
+    [ -640,  -1850,  -1526], [-1454,  -1536,  -1233], [-1890,  -1955,  -1756],
+    [-1086,  -1921,  -2122], [ -750,  -2325,  -2260], [-1325,  -2413,  -2673],
+    [-1114,  -2542,  -3459], [-1341,  -2901,  -3963], [-1160,  -2226,  -1393],
+    [-1001,  -2772,  -1573], [-1594,  -2641,  -1978], [-1534,  -3046,  -2624],
+    [-2224,  -2196,   -675], [-2807,  -3054,  -1102], [-2008,  -2840,  -1186],
+    [-1980,  -3332,  -1695], [-1715,  -3562,   -505], [-2527,  -4000,  -1887],
+    [-2333,  -2734,  -2296], [-3440,  -2401,  -3211], [-2008,  -3528,  -3337],
+    [-2247,  -3291,  -4510], [ -475,    949,    155], [ -149,   1365,    545],
+    [ -757,   1644,   1083], [ -217,   2053,   1353], [-1433,   2301,   1462],
+    [  495,   1661,    529], [   10,   2037,    740], [ 2082,   1898,    978],
+    [ 2831,   2294,    911], [  842,    793,    420], [ 1223,   1023,    863],
+    [ 1237,    451,    780], [ 1744,    708,    822], [ 1533,    284,   1384],
+    [ 2135,    609,   1538], [ 2305,    626,    540], [ 2368,   1187,    955],
+    [ 2586,   1255,     -7], [ 3116,   1131,    726], [ 3431,   1730,    428],
+    [ 2734,   1648,   1307], [ 2988,   1231,   2010], [ 3523,   2024,   1488],
+    [ 1034,   1657,    871], [ 1206,   2163,   1036], [ 1807,   2372,   1233],
+    [ 1808,   1769,   1493], [ 1573,   2332,   1779], [ 1216,   1609,   1866],
+    [ 1480,   1898,   2513], [  465,   2708,   2776], [  771,   3638,   3338],
+    [ 1869,   2599,   2623], [ 2825,   2745,   2468], [ 2638,   2439,   1585],
+    [ 2094,   2970,   1308], [ 2022,   3057,   1999], [ 3428,   2912,   1816],
+    [ 4536,   2974,   2129], [ 1046,   2563,   2086], [ 1363,   3562,   2318],
+    [ 2511,   1891,   2984], [ 1866,   2306,   3986], [ 3272,   2924,   3682],
+    [ 3146,   3564,   2272], [ 3592,   3968,   2822], [ 2431,   3369,   3069],
+    [ 1931,   4709,   3090], [ 2629,   4220,   3986], [ 4639,   4056,   3664],
+    [ 4035,   5334,   4912]
+];
+
+const LSP_CODEBOOK2: [[i16; 4]; 256] = [
+    [    0,      0,      0,      0], [  601,    512,   -542,    334],
+    [  428,   1087,   -484,   -132], [  652,    622,   -391,   -572],
+    [  378,    799,    141,   -860], [ 1040,    409,    112,   -554],
+    [ 1123,    670,    -75,   -847], [ 1421,    494,   -315,  -1095],
+    [  787,   1001,    114,   -460], [  988,   1672,    216,   -681],
+    [ 1007,   1241,   -132,  -1247], [ 1073,    399,    186,     -5],
+    [ 1262,    193,   -694,   -129], [  325,    196,     51,   -641],
+    [  861,    -59,    350,   -458], [ 1261,    567,    586,   -346],
+    [ 1532,    885,    210,   -517], [ 2027,    937,    113,   -792],
+    [ 1383,   1064,    334,     38], [ 1964,   1468,    459,    133],
+    [ 2062,   1186,    -98,   -121], [ 2577,   1445,    506,   -373],
+    [ 2310,   1682,     -2,   -960], [ 2876,   1939,    765,    138],
+    [ 3581,   2360,    649,   -414], [  219,    176,   -398,   -309],
+    [  434,    -78,   -435,   -880], [ -344,    301,    265,   -552],
+    [ -915,    470,    657,   -380], [  419,   -432,   -163,   -453],
+    [  351,   -953,      8,   -562], [  789,    -43,     20,   -958],
+    [  302,   -594,   -352,  -1159], [ 1040,    108,   -668,   -924],
+    [ 1333,    210,  -1217,  -1663], [  483,    589,   -350,  -1140],
+    [ 1003,    824,   -802,  -1184], [  745,     58,   -589,  -1443],
+    [  346,    247,   -915,  -1683], [  270,    796,   -720,  -2043],
+    [ 1208,    722,   -222,   -193], [ 1486,   1180,   -412,   -672],
+    [ 1722,    179,    -69,   -521], [ 2047,    860,   -666,  -1410],
+    [ -146,    222,   -281,   -805], [ -189,     90,   -114,  -1307],
+    [ -152,   1086,   -241,   -764], [ -439,    733,   -601,  -1302],
+    [ -833,   -167,   -351,   -601], [ -856,   -422,   -411,  -1059],
+    [ -747,   -355,   -582,  -1644], [ -837,    210,   -916,  -1144],
+    [-1800,     32,   -878,  -1687], [  -48,    -23,  -1146,     52],
+    [ -350,   -409,  -1656,   -364], [  265,   -728,   -858,   -577],
+    [  458,   -247,  -1141,   -997], [  691,   -407,  -1988,  -1161],
+    [  -66,   -104,   -705,  -1249], [ -431,    -93,  -1191,  -1844],
+    [  203,   -732,  -1000,  -1693], [   10,   -832,  -1846,  -1819],
+    [  493,   -128,  -1436,  -1768], [  488,   -311,  -1730,  -2540],
+    [ -653,   -532,  -1150,  -1172], [-1086,   -289,  -1706,  -1533],
+    [ -699,  -1205,  -1216,  -1766], [-1032,  -1481,  -2074,  -1523],
+    [ -721,  -1220,  -2277,  -2600], [   12,   -539,  -1484,  -1131],
+    [  -40,   -911,  -2106,   -441], [ -471,   -484,  -2267,  -1549],
+    [ -141,   -988,  -3006,  -1721], [-1545,  -2102,   -583,    342],
+    [-1383,  -2772,   -386,    -13], [-2118,  -2589,  -1205,     72],
+    [-2147,  -3231,   -965,    390], [-2949,  -3300,   -621,    637],
+    [-3907,  -4138,   -865,    803], [-1287,   -845,   -375,   -548],
+    [-1416,  -1169,   -487,  -1277], [-1400,  -1690,  -1027,   -418],
+    [-2018,  -1909,  -1188,  -1260], [-1418,  -2222,  -2029,   -128],
+    [-2067,  -2998,  -2693,   -310], [ -950,  -1028,  -1538,    185],
+    [-1616,   -915,  -2205,   -549], [   19,   -821,  -1145,    352],
+    [  184,  -1175,  -1356,   -627], [ -547,  -1088,  -1661,   -911],
+    [ -216,  -1502,  -2197,   -948], [ -795,  -1306,  -2374,   -451],
+    [ -924,  -1889,  -2796,   -680], [ -600,  -1614,  -3609,   -885],
+    [-2392,  -2528,    319,    303], [-2908,  -2095,   -310,    573],
+    [-3460,  -2141,     49,   -113], [-2231,   -448,    675,   -146],
+    [-2805,   -532,   1231,    479], [-2684,   -486,   -200,    611],
+    [-3525,   -971,   -198,    704], [-3707,    173,    349,    254],
+    [-4734,  -1447,    -34,    880], [  777,   -512,    114,    -10],
+    [ 1250,    -66,    442,     -5], [  604,    613,    452,   -352],
+    [ 1224,    777,    675,  -1014], [-1372,    -79,  -1208,   -238],
+    [-2389,    -17,  -1157,   -818], [-1504,   -673,  -1133,  -1060],
+    [-1984,   -799,  -2005,  -1973], [-2037,   -798,  -1068,   -105],
+    [-3190,   -899,  -1817,   -194], [ -156,   -886,    394,   -318],
+    [ -258,  -1283,    551,    202], [ -536,  -1729,    910,    331],
+    [ -847,  -1109,    795,   -163], [-1171,  -1128,    715,    519],
+    [-1080,  -1319,   1685,    668], [-1000,  -1921,     96,    211],
+    [-1487,  -2148,    831,    174], [-1139,   -374,    414,     -4],
+    [-1517,  -1383,    396,   -352], [-1012,    439,    -59,   -967],
+    [-1812,    706,   -440,  -1030], [-1971,   -329,    -34,   -827],
+    [-2472,  -1588,   -151,   -606], [-2161,    374,   -281,     76],
+    [-3012,    231,    -15,   -690], [ 1104,    566,    721,    209],
+    [ 1685,    564,    383,     98], [ 1898,    750,    792,    -97],
+    [  556,    -64,    561,    -93], [  876,    162,    913,    -22],
+    [  961,    675,   1296,    140], [  756,   -396,    851,    544],
+    [  360,   -303,   1341,    396], [  878,    -22,   1464,    863],
+    [ -309,   -273,    642,   -129], [ -686,    -82,    842,    454],
+    [   -5,    -47,   1069,    998], [  -94,    967,   1277,    298],
+    [ -489,    385,   1473,    746], [ -369,   -717,   1333,    242],
+    [  281,   -993,   1726,    924], [  464,    601,   1575,   1376],
+    [ -250,    206,   2339,   1175], [ -438,    377,   -597,   -285],
+    [-1020,    787,   -790,   -287], [ -458,   -410,    215,    295],
+    [ -589,   -860,   -121,    797], [-1175,    122,   -437,    466],
+    [-1480,   -121,    367,    924], [  234,    323,    770,   -555],
+    [  145,     30,    996,     26], [   66,    849,     93,   -145],
+    [ -117,   1261,    474,   -399], [-1495,   1051,    218,   -506],
+    [-1390,    694,    994,     88], [  616,      7,     78,    304],
+    [ 1060,     52,    -62,    835], [  833,    454,    649,   1359],
+    [ -770,    464,     47,     93], [ -574,   1199,    -39,    379],
+    [  114,    -98,    488,    485], [  727,    244,    606,    696],
+    [  -76,    455,    671,    546], [ -565,    -13,    145,    819],
+    [ -376,    569,    448,   1128], [  218,    122,    265,   1167],
+    [  230,    738,    932,   1003], [  138,    477,     36,    450],
+    [  404,    787,    -73,   1000], [  497,   1259,    387,   1231],
+    [   17,    207,    195,    -79], [  562,    358,     53,   -158],
+    [  493,    387,    478,    189], [  678,    831,    640,    558],
+    [ -197,    523,    613,     57], [  429,    894,    769,    111],
+    [   67,   1174,    568,    511], [ 1242,    824,    251,    840],
+    [ 1419,   1074,    864,    481], [  924,   1474,    669,    724],
+    [ 1539,   1879,    654,   1590], [  445,    337,   1111,    541],
+    [  472,   1421,   1264,   1094], [  794,    735,   1103,    668],
+    [ 1055,    863,   1192,   1020], [  778,   1105,    806,   1798],
+    [ 1052,   1527,   1587,   2151], [  881,   1552,   1265,    391],
+    [  726,    872,   1812,    601], [ 1469,    280,   1008,    616],
+    [ 1403,    577,   1803,   1244], [ 1650,   1314,   1148,   1072],
+    [ 1297,   1669,   1911,   1026], [ 2093,   1044,   2115,   1189],
+    [ 1644,   1961,   2587,   1512], [   25,   -315,     -9,   -106],
+    [  290,   -339,    428,   -444], [  -68,   -783,    735,    772],
+    [  245,   -555,    468,     47], [  334,   -895,    814,    146],
+    [  235,    368,   -964,   -959], [ -203,    315,  -1566,  -1217],
+    [  801,     17,   -276,   -354], [  894,   -495,   -789,   -635],
+    [  716,    291,  -1189,   -357], [  560,   -260,   -733,     -2],
+    [  679,   -508,  -1429,    211], [  -51,    -62,   -428,    557],
+    [  322,   -638,   -211,    614], [ -878,  -1057,    -84,    -71],
+    [ -388,  -1415,   -167,   -318], [ -754,  -1574,    214,   -539],
+    [-1419,  -2004,    -92,   -787], [  -47,   -856,   -347,   -255],
+    [   23,  -1211,   -173,    320], [ -658,   -487,   -893,    353],
+    [ -783,  -1587,   -584,    507], [-1420,   -859,   -378,    441],
+    [-2095,  -1491,   -137,    439], [ -321,  -1450,  -1288,    -12],
+    [ -359,  -2113,   -553,     -8], [ -831,  -1918,  -1561,     32],
+    [-1014,  -2487,  -1359,   -939], [ -475,   -311,   -169,   -236],
+    [ -907,   -426,    276,   -611], [  -96,   -400,     50,   -710],
+    [ -426,  -1022,    -10,   -985], [ -197,   -258,   -744,   -575],
+    [ -611,   -930,   -771,   -394], [ -267,   -776,   -612,   -939],
+    [ -256,  -1346,   -802,  -1122], [ -796,  -1570,   -825,   -754],
+    [  712,    876,    141,    227], [  981,   1509,     85,    124],
+    [ 1462,   1228,    979,    -39], [ 1734,    999,   1481,    440],
+    [ 2293,   1116,    769,    440], [ 2504,   1480,   1241,    356],
+    [ 2474,   1909,   1558,    810], [  917,   1134,    607,   -134],
+    [  509,   1809,    781,   -123], [ 1712,   1506,    559,   -423],
+    [ 2037,   2317,    726,   -155], [ 3031,   2676,   1203,    331],
+    [ 3664,   3274,   1768,    531], [ 1610,   1839,    867,    183],
+    [ 1774,   1972,   1538,     97], [ 1822,   2158,   1282,    659],
+    [ 2222,   2758,   1818,    900], [ 3251,   2124,   1723,    996],
+    [ 3633,   2336,   2408,   1453], [ 2923,   3517,   2567,   1318],
+];
+
+const COS_TAB: [i16; 513] = [
+    16384,  16383,  16379,  16373,  16364,  16353,  16340,  16324,
+    16305,  16284,  16261,  16235,  16207,  16176,  16143,  16107,
+    16069,  16029,  15986,  15941,  15893,  15843,  15791,  15736,
+    15679,  15619,  15557,  15493,  15426,  15357,  15286,  15213,
+    15137,  15059,  14978,  14896,  14811,  14724,  14635,  14543,
+    14449,  14354,  14256,  14155,  14053,  13949,  13842,  13733,
+    13623,  13510,  13395,  13279,  13160,  13039,  12916,  12792,
+    12665,  12537,  12406,  12274,  12140,  12004,  11866,  11727,
+    11585,  11442,  11297,  11151,  11003,  10853,  10702,  10549,
+    10394,  10238,  10080,   9921,   9760,   9598,   9434,   9269,
+     9102,   8935,   8765,   8595,   8423,   8250,   8076,   7900,
+     7723,   7545,   7366,   7186,   7005,   6823,   6639,   6455,
+     6270,   6084,   5897,   5708,   5520,   5330,   5139,   4948,
+     4756,   4563,   4370,   4176,   3981,   3786,   3590,   3393,
+     3196,   2999,   2801,   2603,   2404,   2205,   2006,   1806,
+     1606,   1406,   1205,   1005,    804,    603,    402,    201,
+        0,   -201,   -402,   -603,   -804,  -1005,  -1205,  -1406,
+    -1606,  -1806,  -2006,  -2205,  -2404,  -2603,  -2801,  -2999,
+    -3196,  -3393,  -3590,  -3786,  -3981,  -4176,  -4370,  -4563,
+    -4756,  -4948,  -5139,  -5330,  -5520,  -5708,  -5897,  -6084,
+    -6270,  -6455,  -6639,  -6823,  -7005,  -7186,  -7366,  -7545,
+    -7723,  -7900,  -8076,  -8250,  -8423,  -8595,  -8765,  -8935,
+    -9102,  -9269,  -9434,  -9598,  -9760,  -9921, -10080, -10238,
+   -10394, -10549, -10702, -10853, -11003, -11151, -11297, -11442,
+   -11585, -11727, -11866, -12004, -12140, -12274, -12406, -12537,
+   -12665, -12792, -12916, -13039, -13160, -13279, -13395, -13510,
+   -13623, -13733, -13842, -13949, -14053, -14155, -14256, -14354,
+   -14449, -14543, -14635, -14724, -14811, -14896, -14978, -15059,
+   -15137, -15213, -15286, -15357, -15426, -15493, -15557, -15619,
+   -15679, -15736, -15791, -15843, -15893, -15941, -15986, -16029,
+   -16069, -16107, -16143, -16176, -16207, -16235, -16261, -16284,
+   -16305, -16324, -16340, -16353, -16364, -16373, -16379, -16383,
+   -16384, -16383, -16379, -16373, -16364, -16353, -16340, -16324,
+   -16305, -16284, -16261, -16235, -16207, -16176, -16143, -16107,
+   -16069, -16029, -15986, -15941, -15893, -15843, -15791, -15736,
+   -15679, -15619, -15557, -15493, -15426, -15357, -15286, -15213,
+   -15137, -15059, -14978, -14896, -14811, -14724, -14635, -14543,
+   -14449, -14354, -14256, -14155, -14053, -13949, -13842, -13733,
+   -13623, -13510, -13395, -13279, -13160, -13039, -12916, -12792,
+   -12665, -12537, -12406, -12274, -12140, -12004, -11866, -11727,
+   -11585, -11442, -11297, -11151, -11003, -10853, -10702, -10549,
+   -10394, -10238, -10080,  -9921,  -9760,  -9598,  -9434,  -9269,
+    -9102,  -8935,  -8765,  -8595,  -8423,  -8250,  -8076,  -7900,
+    -7723,  -7545,  -7366,  -7186,  -7005,  -6823,  -6639,  -6455,
+    -6270,  -6084,  -5897,  -5708,  -5520,  -5330,  -5139,  -4948,
+    -4756,  -4563,  -4370,  -4176,  -3981,  -3786,  -3590,  -3393,
+    -3196,  -2999,  -2801,  -2603,  -2404,  -2205,  -2006,  -1806,
+    -1606,  -1406,  -1205,  -1005,   -804,   -603,   -402,   -201,
+        0,    201,    402,    603,    804,   1005,   1205,   1406,
+     1606,   1806,   2006,   2205,   2404,   2603,   2801,   2999,
+     3196,   3393,   3590,   3786,   3981,   4176,   4370,   4563,
+     4756,   4948,   5139,   5330,   5520,   5708,   5897,   6084,
+     6270,   6455,   6639,   6823,   7005,   7186,   7366,   7545,
+     7723,   7900,   8076,   8250,   8423,   8595,   8765,   8935,
+     9102,   9269,   9434,   9598,   9760,   9921,  10080,  10238,
+    10394,  10549,  10702,  10853,  11003,  11151,  11297,  11442,
+    11585,  11727,  11866,  12004,  12140,  12274,  12406,  12537,
+    12665,  12792,  12916,  13039,  13160,  13279,  13395,  13510,
+    13623,  13733,  13842,  13949,  14053,  14155,  14256,  14354,
+    14449,  14543,  14635,  14724,  14811,  14896,  14978,  15059,
+    15137,  15213,  15286,  15357,  15426,  15493,  15557,  15619,
+    15679,  15736,  15791,  15843,  15893,  15941,  15986,  16029,
+    16069,  16107,  16143,  16176,  16207,  16235,  16261,  16284,
+    16305,  16324,  16340,  16353,  16364,  16373,  16379,  16383,
+    16384
+];
+
+const FIXED_CB_GAIN: [i16; 24] = [
+      1,    2,    3,    4,    6,    9,   13,   18,
+     26,   38,   55,   80,  115,  166,  240,  348,
+    502,  726, 1050, 1517, 2193, 3170, 4582, 6623
+];
+
+const ACB_GAIN170: [[i16; 20]; 170] = [
+  [
+         0,      0,      0,      0,      0,      0,      0,      0,      0,      0,
+         0,      0,      0,      0,      0,      0,      0,      0,      0,      0
+  ], [
+       776,    212,    715,    670,    809,    -36,     -2,    -31,    -27,    -39,
+       -10,    -33,     -9,    -31,     -8,    -29,    -38,    -10,    -35,    -33
+  ], [
+      1296,   1316,   -168,   -320,   -815,   -102,   -105,     -1,     -6,    -40,
+      -104,     13,     13,     25,     25,     -3,     64,     65,     -8,    -15
+  ], [
+      -589,    680,   2478,    308,   -596,    -21,    -28,   -375,     -5,    -21,
+        24,     89,   -102,     11,    -12,    -46,    -21,     24,     90,     11
+  ], [
+      -735,   -487,     -5,   2948,    468,    -33,    -14,      0,   -530,    -13,
+       -21,      0,      0,    132,     87,      0,     21,     13,      0,    -84
+  ], [
+      1042,   1730,   1068,    333,    626,    -66,   -182,    -69,     -6,    -23,
+      -110,    -67,   -112,    -21,    -35,    -21,    -39,    -66,    -40,    -12
+  ], [
+       486,   -769,   4074,   2825,  -1107,    -14,    -36,  -1013,   -487,    -74,
+        22,   -120,    191,    -83,    132,   -702,     32,    -52,    275,    191
+  ], [
+      1521,   -767,   -124,   4320,   1026,   -141,    -35,      0,  -1139,    -64,
+        71,     11,     -5,   -401,    202,     32,    -95,     48,      7,   -270
+  ], [
+      2425,   1267,   3439,    -91,  -1166,   -359,    -98,   -722,      0,    -83,
+      -187,   -509,   -266,     13,      7,     19,    172,     90,    244,     -6
+  ], [
+     -1251,    975,    173,   4039,   2005,    -95,    -58,     -1,   -996,   -245,
+        74,     13,    -10,    308,   -240,    -42,    153,   -119,    -21,   -494
+  ], [
+      1820,    632,   1322,   2062,   1031,   -202,    -24,   -106,   -259,    -64,
+       -70,   -146,    -51,   -229,    -79,   -166,   -114,    -39,    -83,   -129
+  ], [
+      -447,   4904,    244,   -315,  -2038,    -12,  -1467,     -3,     -6,   -253,
+       134,      6,    -73,     -8,     94,      4,    -55,    610,     30,    -39
+  ], [
+      -208,  -1102,    463,   -448,   5653,     -2,    -74,    -13,    -12,  -1950,
+       -14,      5,     31,     -5,    -30,     12,     71,    380,   -159,    154
+  ], [
+      4739,   2600,  -1864,    856,  -1554,  -1371,   -412,   -212,    -44,   -147,
+      -752,    539,    295,   -247,   -135,     97,    449,    246,   -176,     81
+  ], [
+      1894,   3533,     35,    -26,   2145,   -219,   -762,      0,      0,   -280,
+      -408,     -4,     -7,      3,      5,      0,   -248,   -462,     -4,      3
+  ], [
+     -2699,   1841,   4072,   2443,   1582,   -444,   -207,  -1012,   -364,   -152,
+       303,    670,   -457,    402,   -274,   -607,    260,   -177,   -393,   -236
+  ], [
+      -844,   3358,   6106,  -1059,   -537,    -43,   -688,  -2275,    -68,    -17,
+       173,    314,  -1251,    -54,    217,    395,    -27,    110,    200,    -34
+  ], [
+      1251,   1016,   3020,   2210,   1445,    -95,    -63,   -556,   -298,   -127,
+       -77,   -230,   -187,   -168,   -137,   -407,   -110,    -89,   -266,   -194
+  ], [
+      2099,   2277,   4038,   3533,  -2870,   -269,   -316,   -995,   -762,   -503,
+      -291,   -517,   -561,   -452,   -491,   -871,    367,    399,    707,    619
+  ], [
+       400,  -1114,   8516,   2422,  -1117,     -9,    -75,  -4426,   -358,    -76,
+        27,   -208,    579,    -59,    164,  -1259,     27,    -75,    580,    165
+  ], [
+     -4398,  -2011,   3912,  -2407,   2258,  -1180,   -247,   -934,   -353,   -311,
+      -540,   1050,    480,   -646,   -295,    575,    606,    277,   -539,    331
+  ], [
+      1767,  -1447,   4240,   6160,   -757,   -190,   -127,  -1097,  -2316,    -35,
+       156,   -457,    374,   -664,    544,  -1594,     81,    -66,    195,    284
+  ], [
+      1594,  -1463,   1035,   6938,   1920,   -155,   -130,    -65,  -2938,   -225,
+       142,   -100,     92,   -675,    619,   -438,   -186,    171,   -121,   -813
+  ], [
+      -562,   4716,   4085,   -591,   2421,    -19,  -1357,  -1018,    -21,   -357,
+       162,    140,  -1175,    -20,    170,    147,     83,   -696,   -603,     87
+  ], [
+      1552,   8778,   -935,    354,  -1424,   -147,  -4703,    -53,     -7,   -123,
+      -831,     88,    501,    -33,   -189,     20,    134,    763,    -81,     30
+  ], [
+      4831,  -4431,     41,  -1479,  -2976,  -1424,  -1198,      0,   -133,   -540,
+      1306,    -12,     11,    436,   -400,      3,    877,   -804,      7,   -268
+  ], [
+      2090,   1192,   1006,   1645,   4853,   -266,    -86,    -61,   -165,  -1437,
+      -152,   -128,    -73,   -210,   -119,   -101,   -619,   -353,   -298,   -487
+  ], [
+      2386,   5712,   1426,    -94,   1350,   -347,  -1991,   -124,      0,   -111,
+      -832,   -207,   -497,     13,     32,      8,   -196,   -470,   -117,      7
+  ], [
+     -1349,   1091,   1659,   8891,    313,   -111,    -72,   -168,  -4825,     -5,
+        89,    136,   -110,    732,   -592,   -900,     25,    -20,    -31,   -170
+  ], [
+      9980,    916,   -381,   -808,     88,  -6080,    -51,     -8,    -39,      0,
+      -558,    232,     21,    492,     45,    -18,    -53,     -4,      2,      4
+  ], [
+      2338,  -1031,   -248,   3928,   6484,   -333,    -64,     -3,   -942,  -2566,
+       147,     35,    -15,   -560,    247,     59,   -925,    408,     98,  -1555
+  ], [
+      6166,  -1240,   -337,   3672,  -1277,  -2320,    -93,     -6,   -823,    -99,
+       466,    126,    -25,  -1382,    278,     75,    480,    -96,    -26,    286
+  ], [
+      4377,   -132,  -2588,   1701,   4865,  -1169,     -1,   -409,   -176,  -1444,
+        35,    691,    -20,   -454,     13,    268,  -1299,     39,    768,   -505
+  ], [
+      2594,   3295,   3944,   1481,    682,   -410,   -662,   -949,   -133,    -28,
+      -521,   -624,   -793,   -234,   -297,   -356,   -108,   -137,   -164,    -61
+  ], [
+      4151,    624,    815,   4485,   2229,  -1052,    -23,    -40,  -1228,   -303,
+      -158,   -206,    -31,  -1136,   -170,   -223,   -565,    -84,   -111,   -610
+  ], [
+     -3575,   -361,   4924,   2791,   4698,   -780,     -7,  -1480,   -475,  -1347,
+       -78,   1074,    108,    609,     61,   -839,   1025,    103,  -1412,   -800
+  ], [
+     -2518,   3791,   8623,    315,   2465,   -387,   -877,  -4538,     -6,   -370,
+       582,   1325,  -1995,     48,    -73,   -166,    378,   -570,  -1297,    -47
+  ], [
+      -691,   2989,   9957,   -421,  -1142,    -29,   -545,  -6051,    -10,    -79,
+       126,    420,  -1817,    -17,     76,    256,    -48,    208,    694,    -29
+  ], [
+     -1918,    104,  -3190,  -3410,  -4440,   -224,      0,   -621,   -709,  -1203,
+        12,   -373,     20,   -399,     21,   -664,   -519,     28,   -864,   -924
+  ], [
+     -3359,  -1668,   1854,   6939,   1430,   -688,   -169,   -209,  -2939,   -124,
+      -341,    380,    188,   1422,    706,   -785,    293,    145,   -161,   -606
+  ], [
+        42,   9706,   3164,   -952,    907,      0,  -5750,   -611,    -55,    -50,
+       -25,     -8,  -1874,      2,    564,    183,     -2,   -537,   -175,     52
+  ], [
+      1607,    785,   2862,   4327,   3307,   -157,    -37,   -500,  -1143,   -667,
+       -77,   -280,   -137,   -424,   -207,   -756,   -324,   -158,   -577,   -873
+  ], [
+      6801,   3416,   2227,   1682,  -3217,  -2823,   -712,   -302,   -172,   -631,
+     -1418,   -924,   -464,   -698,   -350,   -228,   1335,    670,    437,    330
+  ], [
+      3459,   3898,    364,   7841,  -2640,   -730,   -927,     -8,  -3753,   -425,
+      -823,    -76,    -86,  -1655,  -1865,   -174,    557,    628,     58,   1263
+  ], [
+     -5902,  -3458,  -2465,  -1886,   4334,  -2126,   -730,   -371,   -217,  -1146,
+     -1245,   -888,   -520,   -679,   -398,   -283,   1561,    915,    652,    499
+  ], [
+     -3710,   1133,   7849,   3443,   -215,   -840,    -78,  -3760,   -723,     -2,
+       256,   1777,   -543,    779,   -238,  -1649,    -48,     14,    103,     45
+  ], [
+      4132,   2828,      2,  -4212,  -4116,  -1042,   -488,      0,  -1083,  -1034,
+      -713,      0,      0,   1062,    727,      0,   1038,    710,      0,  -1058
+  ], [
+      5875,   8496,  -1796,   1376,  -1786,  -2107,  -4406,   -197,   -115,   -194,
+     -3047,    644,    931,   -493,   -713,    150,    640,    926,   -195,    150
+  ], [
+      3143,   3483,   3546,   -793,   4489,   -603,   -740,   -767,    -38,  -1230,
+      -668,   -680,   -754,    152,    168,    171,   -861,   -954,   -971,    217
+  ], [
+      2845,   7965,   3695,  -5432,   3978,   -494,  -3873,   -833,  -1801,   -966,
+     -1383,   -641,  -1796,    943,   2641,   1225,   -691,  -1934,   -897,   1319
+  ], [
+      1538,    150,   7139,   2049,   3097,   -144,     -1,  -3110,   -256,   -585,
+       -14,   -670,    -65,   -192,    -18,   -892,   -290,    -28,  -1349,   -387
+  ], [
+       618,   7520,   4729,   -238,  -3373,    -23,  -3452,  -1365,     -3,   -694,
+      -283,   -178,  -2170,      8,    109,     68,    127,   1548,    973,    -49
+  ], [
+      2965,  -3013,   7912,   7076,  -1997,   -536,   -554,  -3821,  -3056,   -243,
+       545,  -1431,   1455,  -1280,   1301,  -3417,    361,   -367,    964,    862
+  ], [
+      2443,   -929,  -1113,   9677,   4138,   -364,    -52,    -75,  -5716,  -1045,
+       138,    166,    -63,  -1443,    549,    657,   -617,    234,    281,  -2444
+  ], [
+      1966,   3309,  10085,  -3399,   2105,   -236,   -668,  -6207,   -705,   -270,
+      -397,  -1210,  -2037,    408,    686,   2092,   -252,   -425,  -1295,    436
+  ], [
+      -112,  -1368,   8868,   4822,   2048,      0,   -114,  -4800,  -1419,   -256,
+        -9,     61,    740,     33,    402,  -2610,     14,    171,  -1108,   -602
+  ], [
+     -2597,    438,  -1839,   6229,   7266,   -411,    -11,   -206,  -2368,  -3223,
+        69,   -291,     49,    987,   -166,    699,   1152,   -194,    816,  -2763
+  ], [
+      3454,    553,   9127,   4946,  -5596,   -728,    -18,  -5084,  -1493,  -1911,
+      -116,  -1924,   -308,  -1042,   -166,  -2755,   1179,    188,   3117,   1689
+  ], [
+      -532,   -663,  12262,   2495,  -1004,    -17,    -26,  -9177,   -380,    -61,
+       -21,    398,    496,     81,    101,  -1867,    -32,    -40,    751,    152
+  ], [
+     -2100,   1317,  -1509,  11425,   2997,   -269,   -105,   -139,  -7967,   -548,
+       168,   -193,    121,   1464,   -918,   1052,    384,   -240,    276,  -2090
+  ], [
+      1193,  -2697,  11259,   5373,   -763,    -86,   -444,  -7737,  -1762,    -35,
+       196,   -819,   1853,   -391,    884,  -3692,     55,   -125,    525,    250
+  ], [
+      2405,   -471,  11079,    203,    782,   -353,    -13,  -7491,     -2,    -37,
+        69,  -1626,    318,    -29,      5,   -137,   -114,     22,   -529,     -9
+  ], [
+     -1871,   5685,  11290,  -2662,   1353,   -213,  -1972,  -7780,   -432,   -111,
+       649,   1289,  -3917,   -304,    923,   1834,    154,   -469,   -932,    220
+  ], [
+     -3768,   5927,  -3093,   5041,   5212,   -866,  -2144,   -584,  -1551,  -1658,
+      1363,   -711,   1119,   1159,  -1824,    951,   1198,  -1885,    984,  -1603
+  ], [
+     -2546,   9502,   5969,  -2440,   1928,   -395,  -5511,  -2175,   -363,   -226,
+      1477,    927,  -3462,   -379,   1415,    889,    299,  -1118,   -702,    287
+  ], [
+     -4963,   3568,   4592,   5508,   3451,  -1503,   -777,  -1287,  -1851,   -727,
+      1080,   1391,  -1000,   1668,  -1199,  -1543,   1045,   -751,   -967,  -1160
+  ], [
+      1745,  -2586,   3983,  10899,  -1551,   -186,   -408,   -968,  -7250,   -146,
+       275,   -424,    628,  -1161,   1720,  -2649,    165,   -244,    377,   1032
+  ], [
+       867,   -456,   -727,   3369,  11822,    -45,    -12,    -32,   -692,  -8531,
+        24,     38,    -20,   -178,     93,    149,   -625,    329,    525,  -2431
+  ], [
+      7535,   2422,   1926,   1405,   1599,  -3466,   -358,   -226,   -120,   -156,
+     -1114,   -886,   -284,   -646,   -207,   -165,   -735,   -236,   -188,   -137
+  ], [
+      1041,   -735,   -142,  13209,   1515,    -66,    -33,     -1, -10649,   -140,
+        46,      9,     -6,   -839,    593,    114,    -96,     68,     13,  -1222
+  ], [
+      7950,   6745,  -1444,  -1008,   2721,  -3857,  -2777,   -127,    -62,   -452,
+     -3273,    700,    594,    489,    415,    -88,  -1320,  -1120,    239,    167
+  ], [
+     -4754,  -1379,   4522,   -578,  -5733,  -1379,   -116,  -1248,    -20,  -2006,
+      -400,   1312,    380,   -167,    -48,    159,  -1663,   -482,   1582,   -202
+  ], [
+      3220,   5978,   5923,   2430,  -2689,   -633,  -2181,  -2141,   -360,   -441,
+     -1175,  -1164,  -2161,   -477,   -886,   -878,    528,    981,    972,    398
+  ], [
+       377,   1312,  13978,  -1470,    677,     -8,   -105, -11925,   -132,    -28,
+       -30,   -321,  -1119,     33,    117,   1254,    -15,    -54,   -577,     60
+  ], [
+     -3435,   6770,    314,   -885,   5686,   -720,  -2797,     -6,    -47,  -1973,
+      1419,     65,   -129,   -185,    366,     16,   1192,  -2349,   -109,    307
+  ], [
+      3171,   8774,  -2260,   2679,   3069,   -613,  -4699,   -312,   -438,   -575,
+     -1698,    437,   1210,   -518,  -1435,    369,   -594,  -1643,    423,   -501
+  ], [
+      5557,   1509,   5407,   -125,  -7386,  -1884,   -139,  -1784,      0,  -3330,
+      -511,  -1834,   -498,     42,     11,     41,   2505,    680,   2438,    -56
+  ], [
+     -2838,   2595,  13228,    271,   1793,   -491,   -411, -10680,     -4,   -196,
+       449,   2291,  -2095,     47,    -42,   -219,    310,   -284,  -1447,    -29
+  ], [
+       664,   -278,  14966,    951,   -711,    -26,     -4, -13672,    -55,    -30,
+        11,   -606,    253,    -38,     16,   -869,     28,    -12,    650,     41
+  ], [
+       808,   1770,   8658,   5863,  -1486,    -39,   -191,  -4576,  -2098,   -134,
+       -87,   -427,   -935,   -289,   -633,  -3098,     73,    160,    785,    531
+  ], [
+      3063,   1539,   2000,   -542,   9576,   -572,   -144,   -244,    -17,  -5597,
+      -287,   -374,   -188,    101,     51,     66,  -1790,   -900,  -1169,    317
+  ], [
+       514,  14083,   -323,    896,   -891,    -16, -12106,     -6,    -49,    -48,
+      -442,     10,    277,    -28,   -770,     17,     27,    766,    -17,     48
+  ], [
+       892,    158,   5237,  11057,  -1603,    -48,     -1,  -1674,  -7462,   -156,
+        -8,   -285,    -50,   -602,   -106,  -3534,     87,     15,    512,   1082
+  ], [
+     -1612,   2564,  -4296,  12526,   5710,   -158,   -401,  -1126,  -9576,  -1990,
+       252,   -422,    672,   1232,  -1960,   3284,    561,   -893,   1497,  -4365
+  ], [
+      4889,  -6878,    612,   6109,   4753,  -1459,  -2887,    -22,  -2277,  -1379,
+      2052,   -182,    257,  -1823,   2564,   -228,  -1418,   1995,   -177,  -1772
+  ], [
+      3053,   -506,   2403,   9625,   1322,   -569,    -15,   -352,  -5655,   -106,
+        94,   -448,     74,  -1794,    297,  -1412,   -246,     40,   -194,   -777
+  ], [
+      -754,  12904,   4480,  -2113,   1471,    -34, -10163,  -1225,   -272,   -132,
+       594,    206,  -3529,    -97,   1664,    577,     67,  -1159,   -402,    189
+  ], [
+      4255,   1476,   5055,   2393,   2912,  -1105,   -132,  -1559,   -349,   -517,
+      -383,  -1313,   -455,   -621,   -215,   -738,   -756,   -262,   -898,   -425
+  ], [
+     -1371,    535,   1417,  14604,   -997,   -114,    -17,   -122, -13017,    -60,
+        44,    118,    -46,   1222,   -477,  -1263,    -83,     32,     86,    888
+  ], [
+      5368,  -1744,   4083,  -1236,   3753,  -1758,   -185,  -1017,    -93,   -860,
+       571,  -1338,    434,    405,   -131,    308,  -1229,    399,   -935,    283
+  ], [
+      1588,  -3097,  14415,   3699,  -1171,   -154,   -585, -12683,   -835,    -83,
+       300,  -1397,   2725,   -358,    699,  -3255,    113,   -221,   1030,    264
+  ], [
+       212,   7989,   9471,  -3344,   2009,     -2,  -3895,  -5475,   -682,   -246,
+      -103,   -123,  -4618,     43,   1630,   1933,    -26,   -979,  -1161,    410
+  ], [
+       856,   2294,   -627,   6930,   6929,    -44,   -321,    -24,  -2931,  -2930,
+      -119,     32,     87,   -362,   -970,    265,   -362,   -970,    265,  -2931
+  ], [
+      2357,  -4187,   7162,   7683,   3371,   -339,  -1070,  -3131,  -3603,   -693,
+       602,  -1030,   1830,  -1105,   1963,  -3359,   -485,    861,  -1474,  -1581
+  ], [
+       350,   4585,  14053,  -3819,   1218,     -7,  -1283, -12054,   -890,    -90,
+       -97,   -300,  -3933,     81,   1068,   3275,    -26,   -341,  -1045,    284
+  ], [
+     -3248,   3531,    475,   2137,  11711,   -644,   -761,    -13,   -278,  -8372,
+       700,     94,   -102,    423,   -460,    -62,   2322,  -2524,   -340,  -1528
+  ], [
+     -3017,   3852,   1725,   8440,   5257,   -555,   -905,   -181,  -4348,  -1686,
+       709,    317,   -405,   1554,  -1984,   -889,    968,  -1236,   -553,  -2708
+  ], [
+      -909,   3196,  15512,  -2528,   1066,    -50,   -623, -14686,   -390,    -69,
+       177,    861,  -3026,   -140,    493,   2393,     59,   -208,  -1009,    164
+  ], [
+       959,  -3370,   9617,   9545,  -1761,    -56,   -693,  -5645,  -5561,   -189,
+       197,   -563,   1978,   -558,   1963,  -5603,    103,   -362,   1034,   1026
+  ], [
+      7575,  11796,  -4845,   3252,  -1703,  -3502,  -8493,  -1433,   -645,   -177,
+     -5454,   2240,   3488,  -1503,  -2341,    961,    787,   1226,   -503,    338
+  ], [
+      6409,   1722,   1764,  -4191,   6015,  -2507,   -181,   -189,  -1072,  -2208,
+      -673,   -690,   -185,   1639,    440,    451,  -2353,   -632,   -647,   1538
+  ], [
+     -2420,  12161,   5038,   1286,  -2098,   -357,  -9027,  -1549,   -100,   -268,
+      1796,    744,  -3740,    190,   -954,   -395,   -310,   1557,    645,    164
+  ], [
+     -2232,  -1341,   7246,   9470,  -1977,   -304,   -109,  -3204,  -5474,   -238,
+      -182,    987,    593,   1290,    775,  -4188,   -269,   -161,    874,   1143
+  ], [
+      1030,   7034,   4231,   1551,   3077,    -64,  -3019,  -1093,   -146,   -577,
+      -442,   -266,  -1816,    -97,   -666,   -400,   -193,  -1321,   -794,   -291
+  ], [
+      5121,  11835,   -477,  -1749,   2298,  -1601,  -8549,    -13,   -186,   -322,
+     -3699,    149,    344,    546,   1264,    -50,   -718,  -1660,     66,    245
+  ], [
+     -3328,   3827,   5921,   9976,  -1045,   -676,   -894,  -2140,  -6075,    -66,
+       777,   1203,  -1383,   2027,  -2330,  -3605,   -212,    244,    377,    636
+  ], [
+      3813,   5718,  -4666,  -3412,   5674,   -887,  -1995,  -1329,   -710,  -1965,
+     -1331,   1086,   1628,    794,   1191,   -972,  -1320,  -1980,   1616,   1181
+  ], [
+      1348,  -3672,  13154,   6938,  -1690,   -110,   -823, -10561,  -2938,   -174,
+       302,  -1082,   2948,   -570,   1555,  -5570,    139,   -379,   1357,    716
+  ], [
+      2151,  -3586,   6949,  12131,  -1224,   -282,   -785,  -2947,  -8982,    -91,
+       470,   -912,   1521,  -1592,   2655,  -5145,    160,   -268,    519,    906
+  ], [
+     -2889,   9647,  10276,  -2728,    995,   -509,  -5680,  -6445,   -454,    -60,
+      1701,   1812,  -6051,   -481,   1606,   1711,    175,   -586,   -624,    165
+  ], [
+      6177,   2184,    555,   1985,   6589,  -2329,   -291,    -18,   -240,  -2650,
+      -823,   -209,    -74,   -748,   -264,    -67,  -2484,   -878,   -223,   -798
+  ], [
+      -492,    391,  17166,   -681,    240,    -14,     -9, -17987,    -28,     -3,
+        11,    515,   -410,    -20,     16,    713,      7,     -5,   -252,     10
+  ], [
+     12628,   5448,  -2630,   3011,  -2695,  -9733,  -1811,   -422,   -553,   -443,
+     -4199,   2027,    874,  -2321,  -1001,    483,   2077,    896,   -432,    495
+  ], [
+     -3628,   -534,   3447,   7002,   6751,   -803,    -17,   -725,  -2992,  -2782,
+      -118,    763,    112,   1550,    228,  -1473,   1495,    220,  -1420,  -2885
+  ], [
+     -5239,   5901,   8107,   3650,   4846,  -1675,  -2125,  -4012,   -813,  -1433,
+      1887,   2592,  -2920,   1167,  -1315,  -1806,   1550,  -1745,  -2398,  -1080
+  ], [
+      6157,   6678,   4099,  -1074,   2348,  -2314,  -2722,  -1025,    -70,   -336,
+     -2509,  -1540,  -1670,    403,    437,    268,   -882,   -957,   -587,    153
+  ], [
+      1079,  16099,    242,   -881,   1690,    -71, -15820,     -3,    -47,   -174,
+     -1060,    -16,   -238,     58,    865,     13,   -111,  -1661,    -25,     90
+  ], [
+      -278,    227,  -1039,   1636,  16945,     -4,     -3,    -65,   -163, -17526,
+         3,    -17,     14,     27,    -22,    103,    287,   -234,   1074,  -1693
+  ], [
+     15778,  -1454,    574,   -603,   -107, -15195,   -129,    -20,    -22,      0,
+      1400,   -553,     51,    581,    -53,     21,    103,     -9,      3,     -3
+  ], [
+      2406,   -836,  13224,   7993,  -4266,   -353,    -42, -10673,  -3899,  -1111,
+       122,  -1942,    674,  -1174,    407,  -6451,    626,   -217,   3443,   2081
+  ], [
+      3184,  14368,  -3336,   2255,  -1801,   -619, -12600,   -679,   -310,   -198,
+     -2793,    648,   2926,   -438,  -1977,    459,    350,   1580,   -366,    247
+  ], [
+     -1698,  17076,   2504,   -539,   -646,   -176, -17798,   -382,    -17,    -25,
+      1770,    259,  -2610,    -55,    561,     82,    -67,    673,     98,    -21
+  ], [
+      2375,   -797,  -2696,  14483,   5383,   -344,    -38,   -443, -12803,  -1769,
+       115,    391,   -131,  -2100,    705,   2384,   -780,    262,    886,  -4759
+  ], [
+     -2691,   2554,  -4520,   9573,  10655,   -442,   -398,  -1247,  -5594,  -6930,
+       419,   -742,    704,   1572,  -1492,   2641,   1750,  -1661,   2939,  -6226
+  ], [
+     -4332,  -4399,  -1657,   4880,   7375,  -1145,  -1181,   -167,  -1453,  -3319,
+     -1163,   -438,   -444,   1290,   1310,    493,   1950,   1980,    745,  -2196
+  ], [
+     -3498,   7405,   9955,   2693,  -2971,   -746,  -3347,  -6049,   -442,   -538,
+      1581,   2125,  -4499,    575,  -1217,  -1636,   -634,   1342,   1805,    488
+  ], [
+      6717,  -3792,   7739,   2798,   3489,  -2754,   -877,  -3655,   -477,   -743,
+      1554,  -3173,   1791,  -1147,    647,  -1321,  -1430,    807,  -1648,   -595
+  ], [
+      5263,   9770,   3463,   1069,  -3971,  -1690,  -5826,   -732,    -69,   -962,
+     -3138,  -1112,  -2065,   -343,   -637,   -226,   1275,   2368,    839,    259
+  ], [
+      1243,  -2634,  16772,   1871,    332,    -94,   -423, -17169,   -213,     -6,
+       199,  -1273,   2696,   -142,    300,  -1915,    -25,     53,   -339,    -37
+  ], [
+      2691,   2836,   3105,   5711,   4817,   -442,   -491,   -588,  -1991,  -1416,
+      -465,   -510,   -537,   -938,   -988,  -1082,   -791,   -834,   -913,  -1679
+  ], [
+      4366,   2944,   7210,   3627,   1161,  -1163,   -529,  -3172,   -803,    -82,
+      -784,  -1921,  -1295,   -966,   -651,  -1596,   -309,   -208,   -511,   -257
+  ], [
+     13888,   3951,   -671,  -2305,   3354, -11773,   -953,    -27,   -324,   -686,
+     -3349,    569,    161,   1954,    556,    -94,  -2843,   -809,    137,    472
+  ], [
+      7053,   5847,   2929,   8378,  -4794,  -3036,  -2086,   -523,  -4284,  -1403,
+     -2517,  -1261,  -1045,  -3607,  -2990,  -1498,   2064,   1711,    857,   2451
+  ], [
+     -2191,  12838,   9182,  -3915,   1617,   -293, -10059,  -5146,   -935,   -159,
+      1717,   1228,  -7195,   -523,   3068,   2194,    216,  -1267,   -906,    386
+  ], [
+     -4881,  13114,   5767,   -435,   4155,  -1454, -10498,  -2030,    -11,  -1054,
+      3907,   1718,  -4616,   -129,    348,    153,   1238,  -3326,  -1462,    110
+  ], [
+      7843,  -1250,    210,   7106,  -5203,  -3754,    -95,     -2,  -3082,  -1652,
+       598,   -100,     16,  -3402,    542,    -91,   2491,   -397,     66,   2257
+  ], [
+     -2463,   8168,  14551,  -3908,   1828,   -370,  -4072, -12923,   -932,   -204,
+      1228,   2188,  -7254,   -587,   1948,   3471,    274,   -911,  -1623,    436
+  ], [
+     -1579,    347,   -272,  -2735,  16031,   -152,     -7,     -4,   -456, -15686,
+        33,    -26,      5,   -263,     58,    -45,   1545,   -340,    266,   2676
+  ], [
+     -6327,   1328,   5093,  -5079,   7617,  -2443,   -107,  -1583,  -1574,  -3541,
+       513,   1967,   -413,  -1961,    411,   1578,   2941,   -617,  -2367,   2361
+  ], [
+      3286,  -4509,  11306,  11025,  -2623,   -659,  -1241,  -7802,  -7419,   -420,
+       904,  -2267,   3112,  -2211,   3034,  -7608,    526,   -722,   1810,   1765
+  ], [
+      5567,  17853,  -3754,   1166,   -519,  -1892, -19455,   -860,    -83,    -16,
+     -6067,   1275,   4090,   -396,  -1271,    267,    176,    566,   -119,     37
+  ], [
+     -2136,   -424,  15292,   5108,  -1648,   -278,    -10, -14273,  -1593,   -165,
+       -55,   1993,    396,    666,    132,  -4768,   -214,    -42,   1538,    514
+  ], [
+      2267,  -3297,   2549,  16563,   -791,   -313,   -663,   -396, -16745,    -38,
+       456,   -352,    513,  -2291,   3333,  -2576,    109,   -159,    123,    799
+  ], [
+      3655,   1899,  -3364,   6279,  12510,   -815,   -220,   -690,  -2406,  -9552,
+      -423,    750,    390,  -1400,   -728,   1289,  -2791,  -1450,   2568,  -4794
+  ], [
+      8052,   2285,  -6193,   5138,   6003,  -3957,   -318,  -2341,  -1611,  -2199,
+     -1123,   3044,    864,  -2525,   -716,   1942,  -2950,   -837,   2269,  -1882
+  ], [
+      -386,  -2291,   7679,  15387,  -2723,     -9,   -320,  -3599, -14452,   -452,
+       -54,    181,   1074,    362,   2152,  -7212,    -64,   -380,   1276,   2557
+  ], [
+      2777,  -1173,   3984,  13079,   2508,   -470,    -84,   -969, -10440,   -384,
+       198,   -675,    285,  -2217,    936,  -3180,   -425,    179,   -610,  -2002
+  ], [
+     -1879,   1771,  -2684,  16705,   1833,   -215,   -191,   -439, -17032,   -205,
+       203,   -308,    290,   1916,  -1805,   2736,    210,   -198,    300,  -1869
+  ], [
+      1052,   4495,  15519,   1467,  -4032,    -67,  -1233, -14700,   -131,   -992,
+      -288,   -997,  -4257,    -94,   -402,  -1389,    259,   1106,   3819,    361
+  ], [
+      3010,   2544,   6969,   7559,   1996,   -553,   -395,  -2964,  -3487,   -243,
+      -467,  -1280,  -1082,  -1388,  -1174,  -3215,   -366,   -310,   -849,   -921
+  ], [
+     -5209,  -1867,   8713,  10351,   1549,  -1656,   -212,  -4634,  -6540,   -146,
+      -593,   2770,    993,   3291,   1180,  -5505,    492,    176,   -824,   -979
+  ], [
+     -4314,   8513,    913,   7547,  -2723,  -1135,  -4423,    -50,  -3476,   -452,
+      2241,    240,   -474,   1987,  -3921,   -420,   -717,   1415,    151,   1254
+  ], [
+     12929,  -1219,   2448,   1757,   6303, -10204,    -90,   -365,   -188,  -2425,
+       962,  -1932,    182,  -1386,    130,   -262,  -4974,    469,   -941,   -676
+  ], [
+      6465,   4132,   3167,   3160,   5697,  -2551,  -1042,   -612,   -609,  -1981,
+     -1630,  -1249,   -798,  -1247,   -797,   -611,  -2248,  -1437,  -1101,  -1099
+  ], [
+     -3636,   4859,  18914,  -1335,    810,   -807,  -1441, -21836,   -108,    -40,
+      1078,   4198,  -5609,   -296,    396,   1541,    179,   -240,   -936,     66
+  ], [
+      8844,   7864,    654,  -4063,  -5680,  -4774,  -3774,    -26,  -1007,  -1969,
+     -4245,   -353,   -314,   2193,   1950,    162,   3066,   2726,    226,  -1408
+  ], [
+      1859,   2634,   9228,    996,   9464,   -211,   -423,  -5197,    -60,  -5467,
+      -299,  -1047,  -1483,   -113,   -160,   -561,  -1074,  -1521,  -5330,   -575
+  ], [
+      2949,  12260,  10290,   -497,  -3943,   -530,  -9174,  -6463,    -15,   -949,
+     -2206,  -1852,  -7700,     89,    372,    312,    709,   2950,   2476,   -119
+  ], [
+     -2903,   1552,  14867,   9970,   -496,   -514,   -147, -13491,  -6068,    -15,
+       275,   2634,  -1408,   1766,   -944,  -9047,    -87,     47,    450,    302
+  ], [
+      3243,   8234,   7586,   3373,   2151,   -642,  -4138,  -3512,   -694,   -282,
+     -1630,  -1501,  -3812,   -667,  -1695,  -1561,   -425,  -1081,   -996,   -442
+  ], [
+     -9631,     60,   3501,   5359,  10150,  -5662,      0,   -748,  -1752,  -6288,
+        35,   2058,    -12,   3150,    -19,  -1145,   5967,    -37,  -2169,  -3320
+  ], [
+     -6874,  -2553,  -5446,  -2195,  -7841,  -2884,   -397,  -1810,   -294,  -3753,
+     -1071,  -2285,   -848,   -921,   -342,   -729,  -3290,  -1221,  -2606,  -1050
+  ], [
+     -3413,  -1141,   4630,  13612,   7897,   -711,    -79,  -1308, -11310,  -3806,
+      -237,    964,    322,   2836,    948,  -3847,   1645,    550,  -2231,  -6561
+  ], [
+      4410,  -5678,   8006,  -3992,   3811,  -1187,  -1968,  -3912,   -973,   -886,
+      1528,  -2155,   2775,   1074,  -1383,   1951,  -1025,   1321,  -1862,    928
+  ], [
+      5659,  11535,   2203,   -452,   7169,  -1954,  -8121,   -296,    -12,  -3137,
+     -3984,   -761,  -1551,    156,    318,     60,  -2476,  -5048,   -964,    197
+  ], [
+      2914,  -2914,   3485,  -3965,  13675,   -518,   -518,   -741,   -959, -11414,
+       518,   -620,    620,    705,   -705,    843,  -2433,   2432,  -2909,   3310
+  ], [
+      7843,   1907,   1022,   8882,   7972,  -3755,   -222,    -63,  -4815,  -3879,
+      -913,   -489,   -119,  -4252,  -1034,   -554,  -3816,   -928,   -497,  -4322
+  ], [
+     13807,   9531,   1436,   1612,   1779, -11636,  -5544,   -125,   -158,   -193,
+     -8032,  -1210,   -835,  -1358,   -938,   -141,  -1499,  -1035,   -156,   -175
+  ], [
+     13620,  -5337,   5450,  -2263,   1723, -11322,  -1738,  -1813,   -312,   -181,
+      4436,  -4531,   1775,   1881,   -737,    752,  -1432,    561,   -573,    238
+  ], [
+      5297,   8374,   8872,   7694,   6538,  -1712,  -4280,  -4804,  -3613,  -2609,
+     -2707,  -2868,  -4534,  -2487,  -3932,  -4166,  -2113,  -3341,  -3540,  -3070
+  ]
+];
+const ACB_GAIN85: [[i16; 20]; 85] = [
+  [
+         0,      0,      0,      0,      0,      0,      0,      0,      0,      0,
+         0,      0,      0,      0,      0,      0,      0,      0,      0,      0
+  ], [
+       800,   1496,    167,   -256,   -338,    -39,   -136,     -1,     -4,     -6,
+       -73,     -8,    -15,     12,     23,      2,     16,     30,      3,     -5
+  ], [
+      -462,   -686,    493,   2575,    311,    -13,    -28,    -14,   -404,     -5,
+       -19,     13,     20,     72,    107,    -77,      8,     13,     -9,    -48
+  ], [
+      1483,    144,    784,    928,   1243,   -134,     -1,    -37,    -52,    -94,
+       -13,    -71,     -6,    -84,     -8,    -44,   -112,    -10,    -59,    -70
+  ], [
+       -77,    275,   3522,   1056,  -1254,      0,     -4,   -757,    -68,    -95,
+         1,     16,    -59,      4,    -17,   -227,     -5,     21,    269,     80
+  ], [
+      -125,    -40,   -264,    381,   5027,      0,      0,     -4,     -8,  -1542,
+         0,     -2,      0,      2,      0,      6,     38,     12,     81,   -117
+  ], [
+       138,    332,   2215,   2574,   1339,     -1,     -6,   -299,   -404,   -109,
+        -2,    -18,    -44,    -21,    -52,   -348,    -11,    -27,   -181,   -210
+  ], [
+      3685,   2883,   -887,    866,  -1639,   -828,   -507,    -48,    -45,   -164,
+      -648,    199,    156,   -194,   -152,     46,    368,    288,    -88,     86
+  ], [
+      1396,   2146,   2235,    345,    942,   -118,   -281,   -305,     -7,    -54,
+      -182,   -190,   -292,    -29,    -45,    -47,    -80,   -123,   -128,    -19
+  ], [
+        13,   4475,   3549,   -804,   -655,      0,  -1222,   -768,    -39,    -26,
+        -3,     -2,   -969,      0,    219,    174,      0,    179,    141,    -32
+  ], [
+      -724,    254,    242,   6049,   2462,    -32,     -3,     -3,  -2233,   -370,
+        11,     10,     -3,    267,    -94,    -89,    108,    -38,    -36,   -909
+  ], [
+       626,  -1713,   6121,   4561,  -1061,    -23,   -179,  -2287,  -1270,    -68,
+        65,   -233,    640,   -174,    477,  -1704,     40,   -111,    396,    295
+  ], [
+      -350,   1391,   7985,    511,   -405,     -7,   -118,  -3892,    -15,    -10,
+        29,    170,   -678,     10,    -43,   -249,     -8,     34,    197,     12
+  ], [
+      3144,   -529,    608,   2530,   3878,   -603,    -17,    -22,   -390,   -918,
+       101,   -116,     19,   -485,     81,    -93,   -744,    125,   -144,   -599
+  ], [
+      2589,   -689,   3045,   5603,   -404,   -409,    -29,   -566,  -1916,    -10,
+       108,   -481,    128,   -885,    235,  -1041,     63,    -17,     75,    138
+  ], [
+      3107,    513,   1374,  -3594,  -4922,   -589,    -16,   -115,   -788,  -1478,
+       -97,   -260,    -43,    681,    112,    301,    933,    154,    413,  -1079
+  ], [
+      2468,   6010,   1107,   -390,   1961,   -372,  -2204,    -74,     -9,   -234,
+      -905,   -166,   -406,     58,    143,     26,   -295,   -719,   -132,     46
+  ], [
+      4773,   2766,   2368,   4862,  -4044,  -1390,   -467,   -342,  -1443,   -998,
+      -806,   -690,   -399,  -1416,   -821,   -702,   1178,    682,    584,   1200
+  ], [
+      1665,  -1879,   1443,   1701,   8562,   -169,   -215,   -127,   -176,  -4475,
+       190,   -146,    165,   -172,    195,   -149,   -870,    982,   -754,   -889
+  ], [
+      2716,   9011,  -1007,    755,  -1785,   -450,  -4956,    -61,    -34,   -194,
+     -1493,    167,    554,   -125,   -415,     46,    296,    982,   -109,     82
+  ], [
+     -2727,   7548,   1285,    938,   3420,   -453,  -3478,   -100,    -53,   -714,
+      1256,    213,   -592,    156,   -432,    -73,    569,  -1576,   -268,   -196
+  ], [
+      3677,    882,   4050,   1202,   2323,   -825,    -47,  -1001,    -88,   -329,
+      -198,   -909,   -218,   -269,    -64,   -297,   -521,   -125,   -574,   -170
+  ], [
+      2046,   -753,    122,  10102,    603,   -255,    -34,      0,  -6229,    -22,
+        94,    -15,      5,  -1261,    464,    -75,    -75,     27,     -4,   -372
+  ], [
+       449,  -1815,  10690,   3870,   -527,    -12,   -201,  -6976,   -914,    -16,
+        49,   -293,   1184,   -106,    428,  -2525,     14,    -58,    344,    124
+  ], [
+      -941,   2352,   5049,   3650,   2637,    -54,   -337,  -1556,   -813,   -424,
+       135,    290,   -725,    209,   -524,  -1125,    151,   -378,   -812,   -587
+  ], [
+     -1879,    796,   3117,   9569,   -404,   -215,    -38,   -593,  -5589,     -9,
+        91,    357,   -151,   1097,   -464,  -1821,    -46,     19,     76,    236
+  ], [
+     -1715,   2043,  -2096,   9946,   4001,   -179,   -254,   -268,  -6038,   -977,
+       213,   -219,    261,   1041,  -1240,   1272,    418,   -498,    511,  -2429
+  ], [
+     -5772,   -618,  -3921,    284,  -3155,  -2033,    -23,   -938,     -4,   -607,
+      -218,  -1381,   -148,    100,     10,     68,  -1111,   -119,   -755,     54
+  ], [
+       382,   4748,   8003,  -2064,   2198,     -8,  -1376,  -3909,   -260,   -294,
+      -110,   -186,  -2319,     48,    598,   1008,    -51,   -637,  -1073,    277
+  ], [
+      -867,   3015,  11926,  -1675,    947,    -45,   -555,  -8681,   -171,    -54,
+       159,    631,  -2195,    -88,    308,   1219,     50,   -174,   -690,     96
+  ], [
+     -4933,   -432,   6757,   3771,   1352,  -1485,    -11,  -2786,   -867,   -111,
+      -130,   2034,    178,   1135,     99,  -1555,    407,     35,   -557,   -311
+  ], [
+       152,   9726,   4231,  -1928,   1490,     -1,  -5774,  -1092,   -226,   -135,
+       -90,    -39,  -2511,     17,   1144,    498,    -13,   -884,   -384,    175
+  ], [
+      2512,    193,   9033,   5361,  -3148,   -385,     -2,  -4980,  -1754,   -605,
+       -29,  -1385,   -106,   -822,    -63,  -2956,    482,     37,   1735,   1030
+  ], [
+      8464,   2844,     12,    549,   2132,  -4373,   -493,      0,    -18,   -277,
+     -1469,     -6,     -2,   -284,    -95,      0,  -1101,   -370,     -1,    -71
+  ], [
+      2141,  -2602,   7166,   9046,  -1350,   -279,   -413,  -3134,  -4994,   -111,
+       340,   -936,   1138,  -1182,   1436,  -3957,    176,   -214,    590,    745
+  ], [
+      -244,    278,  13307,   1227,   -161,     -3,     -4, -10808,    -91,     -1,
+         4,    198,   -226,     18,    -20,   -997,     -2,      2,    131,     12
+  ], [
+     -1947,   8217,   6269,    917,  -2559,   -231,  -4121,  -2399,    -51,   -399,
+       976,    745,  -3144,    108,   -460,   -350,   -304,   1283,    979,    143
+  ], [
+     -1810,   2061,  -2781,   6056,  10058,   -200,   -259,   -472,  -2238,  -6174,
+       227,   -307,    349,    669,   -761,   1028,   1111,  -1265,   1707,  -3717
+  ], [
+      7827,   9161,  -3409,   2473,  -1510,  -3739,  -5122,   -709,   -373,   -139,
+     -4376,   1628,   1906,  -1181,  -1382,    514,    721,    844,   -314,    228
+  ], [
+     -1430,   8313,   9541,  -2955,   1626,   -124,  -4218,  -5556,   -533,   -161,
+       725,    832,  -4841,   -257,   1499,   1721,    142,   -825,   -947,    293
+  ], [
+      2819,  -4247,   5391,   8673,   2756,   -485,  -1101,  -1774,  -4591,   -463,
+       730,   -927,   1397,  -1492,   2248,  -2854,   -474,    714,   -907,  -1459
+  ], [
+       141,  14552,    690,    257,   -112,     -1, -12926,    -29,     -4,      0,
+      -125,     -5,   -613,     -2,   -228,    -10,      0,     99,      4,      1
+  ], [
+     11938,  -1859,   1806,   -962,   -884,  -8699,   -211,   -199,    -56,    -47,
+      1355,  -1316,    205,    701,   -109,    106,    644,   -100,     97,    -51
+  ], [
+      3728,   1982,   2264,   4584,   3131,   -848,   -239,   -312,  -1282,   -598,
+      -451,   -515,   -273,  -1043,   -554,   -633,   -712,   -378,   -432,   -876
+  ], [
+     -1181,    766,    720,  14303,   -216,    -85,    -35,    -31, -12486,     -2,
+        55,     51,    -33,   1031,   -668,   -628,    -15,     10,      9,    189
+  ], [
+     -4385,   4826,  10112,   1569,   3388,  -1173,  -1421,  -6242,   -150,   -700,
+      1291,   2706,  -2979,    420,   -462,   -969,    906,   -998,  -2091,   -324
+  ], [
+      -448,   1932,  15591,  -1842,    657,    -12,   -227, -14837,   -207,    -26,
+        52,    427,  -1838,    -50,    217,   1753,     18,    -77,   -626,     74
+  ], [
+     -4141,   1844,   3962,   5517,   6220,  -1046,   -207,   -958,  -1858,  -2361,
+       466,   1001,   -446,   1394,   -621,  -1334,   1572,   -700,  -1504,  -2094
+  ], [
+       729,  -2299,  14755,   3657,   -952,    -32,   -322, -13288,   -816,    -55,
+       102,   -656,   2071,   -162,    513,  -3294,     42,   -133,    857,    212
+  ], [
+     -1385,   5801,  13339,  -3137,   1344,   -117,  -2054, -10861,   -600,   -110,
+       490,   1127,  -4723,   -265,   1111,   2554,    113,   -476,  -1094,    257
+  ], [
+      4710,   9661,   1073,  -2467,   3274,  -1354,  -5697,    -70,   -371,   -654,
+     -2777,   -308,   -633,    709,   1455,    161,   -941,  -1930,   -214,    493
+  ], [
+      1843,  -3624,  12422,   6898,  -1559,   -207,   -802,  -9419,  -2904,   -148,
+       407,  -1397,   2748,   -775,   1526,  -5230,    175,   -344,   1182,    656
+  ], [
+      1433,   2394,   2507,   1380,   8780,   -125,   -349,   -383,   -116,  -4705,
+      -209,   -219,   -366,   -120,   -201,   -211,   -768,  -1283,  -1343,   -740
+  ], [
+     -1712,  12915,   5883,  -2197,    991,   -179, -10181,  -2112,   -294,    -60,
+      1350,    615,  -4638,   -229,   1732,    789,    103,   -781,   -356,    133
+  ], [
+     15072,   2158,  -1245,    910,   -496, -13865,   -284,    -94,    -50,    -15,
+     -1986,   1145,    164,   -837,   -119,     69,    456,     65,    -37,     27
+  ], [
+      4655,   7319,   4916,    586,  -3381,  -1322,  -3270,  -1475,    -20,   -697,
+     -2079,  -1396,  -2196,   -166,   -261,   -175,    960,   1510,   1014,    120
+  ], [
+      1191,  -2140,   5120,  13498,  -1418,    -86,   -279,  -1600, -11121,   -122,
+       155,   -372,    669,   -981,   1763,  -4218,    103,   -185,    443,   1168
+  ], [
+     -1530,   -817,   8191,   9632,  -1452,   -143,    -40,  -4095,  -5663,   -128,
+       -76,    765,    408,    900,    480,  -4815,   -135,    -72,    726,    854
+  ], [
+     -3236,    607,   1696,  -2106,  11485,   -639,    -22,   -175,   -270,  -8051,
+       119,    335,    -62,   -416,     78,    218,   2268,   -425,  -1189,   1476
+  ], [
+      3203,  -1903,   -837,   9679,   7057,   -626,   -221,    -42,  -5718,  -3039,
+       372,    163,    -97,  -1892,   1124,    494,  -1380,    819,    360,  -4169
+  ], [
+       213,   -655,  17015,    620,   -384,     -2,    -26, -17671,    -23,     -9,
+         8,   -221,    681,     -8,     24,   -644,      5,    -15,    399,     14
+  ], [
+      5088,     35,  -3339,   3726,   8488,  -1580,      0,   -680,   -847,  -4397,
+       -10,   1037,      7,  -1157,     -8,    759,  -2636,    -18,   1730,  -1930
+  ], [
+      -988,   1454,  -2688,  15039,   2682,    -59,   -129,   -441, -13805,   -439,
+        87,   -162,    238,    907,  -1335,   2467,    161,   -238,    440,  -2462
+  ], [
+     -4865,  -2842,    -53,   5495,   6523,  -1445,   -493,      0,  -1843,  -2597,
+      -844,    -16,     -9,   1632,    953,     18,   1937,   1131,     21,  -2188
+  ], [
+      3076,  15069,  -2914,   1810,   -971,   -577, -13860,   -518,   -200,    -57,
+     -2829,    547,   2680,   -339,  -1665,    322,    182,    893,   -172,    107
+  ], [
+      1311,   5355,  11054,   2299,  -3654,   -105,  -1750,  -7458,   -322,   -814,
+      -428,   -885,  -3613,   -184,   -751,  -1551,    292,   1194,   2465,    512
+  ], [
+      4035,   5619,   4618,   1815,   1912,   -994,  -1927,  -1301,   -201,   -223,
+     -1384,  -1137,  -1583,   -447,   -622,   -511,   -471,   -656,   -539,   -211
+  ], [
+     -2131,   2754,  -4501,  12879,   7432,   -277,   -463,  -1236, -10124,  -3371,
+       358,   -585,    756,   1675,  -2165,   3538,    967,  -1249,   2042,  -5842
+  ], [
+      5618,   -515,   3219,  -4149,   4857,  -1926,    -16,   -632,  -1050,  -1440,
+       176,  -1104,    101,   1422,   -130,    815,  -1666,    152,   -954,   1230
+  ], [
+      1838,  -1709,   1139,  16867,    716,   -206,   -178,    -79, -17366,    -31,
+       191,   -127,    118,  -1892,   1759,  -1173,    -80,     74,    -49,   -737
+  ], [
+      1978,  -3845,  10050,  11854,  -2492,   -238,   -902,  -6164,  -8576,   -379,
+       464,  -1213,   2358,  -1431,   2782,  -7271,    301,   -585,   1529,   1803
+  ], [
+     -2600,  11246,  11289,  -3647,   1463,   -412,  -7720,  -7778,   -812,   -130,
+      1784,   1791,  -7749,   -578,   2504,   2513,    232,  -1004,  -1008,    325
+  ], [
+      3442,    907,   2725,   8970,   3638,   -723,    -50,   -453,  -4911,   -808,
+      -190,   -572,   -150,  -1884,   -496,  -1492,   -764,   -201,   -605,  -1992
+  ], [
+      -126,  17498,   3481,  -2003,   1090,      0, -18689,   -739,   -244,    -72,
+       135,     26,  -3717,    -15,   2139,    425,      8,  -1165,   -231,    133
+  ], [
+     -1814,   1048,  -2164,   4070,  16272,   -200,    -67,   -285,  -1011, -16160,
+       116,   -239,    138,    450,   -260,    537,   1801,  -1041,   2149,  -4042
+  ], [
+      9354,  12580,  -1883,    962,   -617,  -5341,  -9660,   -216,    -56,    -23,
+     -7183,   1075,   1446,   -549,   -738,    110,    352,    474,    -71,     36
+  ], [
+      1708,   4199,   7387,   6335,   1003,   -178,  -1076,  -3330,  -2449,    -61,
+      -437,   -770,  -1893,   -660,  -1623,  -2856,   -104,   -257,   -452,   -388
+  ], [
+     -2624,   5623,  17310,  -2353,    592,   -420,  -1930, -18288,   -338,    -21,
+       900,   2772,  -5941,   -376,    807,   2486,     94,   -203,   -625,     85
+  ], [
+      1211,   -850,   1193,  -1926,  15992,    -89,    -44,    -86,   -226, -15609,
+        62,    -88,     61,    142,   -100,    140,  -1182,    830,  -1165,   1880
+  ], [
+      3983,  -2054,  11506,    -19,   3622,   -968,   -257,  -8080,      0,   -801,
+       499,  -2797,   1442,      4,     -2,     13,   -880,    454,  -2544,      4
+  ], [
+      -786,  -1354,  16092,   7246,  -1665,    -37,   -111, -15805,  -3205,   -169,
+       -65,    772,   1330,    348,    599,  -7117,    -80,   -137,   1636,    736
+  ], [
+     -4316,   -511,   6674,  11665,   4633,  -1137,    -15,  -2719,  -8305,  -1310,
+      -134,   1758,    208,   3073,    364,  -4752,   1220,    144,  -1887,  -3299
+  ], [
+      7912,   4557,   1937,   1885,   7037,  -3821,  -1267,   -229,   -216,  -3022,
+     -2200,   -935,   -538,   -910,   -524,   -222,  -3398,  -1957,   -832,   -809
+  ], [
+      3434,   2967,   5867,   8196,   8766,   -720,   -537,  -2101,  -4100,  -4690,
+      -622,  -1230,  -1062,  -1718,  -1484,  -2935,  -1837,  -1588,  -3139,  -4385
+  ], [
+      5881,   9176,   8119,   3934,   3355,  -2111,  -5139,  -4023,   -944,   -687,
+     -3294,  -2914,  -4547,  -1412,  -2203,  -1949,  -1204,  -1879,  -1662,   -805
+  ]
+];
+
+const PULSE_POSITIONS: [[u32; SUBFRAME_LEN/2]; MAX_PULSES] = [
+  [
+    118755, 98280, 80730, 65780, 53130, 42504, 33649, 26334,  20349, 15504,
+     11628,  8568,  6188,  4368,  3003,  2002,  1287,   792,    462,   252,
+       126,    56,    21,     6,     1,     0,     0,     0,      0,     0
+  ], [
+     23751, 20475, 17550, 14950, 12650, 10626,  8855,  7315,   5985,  4845,
+      3876,  3060,  2380,  1820,  1365,  1001,   715,   495,    330,   210,
+       126,    70,    35,    15,     5,     1,     0,     0,      0,     0
+  ], [
+      3654,  3276,  2925,  2600,  2300,  2024,  1771,  1540,   1330,  1140,
+       969,   816,   680,   560,   455,   364,   286,   220,    165,   120,
+        84,    56,    35,    20,    10,     4,     1,     0,      0,     0
+  ], [
+       406,   378,   351,   325,   300,   276,   253,   231,    210,   190,
+       171,   153,   136,   120,   105,    91,    78,    66,     55,    45,
+        36,    28,    21,    15,    10,     6,     3,     1,      0,     0
+  ], [
+        29,    28,    27,    26,    25,    24,    23,    22,     21,    20,
+        19,    18,    17,    16,    15,    14,    13,    12,     11,    10,
+         9,     8,     7,     6,     5,     4,     3,     2,      1,     0
+  ], [
+     1; SUBFRAME_LEN/2
+  ]
+];
+
+const PITCH_CONTRIBUTION: [i32; 340] = [
+    60,     0,  0,  2489, 60,     0,  0,  5217,  1,  6171,  0,  3953,  0, 10364,  1,  9357,
+    -1,  8843,  1,  9396,  0,  5794, -1, 10816,  2, 11606, -2, 12072,  0,  8616,  1, 12170,
+     0, 14440,  0,  7787, -1, 13721,  0, 18205,  0, 14471,  0, 15807,  1, 15275,  0, 13480,
+    -1, 18375, -1,     0,  1, 11194, -1, 13010,  1, 18836, -2, 20354,  1, 16233, -1,     0,
+    60,     0,  0, 12130,  0, 13385,  1, 17834,  1, 20875,  0, 21996,  1,     0,  1, 18277,
+    -1, 21321,  1, 13738, -1, 19094, -1, 20387, -1,     0,  0, 21008, 60,     0, -2, 22807,
+     0, 15900,  1,     0,  0, 17989, -1, 22259,  1, 24395,  1, 23138,  0, 23948,  1, 22997,
+     2, 22604, -1, 25942,  0, 26246,  1, 25321,  0, 26423,  0, 24061,  0, 27247, 60,     0,
+    -1, 25572,  1, 23918,  1, 25930,  2, 26408, -1, 19049,  1, 27357, -1, 24538, 60,     0,
+    -1, 25093,  0, 28549,  1,     0,  0, 22793, -1, 25659,  0, 29377,  0, 30276,  0, 26198,
+     1, 22521, -1, 28919,  0, 27384,  1, 30162, -1,     0,  0, 24237, -1, 30062,  0, 21763,
+     1, 30917, 60,     0,  0, 31284,  0, 29433,  1, 26821,  1, 28655,  0, 31327,  2, 30799,
+     1, 31389,  0, 32322,  1, 31760, -2, 31830,  0, 26936, -1, 31180,  1, 30875,  0, 27873,
+    -1, 30429,  1, 31050,  0,     0,  0, 31912,  1, 31611,  0, 31565,  0, 25557,  0, 31357,
+    60,     0,  1, 29536,  1, 28985, -1, 26984, -1, 31587,  2, 30836, -2, 31133,  0, 30243,
+    -1, 30742, -1, 32090, 60,     0,  2, 30902, 60,     0,  0, 30027,  0, 29042, 60,     0,
+     0, 31756,  0, 24553,  0, 25636, -2, 30501, 60,     0, -1, 29617,  0, 30649, 60,     0,
+     0, 29274,  2, 30415,  0, 27480,  0, 31213, -1, 28147,  0, 30600,  1, 31652,  2, 29068,
+    60,     0,  1, 28571,  1, 28730,  1, 31422,  0, 28257,  0, 24797, 60,     0,  0,     0,
+    60,     0,  0, 22105,  0, 27852, 60,     0, 60,     0, -1, 24214,  0, 24642,  0, 23305,
+    60,     0, 60,     0,  1, 22883,  0, 21601, 60,     0,  2, 25650, 60,     0, -2, 31253,
+    -2, 25144,  0, 17998
+];
+
+const POSTFILTER_COEFFS: [[i16; LPC_ORDER]; 2] = [
+    [ 21299, 13844,  8999,  5849, 3802, 2471, 1606, 1044,  679,  441 ],
+    [ 24576, 18432, 13824, 10368, 7776, 5832, 4374, 3281, 2460, 1845 ]
+];
diff --git a/nihav-vivo/src/codecs/mod.rs b/nihav-vivo/src/codecs/mod.rs
new file mode 100644 (file)
index 0000000..b2ceb12
--- /dev/null
@@ -0,0 +1,31 @@
+use nihav_core::codecs::*;
+
+#[allow(unused_macros)]
+macro_rules! validate {
+    ($a:expr) => { if !$a { println!("check failed at {}:{}", file!(), line!()); return Err(DecoderError::InvalidData); } };
+}
+
+#[cfg(any(feature="decoder_vivo1", feature="decoder_vivo2"))]
+mod vivo;
+#[cfg(feature="decoder_g723_1")]
+mod g723_1;
+#[cfg(feature="decoder_siren")]
+mod siren;
+
+const VIVO_CODECS: &[DecoderInfo] = &[
+#[cfg(feature="decoder_vivo1")]
+    DecoderInfo { name: "vivo1", get_decoder: vivo::get_decoder },
+#[cfg(feature="decoder_vivo2")]
+    DecoderInfo { name: "vivo2", get_decoder: vivo::get_decoder },
+#[cfg(feature="decoder_g723_1")]
+    DecoderInfo { name: "g723.1", get_decoder: g723_1::get_decoder },
+#[cfg(feature="decoder_siren")]
+    DecoderInfo { name: "siren", get_decoder: siren::get_decoder },
+];
+
+/// Registers all available codecs provided by this crate.
+pub fn vivo_register_all_codecs(rd: &mut RegisteredDecoders) {
+    for decoder in VIVO_CODECS.iter() {
+        rd.add_decoder(decoder.clone());
+    }
+}
diff --git a/nihav-vivo/src/codecs/siren.rs b/nihav-vivo/src/codecs/siren.rs
new file mode 100644 (file)
index 0000000..a9a1e2e
--- /dev/null
@@ -0,0 +1,895 @@
+use nihav_core::formats::SND_F32P_FORMAT;
+use nihav_core::frame::*;
+use nihav_core::codecs::*;
+use nihav_core::io::bitreader::*;
+use nihav_core::io::codebook::*;
+use nihav_codec_support::dsp::mdct::IMDCT;
+use nihav_codec_support::dsp::window::{generate_window, WindowType};
+use std::str::FromStr;
+
+const SAMPLES: usize = 320;
+const NUM_REGIONS: usize = 14;
+const REGION_SIZE: usize = 20;
+const NUM_DIFF_CB: usize = NUM_REGIONS - 1;
+
+struct PRNG {
+    dw: [u16; 4],
+}
+
+impl PRNG {
+    fn new() -> Self { Self{ dw: [1; 4] } }
+    fn get_dw(&mut self) -> u16 {
+        let mut ret = self.dw[0].wrapping_add(self.dw[3]);
+        if (ret & 0x8000) != 0 {
+            ret = ret.wrapping_add(1);
+        }
+
+        self.dw[0] = self.dw[1];
+        self.dw[1] = self.dw[2];
+        self.dw[2] = self.dw[3];
+        self.dw[3] = ret;
+
+        ret
+    }
+}
+
+struct SirenDecoder {
+    chmap:              NAChannelMap,
+    ainfo:              NAAudioInfo,
+    info:               NACodecInfoRef,
+    diff_cb:            Vec<Codebook<i8>>,
+    vec_cb:             [Codebook<u16>; 7],
+    imdct:              IMDCT,
+
+    coeffs:             [f32; SAMPLES],
+    delay:              [f32; SAMPLES],
+    synth_buf:          [f32; SAMPLES * 2],
+    last_good_frame:    [f32; SAMPLES],
+    window:             [f32; SAMPLES],
+    quant_tab:          [f32; 64],
+
+    pow_index:          [i32; NUM_REGIONS],
+    region_quant:       [f32; NUM_REGIONS],
+    power_cat:          [usize; NUM_REGIONS],
+    cat_balance:        [usize; 16],
+
+    rng:                PRNG,
+}
+
+fn map_idx_diff(idx: usize) -> i8 { (idx as i8) - 12 }
+fn map_idx0(idx: usize) -> u16 { VEC_CB0_SYMS[idx] }
+fn map_idx1(idx: usize) -> u16 { VEC_CB1_SYMS[idx] }
+fn map_idx2(idx: usize) -> u16 { VEC_CB2_SYMS[idx] }
+fn map_idx3(idx: usize) -> u16 { VEC_CB3_SYMS[idx] }
+fn map_idx4(idx: usize) -> u16 { VEC_CB4_SYMS[idx] }
+fn map_idx5(idx: usize) -> u16 { VEC_CB5_SYMS[idx] }
+fn map_idx6(idx: usize) -> u16 { VEC_CB6_SYMS[idx] }
+impl SirenDecoder {
+    fn new() -> Self {
+        let mut diff_cb = Vec::with_capacity(NUM_DIFF_CB);
+        for i in 0..NUM_DIFF_CB {
+            let mut cr = TableCodebookDescReader::new(&DIFF_CODE_BITS[i], &DIFF_CODE_LENS[i], map_idx_diff);
+            let cb = Codebook::new(&mut cr, CodebookMode::MSB).unwrap();
+            diff_cb.push(cb);
+        }
+        let mut cr = TableCodebookDescReader::new(&VEC_CB0_BITS, &VEC_CB0_LENS, map_idx0);
+        let vec_cb0 = Codebook::new(&mut cr, CodebookMode::MSB).unwrap();
+        let mut cr = TableCodebookDescReader::new(&VEC_CB1_BITS, &VEC_CB1_LENS, map_idx1);
+        let vec_cb1 = Codebook::new(&mut cr, CodebookMode::MSB).unwrap();
+        let mut cr = TableCodebookDescReader::new(&VEC_CB2_BITS, &VEC_CB2_LENS, map_idx2);
+        let vec_cb2 = Codebook::new(&mut cr, CodebookMode::MSB).unwrap();
+        let mut cr = TableCodebookDescReader::new(&VEC_CB3_BITS, &VEC_CB3_LENS, map_idx3);
+        let vec_cb3 = Codebook::new(&mut cr, CodebookMode::MSB).unwrap();
+        let mut cr = TableCodebookDescReader::new(&VEC_CB4_BITS, &VEC_CB4_LENS, map_idx4);
+        let vec_cb4 = Codebook::new(&mut cr, CodebookMode::MSB).unwrap();
+        let mut cr = TableCodebookDescReader::new(&VEC_CB5_BITS, &VEC_CB5_LENS, map_idx5);
+        let vec_cb5 = Codebook::new(&mut cr, CodebookMode::MSB).unwrap();
+        let mut cr = TableCodebookDescReader::new(&VEC_CB6_BITS, &VEC_CB6_LENS, map_idx6);
+        let vec_cb6 = Codebook::new(&mut cr, CodebookMode::MSB).unwrap();
+        let vec_cb = [vec_cb0, vec_cb1, vec_cb2, vec_cb3, vec_cb4, vec_cb5, vec_cb6];
+
+        let mut window = [0.0f32; SAMPLES];
+        generate_window(WindowType::Sine, 1.0 / 320.0 / 8.0, SAMPLES, true, &mut window);
+
+        let mut quant_tab = [0.0; 64];
+        for i in 0..64 {
+            quant_tab[i] = 2.0f32.powf((((i as i32) - 24) as f32) / 2.0);
+        }
+
+        SirenDecoder {
+            chmap:              NAChannelMap::from_str("C").unwrap(),
+            ainfo:              NAAudioInfo::new(16000, 1, SND_F32P_FORMAT, SAMPLES),
+            info:               NACodecInfo::new_dummy(),
+            diff_cb, vec_cb,
+
+            coeffs:             [0.0; SAMPLES],
+            delay:              [0.0; SAMPLES],
+            synth_buf:          [0.0; SAMPLES * 2],
+            last_good_frame:    [0.0; SAMPLES],
+            imdct:              IMDCT::new(SAMPLES * 2, false),
+            window, quant_tab,
+
+            pow_index:          [0; NUM_REGIONS],
+            region_quant:       [0.0; NUM_REGIONS],
+            power_cat:          [0; NUM_REGIONS],
+            cat_balance:        [0; 16],
+
+            rng:                PRNG::new(),
+        }
+    }
+    fn decode_envelope(&mut self, br: &mut BitReader) -> DecoderResult<()> {
+        self.pow_index[0]               = (br.read(5)? as i32) - 7;
+
+        for i in 1..NUM_REGIONS {
+            let diff                    = br.read_cb(&self.diff_cb[i - 1])?;
+            self.pow_index[i] = (self.pow_index[i - 1] + i32::from(diff)).max(-24).min(39);
+        }
+        for i in 0..NUM_REGIONS {
+            self.region_quant[i]        = self.quant_tab[(self.pow_index[i] + 24) as usize];
+        }
+
+        Ok(())
+    }
+    fn allocate_bits(&mut self, tot_bits: u32, rate_ctl: usize) -> DecoderResult<()> {
+        const CATEGORY_BITS: [u32; 8] = [ 52, 47, 43, 37, 29, 22, 16, 0 ];
+        const MAX_RC: usize = 15;
+
+        let mut offset = -32;
+        let mut delta = 32;
+        while delta > 0 {
+            let mut bitpool = 0;
+            for reg in 0..NUM_REGIONS {
+                let cat = ((delta + offset - self.pow_index[reg]) >> 1).max(0).min(7) as usize;
+                //self.power_cat[reg] = cat;
+                bitpool += CATEGORY_BITS[cat];
+            }
+            if bitpool >= tot_bits - 32 {
+                offset += delta;
+            }
+            delta >>= 1;
+        }
+
+        let mut bitpool = 0;
+        let mut max_rate_cat = [0; NUM_REGIONS];
+        let mut min_rate_cat = [0; NUM_REGIONS];
+        for reg in 0..NUM_REGIONS {
+            let cat = ((offset - self.pow_index[reg]) >> 1).max(0).min(7) as usize;
+            max_rate_cat[reg] = cat;
+            min_rate_cat[reg] = cat;
+            self.power_cat[reg] = cat;
+            bitpool += CATEGORY_BITS[cat];
+        }
+
+        let mut min_bitpool = bitpool;
+        let mut max_bitpool = bitpool;
+        let mut min_offset = MAX_RC + 1;
+        let mut max_offset = MAX_RC + 1;
+        let mut temp_cat = [0; 64];
+        for _ in 0..MAX_RC {
+            if min_bitpool + max_bitpool > tot_bits * 2 {
+                let mut max_value = -99;
+                let mut min_idx = 0;
+                for reg in (0..NUM_REGIONS).rev() {
+                    if min_rate_cat[reg] >= 7 { continue; }
+                    let val = offset - self.pow_index[reg] - 2 * (min_rate_cat[reg] as i32);
+                    if max_value < val {
+                        max_value = val;
+                        min_idx = reg;
+                    }
+                }
+                validate!(max_value != -99);
+                temp_cat[min_offset] = min_idx;
+                min_offset += 1;
+                min_bitpool = min_bitpool + CATEGORY_BITS[min_rate_cat[min_idx] + 1] - CATEGORY_BITS[min_rate_cat[min_idx]];
+                min_rate_cat[min_idx] += 1;
+            } else {
+                let mut min_value = 99;
+                let mut max_idx = 0;
+                for reg in 0..NUM_REGIONS {
+                    if max_rate_cat[reg] == 0 { continue; }
+                    let val = offset - self.pow_index[reg] - 2 * (max_rate_cat[reg] as i32);
+                    if min_value > val {
+                        min_value = val;
+                        max_idx = reg;
+                    }
+                }
+                validate!(min_value != 99);
+                max_offset -= 1;
+                temp_cat[max_offset] = max_idx;
+                max_bitpool = max_bitpool + CATEGORY_BITS[max_rate_cat[max_idx] - 1] - CATEGORY_BITS[max_rate_cat[max_idx]];
+                max_rate_cat[max_idx] -= 1;
+            }
+        }
+
+        self.power_cat = max_rate_cat;
+
+        for i in 0..MAX_RC {
+            self.cat_balance[i] = temp_cat[max_offset + i];
+        }
+
+        for cat in self.cat_balance.iter().take(rate_ctl) {
+            self.power_cat[*cat] += 1;
+        }
+        Ok(())
+    }
+    fn decode_coefficients(&mut self, br: &mut BitReader) -> DecoderResult<bool> {
+        const NUM_VECTORS: [u8; 8] = [ 10, 10, 10, 5, 5, 4, 4, 20 ];
+        const VECTOR_SIZE: [u8; 8] = [ 2, 2, 2, 4, 4, 5, 5, 1 ];
+        const INDEX_BITS:  [u8; 8] = [ 4, 4, 3, 3, 2, 2, 1, 0 ];
+
+        let mut error = false;
+        self.coeffs = [0.0; SAMPLES];
+        for (reg, coeffs) in self.coeffs.chunks_exact_mut(REGION_SIZE).take(NUM_REGIONS).enumerate() {
+            let mut cat = self.power_cat[reg];
+            if cat < 7 {
+                let cb = &self.vec_cb[cat];
+                let num_vecs = NUM_VECTORS[cat] as usize;
+                let vec_len  = VECTOR_SIZE[cat] as usize;
+
+                'vec_loop: for i in 0..num_vecs {
+                    let ret             = br.read_cb(cb);
+                    if let Err(_) = ret {
+                        error = true;
+                        break 'vec_loop;
+                    }
+                    let mut idx = ret.ok().unwrap_or(0) as usize;
+                    let mask = (1 << INDEX_BITS[cat]) - 1;
+                    for j in 0..vec_len {
+                        let coef = QUANT_LEVELS[cat][idx & mask] * self.region_quant[reg];
+                        idx >>= INDEX_BITS[cat];
+                        if coef != 0.0 {
+                            if br.left() == 0 {
+                                break 'vec_loop;
+                            }
+                            coeffs[i * vec_len + j] = if br.read_bool()? { -coef } else { coef };
+                        }
+                    }
+                }
+                if error {
+                    cat = 7;
+                    for i in reg..NUM_REGIONS {
+                        self.power_cat[i] = 7;
+                    }
+                }
+            }
+            let noise_val = match cat {
+                    5 => {
+                        let mut nz_count = 0;
+                        for el in coeffs.iter() {
+                            if *el != 0.0 {
+                                if *el > 2.0 * self.region_quant[reg] {
+                                    nz_count += 4;
+                                }
+                                nz_count += 1;
+                            }
+                        }
+                        if nz_count < REGION_SIZE {
+                            self.region_quant[reg] * CAT5_NOISE_FACTOR[nz_count]
+                        } else {
+                            0.0
+                        }
+                    },
+                    6 => {
+                        let mut nz_count = 0;
+                        for el in coeffs.iter() {
+                            if *el != 0.0 {
+                                nz_count += 1;
+                            }
+                        }
+                        self.region_quant[reg] * CAT6_NOISE_FACTOR[nz_count]
+                    },
+                    7 => {
+                        self.region_quant[reg] * 0.70711
+                    },
+                    _ => 0.0,
+                };
+            if cat >= 5 {
+                let mut dw1 = self.rng.get_dw();
+                let mut dw2 = self.rng.get_dw();
+                for pair in coeffs.chunks_exact_mut(2) {
+                    if (cat == 7) || pair[0] == 0.0 {
+                        pair[0] = if (dw1 & 1) != 0 { noise_val } else { -noise_val };
+                    }
+                    if (cat == 7) || pair[1] == 0.0 {
+                        pair[1] = if (dw2 & 1) != 0 { noise_val } else { -noise_val };
+                    }
+                    dw1 >>= 1;
+                    dw2 >>= 1;
+                }
+            }
+        }
+        Ok(error)
+    }
+}
+
+impl NADecoder for SirenDecoder {
+    fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+        if let NACodecTypeInfo::Audio(_ainfo) = info.get_properties() {
+            self.info = info.replace_info(NACodecTypeInfo::Audio(self.ainfo));
+            Ok(())
+        } else {
+            Err(DecoderError::InvalidData)
+        }
+    }
+    fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+        let info = pkt.get_stream().get_info();
+        validate!(info.get_properties().is_audio());
+        let src = pkt.get_buffer();
+
+        let mut br = BitReader::new(src.as_slice(), BitReaderMode::BE);
+        self.decode_envelope(&mut br)?;
+        let rate_ctl = br.read(4)? as usize;
+        self.allocate_bits(br.left() as u32, rate_ctl)?;
+        self.decode_coefficients(&mut br)?;
+
+        let mut bad_frame = false;
+        while br.left() > 0 {
+            bad_frame                   |= !br.read_bool().ok().unwrap_or(true);
+        }
+        for el in self.pow_index.iter() {
+            if *el > 33 {
+                bad_frame = true;
+            }
+        }
+
+        if bad_frame {
+            self.coeffs.copy_from_slice(&self.last_good_frame);
+            self.last_good_frame = [0.0; SAMPLES];
+        } else {
+            self.last_good_frame.copy_from_slice(&self.coeffs);
+        }
+
+        let abuf = alloc_audio_buffer(self.ainfo, SAMPLES, self.chmap.clone())?;
+        let mut adata = abuf.get_abuf_f32().unwrap();
+        let asamples = adata.get_data_mut().unwrap();
+
+        self.imdct.imdct_half(&self.coeffs, &mut self.synth_buf);
+        for i in (0..SAMPLES/2).step_by(2) {
+            let idx0 = i;
+            let idx1 = SAMPLES / 2 - 1 - i;
+            let idx2 = SAMPLES / 2     + i;
+            let idx3 = SAMPLES     - 1 - i;
+            // samples are actually in reverse order
+            let c3 = self.synth_buf[idx0];
+            let c2 = self.synth_buf[idx1];
+            let c1 = self.synth_buf[idx2];
+            let c0 = self.synth_buf[idx3];
+
+            let d0 = self.delay[idx0];
+            let d1 = self.delay[idx1];
+
+            let w0 = self.window[idx0];
+            let w1 = self.window[idx1];
+            let w2 = self.window[idx2];
+            let w3 = self.window[idx3];
+
+            asamples[idx3] = c1 * w3 - d0 * w0;
+            asamples[idx2] = c0 * w2 - d1 * w1;
+            asamples[idx1] = d1 * w2 + c0 * w1;
+            asamples[idx0] = d0 * w3 + c1 * w0;
+
+            self.delay[idx0] = c2;
+            self.delay[idx1] = c3;
+        }
+
+        let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), abuf);
+        frm.set_keyframe(true);
+        Ok(frm.into_ref())
+    }
+    fn flush(&mut self) {
+        self.delay           = [0.0; SAMPLES];
+        self.last_good_frame = [0.0; SAMPLES];
+    }
+}
+
+pub fn get_decoder() -> Box<dyn NADecoder + Send> {
+    Box::new(SirenDecoder::new())
+}
+
+#[cfg(test)]
+mod test {
+    use nihav_core::codecs::RegisteredDecoders;
+    use nihav_core::demuxers::RegisteredDemuxers;
+    use nihav_codec_support::test::dec_video::*;
+    use crate::vivo_register_all_codecs;
+    use crate::vivo_register_all_demuxers;
+    #[test]
+    fn test_siren() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        vivo_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        vivo_register_all_codecs(&mut dec_reg);
+
+        let file = "assets/Misc/favmovie.viv";
+        //let file = "assets/Misc/gr_al.viv";
+        test_decode_audio("vivo", file, None, None/*Some("siren")*/, &dmx_reg, &dec_reg);
+    }
+}
+
+const DIFF_CODE_BITS: [[u16; 24]; NUM_DIFF_CB] = [
+  [
+    0x0008, 0x0026, 0x0012, 0x000A, 0x0007, 0x0006, 0x0003, 0x0002,
+    0x0000, 0x0001, 0x0007, 0x0006, 0x0005, 0x0004, 0x000B, 0x004E,
+    0x009E, 0x013E, 0x04FE, 0x04FF, 0x09F8, 0x09F9, 0x09FA, 0x09FB
+  ], [
+    0x0024, 0x0008, 0x0003, 0x0005, 0x0000, 0x0001, 0x0007, 0x0006,
+    0x0004, 0x0003, 0x0002, 0x0005, 0x0003, 0x0004, 0x0005, 0x0013,
+    0x004A, 0x0096, 0x012E, 0x04BD, 0x04BE, 0x04BF, 0x0978, 0x0979
+  ], [
+    0x0A16, 0x0284, 0x00A0, 0x0029, 0x0005, 0x000B, 0x0007, 0x0005,
+    0x0004, 0x0001, 0x0000, 0x0006, 0x0004, 0x0007, 0x0003, 0x0006,
+    0x0004, 0x0015, 0x0051, 0x0143, 0x050A, 0x142F, 0x285C, 0x285D
+  ], [
+    0x0B7C, 0x016E, 0x00B5, 0x00B4, 0x002F, 0x002E, 0x001B, 0x000A,
+    0x0008, 0x0005, 0x0001, 0x0000, 0x0003, 0x0007, 0x0004, 0x0009,
+    0x000C, 0x001A, 0x002C, 0x00B6, 0x02DE, 0x0B7D, 0x0B7E, 0x0B7F
+  ], [
+    0x0F8E, 0x1F1F, 0x03E2, 0x00F9, 0x003F, 0x001A, 0x0013, 0x0012,
+    0x000E, 0x0008, 0x0006, 0x0001, 0x0000, 0x0002, 0x0005, 0x0007,
+    0x000C, 0x001E, 0x001B, 0x007D, 0x01F0, 0x07C6, 0x3E3C, 0x3E3D
+  ], [
+    0x0CB6, 0x065A, 0x0197, 0x00CE, 0x00CA, 0x0064, 0x001E, 0x000E,
+    0x0003, 0x0005, 0x0003, 0x0000, 0x0002, 0x0004, 0x0002, 0x000D,
+    0x0018, 0x001F, 0x0066, 0x00CF, 0x032C, 0x196F, 0x32DC, 0x32DD
+  ], [
+    0x0456, 0x08A8, 0x0457, 0x008B, 0x0023, 0x0009, 0x0003, 0x0014,
+    0x000B, 0x0004, 0x0002, 0x0001, 0x0003, 0x0003, 0x0001, 0x0000,
+    0x0015, 0x0005, 0x0010, 0x0044, 0x0114, 0x08A9, 0x08AA, 0x08AB
+  ], [
+    0x03F5, 0x03F6, 0x007F, 0x003E, 0x001D, 0x0006, 0x0004, 0x0010,
+    0x0000, 0x0001, 0x0003, 0x0002, 0x0003, 0x0001, 0x0005, 0x0009,
+    0x0011, 0x0005, 0x001C, 0x001E, 0x00FC, 0x03F7, 0x07E8, 0x07E9
+  ], [
+    0x017D, 0x017C, 0x0174, 0x00BF, 0x005E, 0x002C, 0x0010, 0x000A,
+    0x0007, 0x0003, 0x0001, 0x0000, 0x0002, 0x0006, 0x0009, 0x0011,
+    0x002D, 0x005C, 0x00BB, 0x02EA, 0x05D6, 0x0BAF, 0x175C, 0x175D
+  ], [
+    0x0BDC, 0x02F6, 0x00BC, 0x002D, 0x002B, 0x000A, 0x0004, 0x0003,
+    0x0006, 0x0004, 0x0002, 0x0000, 0x0003, 0x0007, 0x000B, 0x0014,
+    0x002A, 0x002C, 0x002E, 0x005F, 0x017A, 0x0BDD, 0x0BDE, 0x0BDF
+  ], [
+    0x02EF, 0x005C, 0x002D, 0x0014, 0x001A, 0x0004, 0x000C, 0x0007,
+    0x0004, 0x0000, 0x0004, 0x0001, 0x0003, 0x0005, 0x0005, 0x0003,
+    0x001B, 0x0015, 0x002C, 0x002F, 0x00BA, 0x0176, 0x05DC, 0x05DD
+  ], [
+    0xB204, 0x1641, 0x0B21, 0x0591, 0x0165, 0x002D, 0x0017, 0x0006,
+    0x000A, 0x0007, 0x0002, 0x0002, 0x0003, 0x0000, 0x0004, 0x0006,
+    0x0007, 0x0058, 0x00B3, 0x02C9, 0x2C80, 0xB205, 0xB206, 0xB207
+  ], [
+    0x09CF, 0x1398, 0x139A, 0x1399, 0x0138, 0x004F, 0x0026, 0x0024,
+    0x001E, 0x000E, 0x0006, 0x0000, 0x0002, 0x0001, 0x0003, 0x0005,
+    0x0008, 0x001F, 0x0025, 0x009D, 0x0272, 0x139B, 0x139C, 0x139D
+  ]
+];
+const DIFF_CODE_LENS: [[u8; 24]; 13/*27*/] = [
+ [  4,  6,  5,  5,  4,  4,  4,  4,  4,  4,  3,  3,  3,  4,  5,  7,  8,  9, 11, 11, 12, 12, 12, 12 ],
+ [ 10,  8,  6,  5,  5,  4,  3,  3,  3,  3,  3,  3,  4,  5,  7,  9, 11, 12, 13, 15, 15, 15, 16, 16 ],
+ [ 12, 10,  8,  6,  5,  4,  4,  4,  4,  4,  4,  3,  3,  3,  4,  4,  5,  5,  7,  9, 11, 13, 14, 14 ],
+ [ 13, 10,  9,  9,  7,  7,  5,  5,  4,  3,  3,  3,  3,  3,  4,  4,  4,  5,  7,  9, 11, 13, 13, 13 ],
+ [ 12, 13, 10,  8,  6,  6,  5,  5,  4,  4,  3,  3,  3,  3,  3,  4,  5,  5,  6,  7,  9, 11, 14, 14 ],
+ [ 12, 11,  9,  8,  8,  7,  5,  4,  4,  3,  3,  3,  3,  3,  4,  4,  5,  5,  7,  8, 10, 13, 14, 14 ],
+ [ 15, 16, 15, 12, 10,  8,  6,  5,  4,  3,  3,  3,  2,  3,  4,  5,  5,  7,  9, 11, 13, 16, 16, 16 ],
+ [ 14, 14, 11, 10,  9,  7,  7,  5,  5,  4,  3,  3,  2,  3,  3,  4,  5,  7,  9,  9, 12, 14, 15, 15 ],
+ [  9,  9,  9,  8,  7,  6,  5,  4,  3,  3,  3,  3,  3,  3,  4,  5,  6,  7,  8, 10, 11, 12, 13, 13 ],
+ [ 14, 12, 10,  8,  6,  6,  5,  4,  3,  3,  3,  3,  3,  3,  4,  5,  6,  8,  8,  9, 11, 14, 14, 14 ],
+ [ 13, 10,  9,  8,  6,  6,  5,  4,  4,  4,  3,  3,  2,  3,  4,  5,  6,  8,  9,  9, 11, 12, 14, 14 ],
+ [ 16, 13, 12, 11,  9,  6,  5,  5,  4,  4,  4,  3,  2,  3,  3,  4,  5,  7,  8, 10, 14, 16, 16, 16 ],
+ [ 13, 14, 14, 14, 10,  8,  7,  7,  5,  4,  3,  3,  2,  3,  3,  4,  5,  5,  7,  9, 11, 14, 14, 14 ]
+];
+
+const VEC_CB0_BITS: [u16; 181] = [
+    0x0000, 0x0001, 0x0001, 0x0010, 0x0044, 0x0114, 0x0115, 0x008B,
+    0x0023, 0x0009, 0x0005, 0x0018, 0x0019, 0x000D, 0x0007, 0x0001,
+    0x0010, 0x0044, 0x0114, 0x0115, 0x022C, 0x045A, 0x045B, 0x0117,
+    0x0046, 0x0047, 0x0009, 0x0014, 0x0015, 0x0016, 0x002E, 0x005E,
+    0x005F, 0x000C, 0x001A, 0x0036, 0x0037, 0x000E, 0x001E, 0x003E,
+    0x00FC, 0x00FD, 0x01FC, 0x01FD, 0x01FE, 0x01FF, 0x0008, 0x0090,
+    0x0091, 0x0049, 0x004A, 0x004B, 0x004C, 0x009A, 0x0136, 0x09B8,
+    0x1372, 0x1373, 0x1374, 0x1375, 0x1376, 0x1377, 0x026F, 0x004E,
+    0x004F, 0x000A, 0x0016, 0x0017, 0x0018, 0x0032, 0x0033, 0x00D0,
+    0x01A2, 0x0346, 0x0347, 0x0069, 0x00D4, 0x00D5, 0x01AC, 0x06B4,
+    0x06B5, 0x06B6, 0x1ADC, 0x1ADD, 0x0D6F, 0x00D7, 0x0036, 0x0037,
+    0x000E, 0x0078, 0x0079, 0x003D, 0x003E, 0x003F, 0x0002, 0x0003,
+    0x0020, 0x0420, 0x0421, 0x0422, 0x0423, 0x0212, 0x0426, 0x0427,
+    0x0085, 0x010C, 0x010D, 0x010E, 0x010F, 0x0044, 0x0114, 0x022A,
+    0x022B, 0x0116, 0x08B8, 0x08B9, 0x045D, 0x1178, 0x1179, 0x08BD,
+    0x045F, 0x008C, 0x008D, 0x0047, 0x0012, 0x0098, 0x0099, 0x0134,
+    0x0135, 0x0136, 0x0137, 0x004E, 0x0278, 0x0279, 0x013D, 0x013E,
+    0x09F8, 0x27E4, 0x27E5, 0x13F3, 0x04FD, 0x027F, 0x000A, 0x0016,
+    0x005C, 0x00BA, 0x00BB, 0x005E, 0x02F8, 0x02F9, 0x017D, 0x00BF,
+    0x0018, 0x0032, 0x0033, 0x0034, 0x0350, 0x0351, 0x0352, 0x0353,
+    0x00D5, 0x006B, 0x006C, 0x00DA, 0x00DB, 0x006E, 0x01BC, 0x01BD,
+    0x00DF, 0x001C, 0x003A, 0x03B0, 0x0762, 0x0763, 0x01D9, 0x01DA,
+    0x01DB, 0x00EE, 0x00EF, 0x000F, 0x0001
+];
+const VEC_CB0_LENS: [u8; 181] = [
+     9,  9,  8, 11, 13, 15, 15, 14, 12, 10,  9, 11, 11, 10,  9,  6,
+     9, 11, 13, 13, 14, 15, 15, 13, 11, 11,  8,  9,  9,  9, 10, 11,
+    11,  8,  9, 10, 10,  8,  9, 10, 12, 12, 13, 13, 13, 13,  7, 11,
+    11, 10, 10, 10, 10, 11, 12, 15, 16, 16, 16, 16, 16, 16, 13, 10,
+    10,  7,  8,  8,  8,  9,  9, 11, 12, 13, 13, 10, 11, 11, 12, 14,
+    14, 14, 16, 16, 15, 11,  9,  9,  7, 10, 10,  9,  9,  9,  4,  4,
+     7, 12, 12, 12, 12, 11, 12, 12,  9, 10, 10, 10, 10,  8, 10, 11,
+    11, 10, 13, 13, 12, 14, 14, 13, 12,  9,  9,  8,  6,  9,  9, 10,
+    10, 10, 10,  8, 11, 11, 10, 10, 13, 15, 15, 14, 12, 11,  5,  6,
+     8,  9,  9,  8, 11, 11, 10,  9,  6,  7,  7,  7, 11, 11, 11, 11,
+     9,  8,  8,  9,  9,  8, 10, 10,  9,  6,  7, 11, 12, 12, 10, 10,
+    10,  9,  9,  5,  1
+];
+const VEC_CB0_SYMS: [u16; 181] = [
+     37,   51,    5,  102,   76,  139,  155,  169,  151,   41,   82,  103,
+    118,  100,    8,   32,  113,  134,  211,  182,  213,  214,  124,  183,
+     28,   29,   96,   52,   24,   67,  146,  193,  104,   35,  144,  176,
+    115,   21,  129,   70,  210,   45,  123,  197,   92,   61,   49,   74,
+    164,   56,   11,  177,   42,  192,  195,  109,  185,  156,  140,  216,
+    171,  201,  212,   27,   57,   19,   81,   50,    6,  145,   38,   12,
+     60,  122,   77,  131,  119,   59,  166,  153,  199,   93,  125,  141,
+    215,   13,   98,   53,   64,  101,   85,    9,   25,  114,   16,    1,
+      4,  137,  106,  150,  121,  149,   91,  167,   83,  116,  117,   71,
+    178,  112,  162,  179,   44,  147,  152,  198,  181,  154,  170,  138,
+    107,   10,   39,   97,   18,   40,   54,   86,   72,   88,   43,   22,
+    209,   90,  163,  133,  108,  186,  200,  184,  196,  165,   17,   33,
+      7,   26,   68,   36,  135,  208,   87,   99,   48,   20,   80,   34,
+    105,  180,   75,  194,  161,   66,   23,  160,   84,  128,   58,  148,
+     55,    3,   65,  120,  136,  168,  132,   73,   89,   69,  130,    2,    0
+];
+
+const VEC_CB1_BITS: [u16; 94] = [
+    0x0000, 0x0010, 0x0044, 0x0045, 0x0023, 0x0009, 0x0005, 0x000C,
+    0x000D, 0x000E, 0x001E, 0x001F, 0x0001, 0x0002, 0x000C, 0x0034,
+    0x01A8, 0x0352, 0x0353, 0x00D5, 0x006B, 0x0036, 0x006E, 0x006F,
+    0x01C0, 0x01C1, 0x00E1, 0x0071, 0x0072, 0x0073, 0x01D0, 0x03A2,
+    0x03A3, 0x00E9, 0x0075, 0x003B, 0x0078, 0x00F2, 0x01E6, 0x01E7,
+    0x003D, 0x001F, 0x0020, 0x0021, 0x0088, 0x0112, 0x0113, 0x0045,
+    0x0023, 0x0024, 0x0094, 0x0095, 0x004B, 0x004C, 0x009A, 0x0136,
+    0x04DC, 0x4DD0, 0x4DD1, 0x26E9, 0x1375, 0x09BB, 0x026F, 0x0027,
+    0x000A, 0x000B, 0x0018, 0x0064, 0x00CA, 0x0196, 0x0197, 0x0066,
+    0x0067, 0x001A, 0x001B, 0x000E, 0x0078, 0x0790, 0x0F22, 0x1E46,
+    0x1E47, 0x03C9, 0x01E5, 0x00F3, 0x007A, 0x00F6, 0x03DC, 0x07BA,
+    0x1EEC, 0x1EED, 0x0F77, 0x01EF, 0x001F, 0x0001
+];
+const VEC_CB1_LENS: [u8; 94] = [
+     5,  9, 11, 11, 10,  8,  7,  8,  8,  8,  9,  9,  4,  4,  6,  8,
+    11, 12, 12, 10,  9,  8,  9,  9, 11, 11, 10,  9,  9,  9, 11, 12,
+    12, 10,  9,  8,  9, 10, 11, 11,  8,  7,  7,  7,  9, 10, 10,  8,
+     7,  7,  9,  9,  8,  8,  9, 10, 12, 16, 16, 15, 14, 13, 11,  7,
+     5,  5,  6,  8,  9, 10, 10,  8,  8,  6,  6,  5,  8, 12, 13, 14,
+    14, 11, 10,  9,  8,  9, 11, 12, 14, 14, 13, 10,  6,  1
+];
+const VEC_CB1_SYMS: [u16; 94] = [
+     17,   23,   86,  131,   85,   80,   49,   21,    5,   36,   83,  112,
+      1,   16,   33,   51,   71,   87,   88,   70,   39,   81,   53,  113,
+    145,  117,   55,   68,   98,    7,   41,  118,   57,  100,   84,   66,
+     99,  115,  116,  132,   52,   64,   20,   65,   54,   25,  128,   37,
+      4,   35,    8,   69,   22,    6,   24,  144,  147,  120,  105,  150,
+    135,  148,  102,   50,    2,   32,    3,   82,  114,    9,   56,   97,
+     67,   19,   48,   18,   96,  103,   73,  104,  149,  146,  130,   40,
+     38,  129,   72,  133,  119,   89,  134,  101,   34,    0
+];
+
+const VEC_CB2_BITS: [u16; 48] = [
+    0x0000, 0x0008, 0x0024, 0x004A, 0x004B, 0x0013, 0x000A, 0x000B,
+    0x0018, 0x0019, 0x0034, 0x0035, 0x006C, 0x00DA, 0x01B6, 0x01B7,
+    0x0037, 0x001C, 0x001D, 0x001E, 0x003E, 0x003F, 0x0002, 0x0018,
+    0x0019, 0x000D, 0x0007, 0x0008, 0x0009, 0x000A, 0x0160, 0x0161,
+    0x0162, 0x0163, 0x0059, 0x00B4, 0x2D40, 0x5A82, 0x5A83, 0x16A1,
+    0x0B51, 0x05A9, 0x02D5, 0x016B, 0x005B, 0x0017, 0x0003, 0x0001
+];
+const VEC_CB2_LENS: [u8; 48] = [
+     4,  7,  9, 10, 10,  8,  7,  7,  8,  8,  9,  9, 10, 11, 12, 12,
+     9,  8,  8,  8,  9,  9,  4,  7,  7,  6,  5,  5,  5,  5, 10, 10,
+    10, 10,  8,  9, 15, 16, 16, 14, 13, 12, 11, 10,  8,  6,  3,  1
+];
+const VEC_CB2_SYMS: [u16; 48] = [
+     8,   25,   13,   42,   14,   12,    3,   24,   33,    4,   28,   41,
+    36,   22,   44,   30,   35,   32,   27,   20,   40,   21,    9,   19,
+    26,   18,    2,   10,   17,   16,   49,   29,   43,   50,   34,    6,
+    45,   53,   46,   52,   38,   51,   37,   48,    5,   11,    1,    0
+];
+
+const VEC_CB3_BITS: [u16; 520] = [
+    0x0000, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086,
+    0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E,
+    0x008F, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096,
+    0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E,
+    0x013E, 0x013F, 0x0005, 0x0003, 0x0002, 0x000C, 0x001A, 0x001B,
+    0x0038, 0x0039, 0x003A, 0x003B, 0x000F, 0x0008, 0x0090, 0x0091,
+    0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099,
+    0x009A, 0x009B, 0x0027, 0x0005, 0x000C, 0x000D, 0x000E, 0x001E,
+    0x001F, 0x0040, 0x0041, 0x0042, 0x0860, 0x0861, 0x0862, 0x0863,
+    0x0864, 0x0865, 0x0866, 0x0867, 0x0868, 0x0869, 0x086A, 0x086B,
+    0x086C, 0x086D, 0x086E, 0x086F, 0x0870, 0x0871, 0x0872, 0x0873,
+    0x0874, 0x0875, 0x0876, 0x0877, 0x0878, 0x0879, 0x087A, 0x087B,
+    0x087C, 0x087D, 0x087E, 0x087F, 0x0880, 0x0881, 0x0882, 0x0883,
+    0x0884, 0x0885, 0x0886, 0x0887, 0x0888, 0x0889, 0x088A, 0x088B,
+    0x088C, 0x088D, 0x088E, 0x088F, 0x0890, 0x0891, 0x0892, 0x0893,
+    0x0894, 0x0895, 0x0896, 0x0897, 0x0898, 0x0899, 0x089A, 0x089B,
+    0x089C, 0x089D, 0x089E, 0x089F, 0x08A0, 0x08A1, 0x08A2, 0x08A3,
+    0x08A4, 0x08A5, 0x08A6, 0x08A7, 0x08A8, 0x08A9, 0x08AA, 0x08AB,
+    0x08AC, 0x08AD, 0x08AE, 0x08AF, 0x08B0, 0x08B1, 0x08B2, 0x08B3,
+    0x08B4, 0x08B5, 0x08B6, 0x08B7, 0x08B8, 0x08B9, 0x08BA, 0x08BB,
+    0x08BC, 0x08BD, 0x08BE, 0x08BF, 0x08C0, 0x08C1, 0x08C2, 0x08C3,
+    0x08C4, 0x08C5, 0x08C6, 0x08C7, 0x08C8, 0x08C9, 0x08CA, 0x08CB,
+    0x08CC, 0x08CD, 0x08CE, 0x08CF, 0x08D0, 0x08D1, 0x08D2, 0x08D3,
+    0x08D4, 0x08D5, 0x08D6, 0x08D7, 0x08D8, 0x08D9, 0x08DA, 0x08DB,
+    0x08DC, 0x08DD, 0x08DE, 0x08DF, 0x08E0, 0x08E1, 0x08E2, 0x08E3,
+    0x08E4, 0x08E5, 0x08E6, 0x08E7, 0x08E8, 0x08E9, 0x08EA, 0x08EB,
+    0x08EC, 0x08ED, 0x08EE, 0x08EF, 0x08F0, 0x08F1, 0x08F2, 0x08F3,
+    0x08F4, 0x08F5, 0x08F6, 0x08F7, 0x08F8, 0x08F9, 0x08FA, 0x08FB,
+    0x08FC, 0x08FD, 0x08FE, 0x08FF, 0x0009, 0x0A00, 0x0A01, 0x0A02,
+    0x0A03, 0x0A04, 0x0A05, 0x0A06, 0x0A07, 0x0A08, 0x0A09, 0x0A0A,
+    0x0A0B, 0x0A0C, 0x0A0D, 0x0A0E, 0x0A0F, 0x0A10, 0x0A11, 0x0A12,
+    0x0A13, 0x0A14, 0x0A15, 0x0A16, 0x0A17, 0x0A18, 0x0A19, 0x0A1A,
+    0x0A1B, 0x0A1C, 0x0A1D, 0x0A1E, 0x0A1F, 0x0A20, 0x0A21, 0x0A22,
+    0x0A23, 0x0A24, 0x0A25, 0x0A26, 0x0A27, 0x0A28, 0x0A29, 0x0A2A,
+    0x0A2B, 0x0A2C, 0x0A2D, 0x0A2E, 0x0A2F, 0x00A3, 0x0029, 0x002A,
+    0x0056, 0x0057, 0x000B, 0x0003, 0x0004, 0x0005, 0x0030, 0x0062,
+    0x18C0, 0x18C1, 0x18C2, 0x18C3, 0x18C4, 0x18C5, 0x18C6, 0x18C7,
+    0x0319, 0x018D, 0x00C7, 0x0032, 0x0198, 0x0199, 0x019A, 0x019B,
+    0x0067, 0x001A, 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365,
+    0x0366, 0x0367, 0x0368, 0x0369, 0x036A, 0x036B, 0x00DB, 0x006E,
+    0x006F, 0x001C, 0x00E8, 0x00E9, 0x0075, 0x0076, 0x0077, 0x0780,
+    0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0786, 0x0787, 0x0788,
+    0x0789, 0x078A, 0x078B, 0x078C, 0x078D, 0x078E, 0x078F, 0x0079,
+    0x003D, 0x007C, 0x07D0, 0x07D1, 0x07D2, 0x07D3, 0x01F5, 0x00FB,
+    0x00FC, 0x00FD, 0x00FE, 0x01FE, 0x01FF, 0x0004, 0x0005, 0x000C,
+    0x0068, 0x00D2, 0x00D3, 0x0035, 0x0036, 0x0037, 0x0038, 0x00E4,
+    0x00E5, 0x0073, 0x003A, 0x0076, 0x01DC, 0x01DD, 0x00EF, 0x000F,
+    0x0008, 0x0009, 0x0050, 0x0144, 0x0145, 0x0146, 0x028E, 0x051E,
+    0x051F, 0x0052, 0x0530, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535,
+    0x0536, 0x0537, 0x014E, 0x0A78, 0x0A79, 0x0A7A, 0x0A7B, 0x0A7C,
+    0x0A7D, 0x0A7E, 0x0A7F, 0x0015, 0x0016, 0x0017, 0x0006, 0x0070,
+    0x00E2, 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x038D, 0x01C7, 0x0039,
+    0x01D0, 0x03A2, 0x0E8C, 0x0E8D, 0x0747, 0x00E9, 0x0075, 0x003B,
+    0x001E, 0x007C, 0x00FA, 0x03EC, 0x03ED, 0x03EE, 0x07DE, 0x07DF,
+    0x07E0, 0x07E1, 0x07E2, 0x07E3, 0x01F9, 0x00FD, 0x007F, 0x0008,
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0128, 0x0129, 0x012A, 0x0958,
+    0x9590, 0x9591, 0x4AC9, 0x2565, 0x12B3, 0x04AD, 0x0257, 0x004B,
+    0x0098, 0x0264, 0x0265, 0x0266, 0x04CE, 0x04CF, 0x004D, 0x009C,
+    0x013A, 0x0276, 0x0277, 0x004F, 0x00A0, 0x0284, 0x050A, 0x050B,
+    0x050C, 0x0A1A, 0x0A1B, 0x0A1C, 0x0A1D, 0x0A1E, 0x0A1F, 0x00A2,
+    0x0A30, 0x0A31, 0x0A32, 0x0A33, 0x028D, 0x028E, 0x1478, 0x1479,
+    0x147A, 0x147B, 0x147C, 0x147D, 0x147E, 0x147F, 0x0029, 0x0015,
+    0x0058, 0x0059, 0x00B4, 0x16A0, 0x16A1, 0x16A2, 0x16A3, 0x16A4,
+    0x16A5, 0x16A6, 0x2D4E, 0x2D4F, 0x02D5, 0x016B, 0x02D8, 0x02D9,
+    0x016D, 0x016E, 0x05BC, 0x05BD, 0x02DF, 0x002E, 0x002F, 0x0003
+];
+const VEC_CB3_LENS: [u8; 520] = [
+     8, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+    16, 16, 10,  9,  8, 10, 11, 11, 12, 12, 12, 12, 10,  9, 13, 13,
+    13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 11,  8,  9,  9,  9, 10,
+    10, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16,  8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 12, 10, 10, 11, 11,  8,  6,  6,  6,  9, 10,
+    16, 16, 16, 16, 16, 16, 16, 16, 13, 12, 11,  9, 12, 12, 12, 12,
+    10,  8, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 11, 10,
+    10,  8, 11, 11, 10, 10, 10, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+    14, 14, 14, 14, 14, 14, 14, 10,  9, 10, 14, 14, 14, 14, 12, 11,
+    11, 11, 11, 12, 12,  5,  5,  6,  9, 10, 10,  8,  8,  8,  8, 10,
+    10,  9,  8,  9, 11, 11, 10,  6,  5,  5,  8, 10, 10, 10, 11, 12,
+    12,  8, 12, 12, 12, 12, 12, 12, 12, 12, 10, 13, 13, 13, 13, 13,
+    13, 13, 13,  6,  6,  6,  4,  8,  9, 13, 13, 13, 13, 11, 10,  7,
+    10, 11, 13, 13, 12,  9,  8,  7,  6,  8,  9, 11, 11, 11, 12, 12,
+    12, 12, 12, 12, 10,  9,  8,  4,  8,  8,  8,  8,  9,  9,  9, 12,
+    16, 16, 15, 14, 13, 11, 10,  7,  8, 10, 10, 10, 11, 11,  7,  8,
+     9, 10, 10,  7,  8, 10, 11, 11, 11, 12, 12, 12, 12, 12, 12,  8,
+    12, 12, 12, 12, 10, 10, 13, 13, 13, 13, 13, 13, 13, 13,  6,  5,
+     7,  7,  8, 13, 13, 13, 13, 13, 13, 13, 14, 14, 10,  9, 10, 10,
+     9,  9, 11, 11, 10,  6,  6,  2
+];
+const VEC_CB3_SYMS: [u16; 520] = [
+      74, 1120, 1682, 1745, 1801,  274, 1050,  730,  546, 1058, 1729, 1539,
+    2240, 1547, 1163, 1730, 2064, 1624,  723, 1235, 1115,   35, 2248, 1304,
+     644,  196, 1625, 1036, 1548, 1185,  652, 1044, 2136, 1753, 1664,  129,
+     640,   83,  665,  130, 1225, 1232,  602,   76,  587, 1161, 1049,  545,
+     265, 1218, 2113,  202,   98,  131,  643, 1288,  524,  785,  720, 1096,
+     704,  528,  657,  601,   67,  537,   33,  579, 1746, 2258, 1248,  786,
+    1298, 1810, 2265, 2313, 1761, 1562, 2074, 2264, 1569, 2257,  225, 2138,
+    1056, 2081, 2249, 1690, 2202,  736, 2186, 1242, 1754,  282,  794, 1306,
+    1792, 1561, 2201, 1570, 2073,  281, 1122, 1634, 2178,  674, 1186, 1698,
+     226,  738, 1250, 1240,  289,  800, 1738, 2051, 1632, 1121,  793, 1603,
+    2115, 1633, 2250,  737, 1667, 2179,  195,  707, 1219, 1731, 2243,  259,
+     771, 1283, 1795, 2304, 2144, 2305, 1249, 2059, 2192, 2072, 2200, 1611,
+    2123, 1689,  288, 1696, 1675, 2187,  203,  792, 1227, 1739, 2251,  267,
+     779, 1291, 1803, 1802, 2256, 2242, 1555, 2067, 1560,  258, 1688, 1619,
+    2131,  770, 2145, 2114, 1683, 2195,  211, 1057, 2065, 1747,  275,  787,
+    1299, 1817, 1697, 1051, 1563, 2075, 1794, 2050,  801, 1627, 2139, 1800,
+     667, 1179, 1691,  219,  731, 1243, 1755,  283,  795, 2066,  547, 1059,
+    1571, 1184,  611, 1123, 1635,  163,  675, 1187,  227,  739, 1568, 1760,
+    1028, 1540, 2052, 2193, 1752, 1092, 1604, 2116, 1793, 2177, 1156, 1668,
+     649, 1618,  708, 1220, 1732,  260,  772, 1808, 1816,  280, 2312, 2060,
+    1305, 1241, 1297, 1612, 2124,  140, 2122, 1164, 1676,  204,  716, 1228,
+    1740,  268,  780, 2241, 1809, 2194, 1556, 2137, 2058, 1108, 1620,  148,
+     660, 1172, 1684,  212,  724, 1236,   28,  540, 1052, 1564,   92,  604,
+    1116,  666, 1105, 1153, 1673, 1162,  522,  584,  513,   73,  712,  650,
+    1628,  156,  668, 1180,  220,  732,   36,  548,  672,  777,   32,  656,
+    1233, 1113, 1602, 1665,  523,  586,  609, 1538,  728, 1112, 1289,  257,
+    1280, 1234,  216,  610, 1091, 2121, 1026, 1224, 1608, 1600,   96,  536,
+     713, 1104,   26, 1296, 1282, 2130, 1626,  218,  162, 1744, 1155, 2129,
+     715, 1043, 1281,  147, 1171,  224,   99, 1169,  144, 1672,  132, 1100,
+    2185,  596,  264,  768,  664, 1168, 1106,  154,  651,   64,    8,  585,
+     594,  201,   90, 1097,  593,   11, 1089, 2048, 1098,   89,    3,   75,
+     208,   91, 1090,   65,  576,    9,  137,  642,  658,  515,   20, 1736,
+    1728, 1025,  194,  784,  722,  538,   34, 1099,  769, 2176, 1544,  608,
+    1666, 1546,  217,  160, 2184,  544, 1027,  577,  521,  520,    1, 1032,
+     145, 2049, 1680,  659,  273,   97, 2112,   16,  138, 1217,  580,  588,
+     729,    4,  514,   81,    2,  592,  192, 1042,  531,  595, 2120, 1610,
+     714, 1170,  139, 1737, 1034,   88,  578,  512,   18, 1536, 1033,   66,
+      24,  200,  530,   84,  100,  612, 2080,  266, 1177,  209, 1601,   80,
+    1160,  146,  600,  152, 1035,  776,   17,   82,   25, 1609,  153,  648,
+     529, 1216, 1545,   27,   68,  272, 1552,  161, 1176, 1048, 1617,  641,
+     210, 1114, 1553, 1616, 1040,  705, 2057,  706, 2128, 1674, 1226, 1554,
+    1178, 1107,   10,   72,  136,  128, 1152, 1681,  539,  603,  155,  516,
+     673,  532,  778, 1290, 1537,   12,  721,  256, 1041,   19, 1154, 2056,
+     193, 1024, 1088,    0
+];
+
+const VEC_CB4_BITS: [u16; 209] = [
+    0x0000, 0x0004, 0x000A, 0x0016, 0x002E, 0x0178, 0x0179, 0x00BD,
+    0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x05F5, 0x05F6, 0x05F7,
+    0x05F8, 0x05F9, 0x05FA, 0x05FB, 0x05FC, 0x05FD, 0x05FE, 0x05FF,
+    0x0018, 0x0019, 0x000D, 0x001C, 0x001D, 0x000F, 0x0001, 0x0002,
+    0x0018, 0x0064, 0x0328, 0x0CA4, 0x0CA5, 0x0CA6, 0x0CA7, 0x0CA8,
+    0x0CA9, 0x0655, 0x032B, 0x00CB, 0x0066, 0x0067, 0x000D, 0x000E,
+    0x000F, 0x0001, 0x0010, 0x0440, 0x1104, 0x220A, 0x220B, 0x0883,
+    0x0221, 0x0111, 0x0089, 0x0045, 0x0118, 0x0119, 0x0468, 0x08D2,
+    0x08D3, 0x08D4, 0x08D5, 0x046B, 0x011B, 0x008E, 0x023C, 0x047A,
+    0x08F6, 0x23DC, 0x23DD, 0x23DE, 0x23DF, 0x011F, 0x0009, 0x000A,
+    0x0058, 0x02C8, 0x0592, 0x164C, 0x164D, 0x164E, 0x164F, 0x0B28,
+    0x0B29, 0x0595, 0x0596, 0x0597, 0x02CC, 0x059A, 0x0B36, 0x0B37,
+    0x0B38, 0x2CE4, 0x59CA, 0x59CB, 0x1673, 0x059D, 0x02CF, 0x00B4,
+    0x00B5, 0x005B, 0x0017, 0x0018, 0x0019, 0x001A, 0x0036, 0x01B8,
+    0x06E4, 0x1B94, 0x1B95, 0x0DCB, 0x0373, 0x06E8, 0x06E9, 0x0375,
+    0x01BB, 0x006F, 0x001C, 0x0074, 0x00EA, 0x01D6, 0x0EB8, 0x1D72,
+    0x75CC, 0x75CD, 0x75CE, 0x75CF, 0x75D0, 0x75D1, 0x75D2, 0x75D3,
+    0x75D4, 0x75D5, 0x75D6, 0x75D7, 0x75D8, 0x75D9, 0x75DA, 0x75DB,
+    0x75DC, 0x75DD, 0x75DE, 0x75DF, 0x75E0, 0x75E1, 0x75E2, 0x75E3,
+    0x75E4, 0x75E5, 0x75E6, 0x75E7, 0x75E8, 0x75E9, 0x75EA, 0x75EB,
+    0x75EC, 0x75ED, 0x75EE, 0x75EF, 0x75F0, 0x75F1, 0x75F2, 0x75F3,
+    0x75F4, 0x75F5, 0x75F6, 0x75F7, 0x75F8, 0x75F9, 0x75FA, 0x75FB,
+    0x75FC, 0x75FD, 0x75FE, 0x75FF, 0x00EC, 0x01DA, 0x76C0, 0x76C1,
+    0x76C2, 0x76C3, 0x76C4, 0x76C5, 0x76C6, 0x76C7, 0x76C8, 0x76C9,
+    0x76CA, 0x76CB, 0x76CC, 0x76CD, 0x76CE, 0x76CF, 0x76D0, 0x76D1,
+    0x76D2, 0x76D3, 0x76D4, 0x76D5, 0x3B6B, 0x0EDB, 0x03B7, 0x0077, 0x000F
+];
+const VEC_CB4_LENS: [u8; 209] = [
+     5,  7,  8,  9, 10, 13, 13, 12, 15, 15, 15, 15, 15, 15, 15, 15,
+    15, 15, 15, 15, 15, 15, 15, 15,  9,  9,  8,  9,  9,  8,  4,  4,
+     7,  9, 12, 14, 14, 14, 14, 14, 14, 13, 12, 10,  9,  9,  6,  6,
+     6,  2,  5, 11, 13, 14, 14, 12, 10,  9,  8,  7,  9,  9, 11, 12,
+    12, 12, 12, 11,  9,  8, 10, 11, 12, 14, 14, 14, 14,  9,  4,  4,
+     7, 10, 11, 13, 13, 13, 13, 12, 12, 11, 11, 11, 10, 11, 12, 12,
+    12, 14, 15, 15, 13, 11, 10,  8,  8,  7,  5,  5,  5,  5,  6,  9,
+    11, 13, 13, 12, 10, 11, 11, 10,  9,  7,  5,  7,  8,  9, 12, 13,
+    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+    15, 15, 15, 15,  8,  9, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 12, 10,  7,  4
+];
+const VEC_CB4_SYMS: [u16; 209] = [
+     80,  128,   96,  132,  160,  134,   92,   23,  180,  228,  232,  214,
+    225,  166,   54,  138,  121,  131,  147,   99,  141,   75,   27,   91,
+     37,   66,   24,  129,  145,   36,    1,   64,    6,   73,   34,   57,
+     49,   42,  106,   14,   56,  130,   98,    7,   33,   97,   69,   81,
+     85,    0,   20,   12,   76,   11,   44,  212,   40,   70,   22,  144,
+     86,  149,  165,  196,  102,  137,  146,   52,   72,  148,  192,   28,
+     19,  120,   30,  224,  169,   82,    4,   16,    8,  208,   48,   93,
+    168,  193,   53,  152,   67,  161,  112,   29,   41,   74,   77,  150,
+     71,   87,  114,  178,  213,  116,  164,   18,  101,   32,   65,   21,
+     84,   17,    2,    3,  153,  113,   83,  117,  105,   38,   90,   13,
+     10,  100,   68,    9,   89,  133,  197,  209,  181,   61,  245,  198,
+    205,  125,  156,  220,  233,  172,  216,  230,  109,  118,  182,  217,
+    176,  173,  202,  204,  108,  154,  218,  184,  185,  170,   58,  122,
+    124,   78,  142,  201,   94,  158,   46,  110,  157,  200,  240,  195,
+    244,   60,  210,  211,   35,  241,  163,   51,  115,  221,  229,  135,
+     88,   26,  199,  177,   45,  151,  215,   39,  103,  167,   55,  119,
+    140,  162,  139,  226,   50,  155,   43,  107,   15,   79,   31,   95,
+    194,  136,  104,   25,    5
+];
+
+const VEC_CB5_BITS: [u16; 192] = [
+    0x0000, 0x0004, 0x0005, 0x0060, 0x0184, 0x030A, 0x0616, 0x0617,
+    0x030C, 0x061A, 0x061B, 0x0187, 0x0031, 0x0019, 0x001A, 0x001B,
+    0x000E, 0x003C, 0x003D, 0x003E, 0x007E, 0x01FC, 0x03FA, 0x1FD8,
+    0x1FD9, 0x0FED, 0x0FEE, 0x0FEF, 0x00FF, 0x0010, 0x0044, 0x008A,
+    0x008B, 0x0023, 0x0009, 0x000A, 0x000B, 0x0060, 0x1840, 0x1841,
+    0x1842, 0x1843, 0x0C22, 0x0C23, 0x0612, 0x184C, 0x309A, 0x309B,
+    0x0C27, 0x030A, 0x0616, 0x0617, 0x0186, 0x1870, 0x1871, 0x0C39,
+    0x0C3A, 0x0C3B, 0x061E, 0x0C3E, 0x0C3F, 0x0031, 0x0019, 0x001A,
+    0x0036, 0x00DC, 0x0DD0, 0x0DD1, 0x06E9, 0x0375, 0x01BB, 0x01BC,
+    0x0DE8, 0x37A4, 0x6F4A, 0x6F4B, 0x1BD3, 0x06F5, 0x06F6, 0x1BDC,
+    0x1BDD, 0x0DEF, 0x0DF0, 0x0DF1, 0x6F90, 0x6F91, 0x6F92, 0x6F93,
+    0x6F94, 0x6F95, 0x6F96, 0x6F97, 0x6F98, 0x6F99, 0x6F9A, 0x6F9B,
+    0x6F9C, 0x6F9D, 0x6F9E, 0x6F9F, 0x6FA0, 0x6FA1, 0x6FA2, 0x6FA3,
+    0x6FA4, 0x6FA5, 0x6FA6, 0x6FA7, 0x6FA8, 0x6FA9, 0x6FAA, 0x6FAB,
+    0x6FAC, 0x6FAD, 0x6FAE, 0x6FAF, 0x6FB0, 0x6FB1, 0x6FB2, 0x6FB3,
+    0x6FB4, 0x6FB5, 0x6FB6, 0x6FB7, 0x6FB8, 0x6FB9, 0x6FBA, 0x6FBB,
+    0x6FBC, 0x6FBD, 0x6FBE, 0x6FBF, 0x6FC0, 0x6FC1, 0x6FC2, 0x6FC3,
+    0x6FC4, 0x6FC5, 0x6FC6, 0x6FC7, 0x6FC8, 0x6FC9, 0x6FCA, 0x6FCB,
+    0x6FCC, 0x6FCD, 0x6FCE, 0x6FCF, 0x6FD0, 0x6FD1, 0x6FD2, 0x6FD3,
+    0x6FD4, 0x6FD5, 0x6FD6, 0x6FD7, 0x6FD8, 0x6FD9, 0x6FDA, 0x6FDB,
+    0x6FDC, 0x6FDD, 0x37EF, 0x0DFC, 0x0DFD, 0x0DFE, 0x37FC, 0x37FD,
+    0x37FE, 0x37FF, 0x001C, 0x003A, 0x0076, 0x0077, 0x00F0, 0x00F1,
+    0x03C8, 0x0792, 0x1E4C, 0x1E4D, 0x1E4E, 0x1E4F, 0x1E50, 0x1E51,
+    0x1E52, 0x1E53, 0x0F2A, 0x0F2B, 0x03CB, 0x00F3, 0x003D, 0x001F
+];
+const VEC_CB5_LENS: [u8; 192] = [
+     2,  4,  4,  8, 10, 11, 12, 12, 11, 12, 12, 10,  7,  6,  6,  6,
+     5,  7,  7,  7,  8, 10, 11, 14, 14, 13, 13, 13,  9,  5,  7,  8,
+     8,  6,  4,  4,  4,  7, 13, 13, 13, 13, 12, 12, 11, 13, 14, 14,
+    12, 10, 11, 11,  9, 13, 13, 12, 12, 12, 11, 12, 12,  6,  5,  5,
+     6,  8, 12, 12, 11, 10,  9,  9, 12, 14, 15, 15, 13, 11, 11, 13,
+    13, 12, 12, 12, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+    15, 15, 14, 12, 12, 12, 14, 14, 14, 14,  5,  6,  7,  7,  8,  8,
+    10, 11, 13, 13, 13, 13, 13, 13, 13, 13, 12, 12, 10,  8,  6,  5
+];
+const VEC_CB5_SYMS: [u16; 192] = [
+      0,    1,  256,  277,   24,   25,  262,   88,  100,  132,   70,  384,
+     81,   17,  272,   68,    5,  321,   69,  276,  337,  144,   72,   10,
+    389,  289,  532,  517,   32,  320,  324,  325,    2,  260,    4,   64,
+     16,  273,  352,  265,  264,  329,   97,  148,   66,   86,  533,  149,
+     37,   36,  400,   22,  128,  280,  326,  516,  101,  292,  528,  577,
+    592,   21,   20,   80,  336,  512,  133,  580,   33,   96,  576,    9,
+    274,  593,  597,  581,  356,  513,  288,  338,  278,  328,  388,   73,
+    405,  661,  344,  660,  549,  152,  417,  613,  165,  421,  392,  296,
+    521,  645,  641,  585,  137,  393,  608,  416,  537,  656,  609,  601,
+    153,  409,  420,  297,  105,  361,  360,  408,  514,  164,   40,  578,
+    130,  386,  548,  544,  530,  584,  545,  594,  146,  402,  600,  290,
+     98,  354,  644,  136,  518,  657,  520,  582,  134,  390,  104,  536,
+    534,  161,  342,  598,  150,  406,   38,  294,  102,  358,  612,  266,
+     74,  330,   26,  282,   90,  346,   41,  293,  322,   82,   34,  640,
+    401,  160,  257,   84,  340,   85,  341,    6,  258,  129,  353,  529,
+    404,  357,  281,   89,  345,  596,  385,  145,   18,    8,  261,   65
+];
+
+const VEC_CB6_BITS: [u16; 32] = [
+    0x0000, 0x0004, 0x0005, 0x0003, 0x0001, 0x0002, 0x0003, 0x0001,
+    0x0002, 0x0003, 0x0004, 0x0005, 0x0018, 0x00C8, 0x0192, 0x0326,
+    0x0327, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x01A8, 0x01A9,
+    0x00D5, 0x006B, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0001
+];
+const VEC_CB6_LENS: [u8; 32] = [
+     7,  9,  9,  8,  6,  6,  6,  4,  4,  4,  4,  4,  6,  9, 10, 11,
+    11,  8,  8,  8,  8,  8, 10, 10,  9,  8,  6,  6,  6,  6,  6,  1
+];
+const VEC_CB6_SYMS: [u16; 32] = [
+     9,   19,   22,    7,    3,   24,    6,    1,   16,    2,    8,    4,
+    12,   21,   27,   29,   31,   28,   14,   13,   11,   26,   30,   23,
+    15,   25,    5,   10,   20,   17,   18,    0
+];
+
+const QUANT_LEVELS: [[f32; 14]; 7] = [
+    [ 0.0, 0.392, 0.761, 1.120, 1.477, 1.832, 2.183, 2.541, 2.893, 3.245, 3.598, 3.942, 4.288, 4.724 ],
+    [ 0.0, 0.544, 1.060, 1.563, 2.068, 2.571, 3.072, 3.562, 4.070, 4.620, 0.0,   0.0,   0.0,   0.0 ],
+    [ 0.0, 0.746, 1.464, 2.180, 2.882, 3.584, 4.316, 0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0 ],
+    [ 0.0, 1.006, 2.000, 2.993, 3.985, 0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0 ],
+    [ 0.0, 1.321, 2.703, 3.983, 0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0 ],
+    [ 0.0, 1.657, 3.491, 0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0 ],
+    [ 0.0, 1.964, 0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0,   0.0 ]
+];
+
+const CAT5_NOISE_FACTOR: [f32; REGION_SIZE + 1] = [
+    0.70711, 0.6179,  0.5005,  0.3220,  0.17678, 0.17678, 0.17678, 0.17678, 0.17678, 0.17678,
+    0.17678, 0.17678, 0.17678, 0.17678, 0.17678, 0.17678, 0.17678, 0.17678, 0.17678, 0.17678, 0.0
+];
+const CAT6_NOISE_FACTOR: [f32; REGION_SIZE + 1] = [
+    0.70711, 0.5686, 0.3563, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25,
+    0.25,    0.25,   0.25,   0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.0
+];
diff --git a/nihav-vivo/src/codecs/vivo.rs b/nihav-vivo/src/codecs/vivo.rs
new file mode 100644 (file)
index 0000000..807814d
--- /dev/null
@@ -0,0 +1,513 @@
+use nihav_core::io::bitreader::*;
+use nihav_core::io::codebook::*;
+use nihav_core::formats;
+use nihav_core::frame::*;
+use nihav_core::codecs::*;
+use nihav_codec_support::codecs::{MV, ZIGZAG};
+use nihav_codec_support::codecs::h263::*;
+use nihav_codec_support::codecs::h263::code::H263BlockDSP;
+use nihav_codec_support::codecs::h263::decoder::*;
+use nihav_codec_support::codecs::h263::data::*;
+
+#[allow(dead_code)]
+struct Tables {
+    intra_mcbpc_cb: Codebook<u8>,
+    inter_mcbpc_cb: Codebook<u8>,
+    cbpy_cb:        Codebook<u8>,
+    rl_cb:          Codebook<H263RLSym>,
+    aic_rl_cb:      Codebook<H263RLSym>,
+    mv_cb:          Codebook<u8>,
+}
+
+struct VivoDecoder {
+    info:       NACodecInfoRef,
+    dec:        H263BaseDecoder,
+    tables:     Tables,
+    bdsp:       H263BlockDSP,
+    lastframe:  Option<NABufferType>,
+    lastpts:    Option<u64>,
+    width:      usize,
+    height:     usize,
+}
+
+struct VivoBR<'a> {
+    br:     BitReader<'a>,
+    tables: &'a Tables,
+    gob_no: usize,
+    mb_w:   usize,
+    is_pb:  bool,
+    is_ipb: bool,
+    ref_w:  usize,
+    ref_h:  usize,
+    aic:    bool,
+}
+
+fn check_marker<'a>(br: &mut BitReader<'a>) -> DecoderResult<()> {
+    let mark = br.read(1)?;
+    validate!(mark == 1);
+    Ok(())
+}
+
+impl<'a> VivoBR<'a> {
+    fn new(src: &'a [u8], tables: &'a Tables, ref_w: usize, ref_h: usize) -> Self {
+        VivoBR {
+            br:     BitReader::new(src, BitReaderMode::BE),
+            tables,
+            gob_no: 0,
+            mb_w:   0,
+            is_pb:  false,
+            is_ipb: false,
+            ref_w, ref_h,
+            aic:    false,
+        }
+    }
+
+    fn decode_block(&mut self, quant: u8, intra: bool, coded: bool, blk: &mut [i16; 64], plane_no: usize, acpred: ACPredMode) -> DecoderResult<()> {
+        let br = &mut self.br;
+        let mut idx = 0;
+        if !self.aic && intra {
+            let mut dc = br.read(8)? as i16;
+            if dc == 255 { dc = 128; }
+            blk[0] = dc << 3;
+            idx = 1;
+        }
+        if !coded { return Ok(()); }
+        let scan = match acpred {
+                    ACPredMode::Hor => H263_SCAN_V,
+                    ACPredMode::Ver => H263_SCAN_H,
+                    _               => &ZIGZAG,
+                };
+
+        let rl_cb = if self.aic && intra { &self.tables.aic_rl_cb } else { &self.tables.rl_cb };
+        let q = if plane_no == 0 { (quant * 2) as i16 } else { (H263_CHROMA_QUANT[quant as usize] * 2) as i16 };
+        let q_add = if q == 0 || self.aic { 0i16 } else { (((q >> 1) - 1) | 1) as i16 };
+        while idx < 64 {
+            let code = br.read_cb(rl_cb)?;
+            let run;
+            let mut level;
+            let last;
+            if !code.is_escape() {
+                run   = code.get_run();
+                level = code.get_level();
+                last  = code.is_last();
+                if br.read_bool()? { level = -level; }
+                if level >= 0 {
+                    level = (level * q) + q_add;
+                } else {
+                    level = (level * q) - q_add;
+                }
+            } else {
+                last  = br.read_bool()?;
+                run   = br.read(6)? as u8;
+                level = br.read_s(8)? as i16;
+                if level == -128 {
+                    let low = br.read(5)? as i16;
+                    let top = br.read_s(6)? as i16;
+                    level = (top << 5) | low;
+                }
+                if level >= 0 {
+                    level = (level * q) + q_add;
+                } else {
+                    level = (level * q) - q_add;
+                }
+                if level < -2048 { level = -2048; }
+                if level >  2047 { level =  2047; }
+            }
+            idx += run;
+            validate!(idx < 64);
+            let oidx = scan[idx as usize];
+            blk[oidx] = level;
+            idx += 1;
+            if last { break; }
+        }
+        Ok(())
+    }
+}
+
+fn decode_mv_component(br: &mut BitReader, mv_cb: &Codebook<u8>) -> DecoderResult<i16> {
+    let code = i16::from(br.read_cb(mv_cb)?);
+    if code == 0 { return Ok(0) }
+    if !br.read_bool()? {
+        Ok(code)
+    } else {
+        Ok(-code)
+    }
+}
+
+fn decode_mv(br: &mut BitReader, mv_cb: &Codebook<u8>) -> DecoderResult<MV> {
+    let xval = decode_mv_component(br, mv_cb)?;
+    let yval = decode_mv_component(br, mv_cb)?;
+    Ok(MV::new(xval, yval))
+}
+
+fn decode_b_info(br: &mut BitReader, is_pb: bool, is_ipb: bool, is_intra: bool) -> DecoderResult<BBlockInfo> {
+    if is_pb { // as improved pb
+        if is_ipb {
+            let pb_mv_add = if is_intra { 1 } else { 0 };
+            if br.read_bool()?{
+                if br.read_bool()? {
+                    let pb_mv_count = 1 - (br.read(1)? as usize);
+                    let cbpb = br.read(6)? as u8;
+                    Ok(BBlockInfo::new(true, cbpb, pb_mv_count + pb_mv_add, pb_mv_count == 1))
+                } else {
+                    Ok(BBlockInfo::new(true, 0, 1 + pb_mv_add, true))
+                }
+            } else {
+                Ok(BBlockInfo::new(true, 0, pb_mv_add, false))
+            }
+        } else {
+            let mvdb = br.read_bool()?;
+            let cbpb = if mvdb && br.read_bool()? { br.read(6)? as u8 } else { 0 };
+            Ok(BBlockInfo::new(true, cbpb, if mvdb { 1 } else { 0 }, false))
+        }
+    } else {
+        Ok(BBlockInfo::new(false, 0, 0, false))
+    }
+}
+
+impl<'a> BlockDecoder for VivoBR<'a> {
+
+#[allow(unused_variables)]
+    fn decode_pichdr(&mut self) -> DecoderResult<PicInfo> {
+        let br = &mut self.br;
+        let syncw = br.read(22)?;
+        validate!(syncw == 0x000020);
+        let tr = (br.read(8)? << 8) as u16;
+        check_marker(br)?;
+        let id = br.read(1)?;
+        validate!(id == 0);
+        br.read(1)?; // split screen indicator
+        br.read(1)?; // document camera indicator
+        br.read(1)?; // freeze picture release
+        let mut sfmt = br.read(3)?;
+        validate!(sfmt != 0b000);
+        let is_intra = !br.read_bool()?;
+        let umv = br.read_bool()?;
+        br.read(1)?; // syntax arithmetic coding
+        let apm = br.read_bool()?;
+        self.is_pb = br.read_bool()?;
+        let deblock;
+        let pbplus;
+        let aic;
+        if sfmt == 0b110 {
+            sfmt = br.read(3)?;
+            validate!(sfmt != 0b000 && sfmt != 0b110);
+            aic = br.read_bool()?;
+            br.read(1)?; // umv mode
+            deblock = br.read_bool()?;
+            br.read(3)?; // unknown flags
+            pbplus = br.read_bool()?;
+            br.read(4)?; // unknown flags
+        } else {
+            aic = false;
+            deblock = false;
+            pbplus = false;
+        }
+        self.is_ipb = pbplus;
+        let (w, h) = match sfmt {
+                0b001 => ( 64,  48),
+                0b011 => ( 88,  72),
+                0b010 => (176, 144),
+                0b100 => (352, 288),
+                0b101 => (704, 576),
+                0b111 => {
+                    validate!((self.ref_w != 0) && (self.ref_h != 0));
+                    ((self.ref_w + 15) & !15, (self.ref_h + 15) & !15)
+                },
+                _ => return Err(DecoderError::InvalidData),
+            };
+        let quant = br.read(5)?;
+        let cpm = br.read_bool()?;
+        validate!(!cpm);
+
+        let pbinfo;
+        if self.is_pb {
+            let trb = br.read(3)?;
+            let dbquant = br.read(2)?;
+            pbinfo = Some(PBInfo::new(trb as u8, dbquant as u8, pbplus));
+        } else {
+            pbinfo = None;
+        }
+        while br.read_bool()? { // skip PEI
+            br.read(8)?;
+        }
+        self.gob_no = 0;
+        self.mb_w = (w + 15) >> 4;
+        self.aic = aic;
+
+        let ftype = if is_intra { Type::I } else { Type::P };
+        let plusinfo = Some(PlusInfo::new(aic, deblock, false, false));
+        let mvmode = if umv { MVMode::UMV } else { MVMode::Old };
+        let picinfo = PicInfo::new(w, h, ftype, mvmode, umv, apm, quant as u8, tr, pbinfo, plusinfo);
+        Ok(picinfo)
+    }
+
+    #[allow(unused_variables)]
+    fn decode_slice_header(&mut self, info: &PicInfo) -> DecoderResult<SliceInfo> {
+        let br = &mut self.br;
+        let gbsc = br.read(17)?;
+        validate!(gbsc == 1);
+        let gn = br.read(5)?;
+        let gfid = br.read(2)?;
+        let gquant = br.read(5)?;
+        let ret = SliceInfo::new_gob(0, self.gob_no, gquant as u8);
+        self.gob_no += 1;
+        Ok(ret)
+    }
+
+    #[allow(unused_variables)]
+    fn decode_block_header(&mut self, info: &PicInfo, slice: &SliceInfo, sstate: &SliceState) -> DecoderResult<BlockInfo> {
+        let br = &mut self.br;
+        let mut q = slice.get_quant();
+        match info.get_mode() {
+            Type::I => {
+                    let mut cbpc = br.read_cb(&self.tables.intra_mcbpc_cb)?;
+                    while cbpc == 8 { cbpc = br.read_cb(&self.tables.intra_mcbpc_cb)?; }
+                    let mut acpred = ACPredMode::None;
+                    if let Some(ref pi) = info.plusinfo {
+                        if pi.aic {
+                            let acpp = br.read_bool()?;
+                            acpred = ACPredMode::DC;
+                            if acpp {
+                                acpred = if !br.read_bool()? { ACPredMode::Hor } else { ACPredMode::Ver };
+                            }
+                        }
+                    }
+                    let cbpy = br.read_cb(&self.tables.cbpy_cb)?;
+                    let cbp = (cbpy << 2) | (cbpc & 3);
+                    let dquant = (cbpc & 4) != 0;
+                    if dquant {
+                        let idx = br.read(2)? as usize;
+                        q = (i16::from(q) + i16::from(H263_DQUANT_TAB[idx])) as u8;
+                    }
+                    let mut binfo = BlockInfo::new(Type::I, cbp, q);
+                    binfo.set_acpred(acpred);
+                    Ok(binfo)
+                },
+            Type::P => {
+                    if br.read_bool()? { return Ok(BlockInfo::new(Type::Skip, 0, info.get_quant())); }
+                    let mut cbpc = br.read_cb(&self.tables.inter_mcbpc_cb)?;
+                    while cbpc == 20 { cbpc = br.read_cb(&self.tables.inter_mcbpc_cb)?; }
+                    let is_intra = (cbpc & 0x04) != 0;
+                    let dquant   = (cbpc & 0x08) != 0;
+                    let is_4x4   = (cbpc & 0x10) != 0;
+                    if is_intra {
+                        let mut acpred = ACPredMode::None;
+                        if let Some(ref pi) = info.plusinfo {
+                            if pi.aic {
+                                let acpp = br.read_bool()?;
+                                acpred = ACPredMode::DC;
+                                if acpp {
+                                    acpred = if !br.read_bool()? { ACPredMode::Hor } else { ACPredMode::Ver };
+                                }
+                            }
+                        }
+                        let mut mvec: Vec<MV> = Vec::new();
+                        let bbinfo = decode_b_info(br, self.is_pb, self.is_ipb, true)?;
+                        let cbpy = br.read_cb(&self.tables.cbpy_cb)?;
+                        let cbp = (cbpy << 2) | (cbpc & 3);
+                        if dquant {
+                            let idx = br.read(2)? as usize;
+                            q = (i16::from(q) + i16::from(H263_DQUANT_TAB[idx])) as u8;
+                        }
+                        let mut binfo = BlockInfo::new(Type::I, cbp, q);
+                        binfo.set_bpart(bbinfo);
+                        binfo.set_acpred(acpred);
+                        if self.is_pb {
+                            for _ in 0..bbinfo.get_num_mv() {
+                                mvec.push(decode_mv(br, &self.tables.mv_cb)?);
+                            }
+                            binfo.set_b_mv(mvec.as_slice());
+                        }
+                        return Ok(binfo);
+                    }
+
+                    let bbinfo = decode_b_info(br, self.is_pb, self.is_ipb, false)?;
+                    let mut cbpy = br.read_cb(&self.tables.cbpy_cb)?;
+//                    if /* !aiv && */(cbpc & 3) != 3 {
+                        cbpy ^= 0xF;
+//                    }
+                    let cbp = (cbpy << 2) | (cbpc & 3);
+                    if dquant {
+                        let idx = br.read(2)? as usize;
+                        q = (i16::from(q) + i16::from(H263_DQUANT_TAB[idx])) as u8;
+                    }
+                    let mut binfo = BlockInfo::new(Type::P, cbp, q);
+                    binfo.set_bpart(bbinfo);
+                    if !is_4x4 {
+                        let mvec: [MV; 1] = [decode_mv(br, &self.tables.mv_cb)?];
+                        binfo.set_mv(&mvec);
+                    } else {
+                        let mvec: [MV; 4] = [
+                                decode_mv(br, &self.tables.mv_cb)?,
+                                decode_mv(br, &self.tables.mv_cb)?,
+                                decode_mv(br, &self.tables.mv_cb)?,
+                                decode_mv(br, &self.tables.mv_cb)?
+                            ];
+                        binfo.set_mv(&mvec);
+                    }
+                    if self.is_pb {
+                        let mut mvec: Vec<MV> = Vec::with_capacity(bbinfo.get_num_mv());
+                        for _ in 0..bbinfo.get_num_mv() {
+                            let mv = decode_mv(br, &self.tables.mv_cb)?;
+                            mvec.push(mv);
+                        }
+                        binfo.set_b_mv(mvec.as_slice());
+                    }
+                    Ok(binfo)
+                },
+            _ => Err(DecoderError::InvalidData),
+        }
+    }
+
+    #[allow(unused_variables)]
+    fn decode_block_intra(&mut self, info: &BlockInfo, _sstate: &SliceState, quant: u8, no: usize, coded: bool, blk: &mut [i16; 64]) -> DecoderResult<()> {
+        self.decode_block(quant, true, coded, blk, if no < 4 { 0 } else { no - 3 }, info.get_acpred())
+    }
+
+    #[allow(unused_variables)]
+    fn decode_block_inter(&mut self, info: &BlockInfo, _sstate: &SliceState, quant: u8, no: usize, coded: bool, blk: &mut [i16; 64]) -> DecoderResult<()> {
+        self.decode_block(quant, false, coded, blk, if no < 4 { 0 } else { no - 3 }, ACPredMode::None)
+    }
+
+    fn is_slice_end(&mut self) -> bool { self.br.peek(16) == 0 }
+}
+
+impl VivoDecoder {
+    fn new() -> Self {
+        let mut coderead = H263ShortCodeReader::new(H263_INTRA_MCBPC);
+        let intra_mcbpc_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+        let mut coderead = H263ShortCodeReader::new(H263_INTER_MCBPC);
+        let inter_mcbpc_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+        let mut coderead = H263ShortCodeReader::new(H263_CBPY);
+        let cbpy_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+        let mut coderead = H263RLCodeReader::new(H263_RL_CODES);
+        let rl_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+        let mut coderead = H263RLCodeReader::new(H263_RL_CODES_AIC);
+        let aic_rl_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+        let mut coderead = H263ShortCodeReader::new(H263_MV);
+        let mv_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+        let tables = Tables {
+            intra_mcbpc_cb,
+            inter_mcbpc_cb,
+            cbpy_cb,
+            rl_cb,
+            aic_rl_cb,
+            mv_cb,
+        };
+
+        VivoDecoder{
+            info:           NACodecInfo::new_dummy(),
+            dec:            H263BaseDecoder::new(true),
+            tables,
+            bdsp:           H263BlockDSP::new(),
+            lastframe:      None,
+            lastpts:        None,
+            width:          0,
+            height:         0,
+        }
+    }
+}
+
+impl NADecoder for VivoDecoder {
+    fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+        if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
+            let w = vinfo.get_width();
+            let h = vinfo.get_height();
+            let fmt = formats::YUV420_FORMAT;
+            let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(w, h, false, fmt));
+            self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
+            self.width  = w;
+            self.height = h;
+            Ok(())
+        } else {
+            Err(DecoderError::InvalidData)
+        }
+    }
+    fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+        let src = pkt.get_buffer();
+
+        if src.len() == 0 {
+            let buftype;
+            let ftype;
+            if self.lastframe.is_none() {
+                buftype = NABufferType::None;
+                ftype = FrameType::Skip;
+            } else {
+                let mut buf = None;
+                std::mem::swap(&mut self.lastframe, &mut buf);
+                buftype = buf.unwrap();
+                ftype = FrameType::B;
+            }
+            let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), buftype);
+            frm.set_keyframe(false);
+            frm.set_frame_type(ftype);
+            if self.lastpts.is_some() {
+                frm.set_pts(self.lastpts);
+                self.lastpts = None;
+            }
+            return Ok(frm.into_ref());
+        }
+        let mut ibr = VivoBR::new(&src, &self.tables, self.width, self.height);
+
+        let bufinfo = self.dec.parse_frame(&mut ibr, &self.bdsp)?;
+
+        let mut cur_pts = pkt.get_pts();
+        if !self.dec.is_intra() {
+            let bret = self.dec.get_bframe(&self.bdsp);
+            if let Ok(b_buf) = bret {
+                self.lastframe = Some(b_buf);
+                self.lastpts = pkt.get_pts();
+                if let Some(pts) = pkt.get_pts() {
+                    cur_pts = Some(pts + 1);
+                }
+            }
+        }
+
+        let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo);
+        frm.set_keyframe(self.dec.is_intra());
+        frm.set_frame_type(if self.dec.is_intra() { FrameType::I } else { FrameType::P });
+        frm.set_pts(cur_pts);
+        Ok(frm.into_ref())
+    }
+    fn flush(&mut self) {
+        self.dec.flush();
+    }
+}
+
+
+pub fn get_decoder() -> Box<dyn NADecoder + Send> {
+    Box::new(VivoDecoder::new())
+}
+
+#[cfg(test)]
+mod test {
+    use nihav_core::codecs::RegisteredDecoders;
+    use nihav_core::demuxers::RegisteredDemuxers;
+    use nihav_codec_support::test::dec_video::*;
+    use crate::vivo_register_all_codecs;
+    use crate::vivo_register_all_demuxers;
+    #[test]
+    fn test_vivo1() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        vivo_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        vivo_register_all_codecs(&mut dec_reg);
+
+test_file_decoding("vivo", "assets/Misc/gr_al.viv", Some(16), true, false, Some("viv1"), &dmx_reg, &dec_reg);
+//        test_decoding("vivo", "vivo1", "assets/Misc/gr_al.viv", Some(16),
+//                      &dmx_reg, &dec_reg, ExpectedTestResult::GenerateMD5Frames));
+    }
+    #[test]
+    fn test_vivo2() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        vivo_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        vivo_register_all_codecs(&mut dec_reg);
+
+test_file_decoding("vivo", "assets/Misc/02-KimagureOrangeRoad.viv", Some(50), true, false, Some("viv2"), &dmx_reg, &dec_reg);
+panic!("end");
+//        test_decoding("vivo", "vivo2", "assets/Misc/greetings.viv", Some(16),
+//                      &dmx_reg, &dec_reg, ExpectedTestResult::GenerateMD5Frames));
+    }
+}
diff --git a/nihav-vivo/src/demuxers/mod.rs b/nihav-vivo/src/demuxers/mod.rs
new file mode 100644 (file)
index 0000000..c76f106
--- /dev/null
@@ -0,0 +1,22 @@
+use nihav_core::demuxers::*;
+
+
+#[allow(unused_macros)]
+macro_rules! validate {
+    ($a:expr) => { if !$a { println!("check failed at {}:{}", file!(), line!()); return Err(DemuxerError::InvalidData); } };
+}
+
+#[cfg(feature="demuxer_vivo")]
+mod vivo;
+
+const DEMUXERS: &[&DemuxerCreator] = &[
+#[cfg(feature="demuxer_vivo")]
+    &vivo::VivoDemuxerCreator {},
+];
+
+/// Registers all available demuxers provided by this crate.
+pub fn vivo_register_all_demuxers(rd: &mut RegisteredDemuxers) {
+    for demuxer in DEMUXERS.iter() {
+        rd.add_demuxer(*demuxer);
+    }
+}
diff --git a/nihav-vivo/src/demuxers/vivo.rs b/nihav-vivo/src/demuxers/vivo.rs
new file mode 100644 (file)
index 0000000..06decd1
--- /dev/null
@@ -0,0 +1,268 @@
+use nihav_core::demuxers::*;
+
+struct VivoDemuxer<'a> {
+    src:            &'a mut ByteReader<'a>,
+    video_id:       usize,
+    audio_id:       usize,
+    video_buf:      Vec<u8>,
+    vpts:           u64,
+    apts:           u64,
+    v_num:          u32,
+    v_den:          u32,
+    a_num:          u32,
+    a_den:          u32,
+    fps:            f32,
+    width:          usize,
+    height:         usize,
+    vname:          &'static str,
+    aname:          &'static str,
+}
+
+fn read_size(br: &mut ByteReader) -> DemuxerResult<usize> {
+    let mut ret = 0;
+    loop {
+        let c = br.read_byte()?;
+        ret = (ret << 7) | ((c & 0x7F) as usize);
+        if (c & 0x80) == 0 {
+            break;
+        }
+    }
+    Ok(ret)
+}
+
+impl<'a> DemuxCore<'a> for VivoDemuxer<'a> {
+    fn open(&mut self, strmgr: &mut StreamManager, _seek_idx: &mut SeekIndex) -> DemuxerResult<()> {
+        let mut hdr_data: Vec<u8> = Vec::with_capacity(256);
+        loop {
+            let hdr                     = self.src.peek_byte()?;
+            if (hdr & 0xF0) != 0 { break; }
+                                          self.src.read_skip(1)?;
+            let hdr_len                 = read_size(&mut self.src)?;
+            hdr_data.resize(hdr_len, 0);
+                                          self.src.read_buf(&mut hdr_data)?;
+            self.parse_header_packet(&hdr_data)?;
+        }
+
+        validate!(self.vname != "none");
+        if self.width == 0 && self.height == 0 {
+            self.width  = 160;
+            self.height = 120;
+        }
+        validate!(self.fps > 0.0 || (self.v_num > 0 && self.v_den > 0));
+
+        if self.v_num == 0 {
+            self.v_num = 1000;
+            self.v_den = (self.fps * 1000.0) as u32;
+        }
+
+        let vhdr = NAVideoInfo::new(self.width, self.height, false, YUV420_FORMAT);
+        let vinfo = NACodecInfo::new(self.vname, NACodecTypeInfo::Video(vhdr), None);
+        let res = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, self.v_num, self.v_den));
+        validate!(res.is_some());
+        self.video_id = res.unwrap();
+
+        if self.aname == "none" && self.vname == "vivo1" {
+            self.aname = "g723.1";
+            self.a_num = 240;
+            self.a_den = 8000;
+        }
+        if self.aname != "none" {
+            let ahdr = NAAudioInfo::new(self.a_den, 1, SND_S16_FORMAT, self.a_num as usize);
+            let ainfo = NACodecInfo::new(self.aname, NACodecTypeInfo::Audio(ahdr), None);
+            let res = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, self.a_num, self.a_den));
+            validate!(res.is_some());
+            self.audio_id = res.unwrap();
+        }
+
+        Ok(())
+    }
+    fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
+        let br = &mut self.src;
+
+        loop {
+            let ret                     = br.read_byte();
+            let hdr1 = match ret {
+                    Err(ByteIOError::EOF) => return Err(DemuxerError::EOF),
+                    Err(error)            => return Err(error.into()),
+                    Ok(val) => val,
+                };
+            let force_size = hdr1 == 0x82;
+            let hdr = if force_size { br.read_byte()? } else { hdr1 };
+            let (is_video, mut size) = match hdr >> 4 {
+                    1 => { (true, 128) },
+                    2 => { validate!(!force_size); (true, read_size(br)?) },
+                    3 => { (false, 40) },
+                    4 => { (false, 24) },
+                    _ => return Err(DemuxerError::InvalidData),
+                };
+            if force_size {
+                size                    = read_size(br)?;
+            }
+            if is_video {
+                validate!(self.v_den != 0);
+                let cur_size = self.video_buf.len();
+                let new_size = cur_size + size;
+                self.video_buf.resize(new_size, 0);
+                                          br.read_buf(&mut self.video_buf[cur_size..])?;
+                if (hdr >> 4) == 2 {
+                    let mut buf = Vec::new();
+                    std::mem::swap(&mut self.video_buf, &mut buf);
+                    let strres = strmgr.get_stream(self.video_id);
+                    validate!(strres.is_some());
+                    let stream = strres.unwrap();
+                    let ts = NATimeInfo::new(Some(self.vpts), None, None, self.v_num, self.v_den);
+                    let pkt = NAPacket::new(stream, ts, self.vpts == 0, buf);
+                    self.vpts += 1;
+                    return Ok(pkt);
+                }
+            } else {
+                validate!(self.a_den != 0);
+                let mut buf: Vec<u8> = vec![0; size];
+                                          br.read_buf(&mut buf)?;
+                let strres = strmgr.get_stream(self.audio_id);
+                validate!(strres.is_some());
+                let stream = strres.unwrap();
+                let ts = NATimeInfo::new(Some(self.apts), None, None, self.a_num, self.a_den);
+                let pkt = NAPacket::new(stream, ts, true, buf);
+                self.apts += 1;
+                return Ok(pkt);
+            }
+        }
+    }
+    fn seek(&mut self, _time: u64, _seek_idx: &SeekIndex) -> DemuxerResult<()> {
+        Err(DemuxerError::NotPossible)
+    }
+}
+
+impl<'a> VivoDemuxer<'a> {
+    fn new(io: &'a mut ByteReader<'a>) -> Self {
+        VivoDemuxer {
+            src:            io,
+            video_id:       0,
+            audio_id:       0,
+            video_buf:      Vec::new(),
+            vpts:           0,
+            apts:           0,
+            v_num:          0,
+            v_den:          0,
+            a_num:          0,
+            a_den:          0,
+            width:          0,
+            height:         0,
+            fps:            0.0,
+            vname:          "none",
+            aname:          "none",
+        }
+    }
+    fn parse_header_packet(&mut self, pkt: &[u8]) -> DemuxerResult<()> {
+        for entry in pkt.split(|ch| *ch == 0xD) {
+            if entry.len() < 3 || !entry.contains(&b':') { continue; }
+            let entry = if entry.len() > 0 && entry[0] == 0xA { &entry[1..] } else { entry };
+            let mut split = entry.split(|ch| *ch == b':');
+            let name  = split.next().unwrap();
+            let value = split.next().unwrap();
+
+            let valstr = String::from_utf8_lossy(value);
+            match name {
+                b"Version" => {
+                    match value {
+                        b"Vivo/0.90" => { self.vname = "vivo1"; },
+                        b"Vivo/1.00" => { self.vname = "vivo1"; },
+                        b"Vivo/2.00" => { self.vname = "vivo2"; },
+                        _ => {
+                            println!("Unknown codec name {}", valstr);
+                            return Err(DemuxerError::InvalidData);
+                        },
+                    };
+                },
+                b"FPS" => {
+                    self.fps = if let Ok(val) = valstr.parse() {
+                            val
+                        } else { return Err(DemuxerError::InvalidData); };
+                    validate!(self.fps > 0.0 && self.fps < 1000.0);
+                },
+                b"Width" => {
+                    self.width = if let Ok(val) = valstr.parse() {
+                            val
+                        } else { return Err(DemuxerError::InvalidData); };
+                    validate!(self.width > 0 && self.width <= 1024);
+                },
+                b"Height" => {
+                    self.height = if let Ok(val) = valstr.parse() {
+                            val
+                        } else { return Err(DemuxerError::InvalidData); };
+                    validate!(self.height > 0 && self.height <= 768);
+                },
+                b"SamplingFrequency" => {
+                    let samp_freq: u32 = if let Ok(val) = valstr.parse() {
+                            val
+                        } else { return Err(DemuxerError::InvalidData); };
+                    validate!(samp_freq == 8000 || samp_freq == 16000);
+                    if samp_freq == 8000 {
+                        self.aname = "g723.1";
+                        self.a_num = 240;
+                        self.a_den = 8000;
+                    } else if samp_freq == 16000 {
+                        self.aname = "siren";
+                        self.a_num = 320;
+                        self.a_den = 16000;
+                    } else {
+                        return Err(DemuxerError::InvalidData);
+                    };
+                },
+/*                b"TimeUnitNumerator" => {
+                    self.v_num = if let Ok(val) = valstr.parse() {
+                            val
+                        } else { return Err(DemuxerError::InvalidData); };
+                    validate!(self.v_num > 0);
+                    println!(" video codec tb_num {}", self.v_num);
+                },
+                b"TimeUnitDenominator" => {
+                    self.v_den = if let Ok(val) = valstr.parse() {
+                            val
+                        } else { return Err(DemuxerError::InvalidData); };
+                    validate!(self.v_den > 0);
+                    println!(" video codec tb_den {}", self.v_den);
+                },*/
+                _ => { },
+            };
+        }
+        Ok(())
+    }
+}
+
+pub struct VivoDemuxerCreator { }
+
+impl DemuxerCreator for VivoDemuxerCreator {
+    fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> {
+        Box::new(VivoDemuxer::new(br))
+    }
+    fn get_name(&self) -> &'static str { "vivo" }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use std::fs::File;
+
+    #[test]
+    fn test_vivo_demux() {
+//        let mut file = File::open("assets/Misc/greetings.viv").unwrap();
+        let mut file = File::open("assets/Misc/favmovie.viv").unwrap();
+        let mut fr = FileReader::new_read(&mut file);
+        let mut br = ByteReader::new(&mut fr);
+        let mut dmx = VivoDemuxer::new(&mut br);
+        let mut sm = StreamManager::new();
+        let mut si = SeekIndex::new();
+        dmx.open(&mut sm, &mut si).unwrap();
+        loop {
+            let pktres = dmx.get_frame(&mut sm);
+            if let Err(e) = pktres {
+                if (e as i32) == (DemuxerError::EOF as i32) { break; }
+                panic!("error {:?}", e);
+            }
+            let pkt = pktres.unwrap();
+            println!("Got {}", pkt);
+        }
+    }
+}
diff --git a/nihav-vivo/src/lib.rs b/nihav-vivo/src/lib.rs
new file mode 100644 (file)
index 0000000..74bdb98
--- /dev/null
@@ -0,0 +1,8 @@
+extern crate nihav_core;
+extern crate nihav_codec_support;
+
+mod codecs;
+mod demuxers;
+
+pub use crate::codecs::vivo_register_all_codecs;
+pub use crate::demuxers::vivo_register_all_demuxers;