| # |
| # Copyright 2022 Google LLC |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| |
| import numpy as np |
| |
| import lc3 |
| import tables as T, appendix_c as C |
| |
| import bwdet as m_bwdet |
| import ltpf as m_ltpf |
| import sns as m_sns |
| import tns as m_tns |
| |
| ### ------------------------------------------------------------------------ ### |
| |
| class SpectrumQuantization: |
| |
| def __init__(self, dt, sr): |
| |
| self.dt = dt |
| self.sr = sr |
| |
| def get_gain_offset(self, nbytes): |
| |
| g_off = (nbytes * 8) // (10 * (1 + self.sr)) |
| g_off = -min(115, g_off) - (105 + 5*(1 + self.sr)) |
| |
| return g_off |
| |
| def get_noise_indices(self, bw, xq, lastnz): |
| |
| nf_start = [ 18, 24 ][self.dt] |
| nf_width = [ 2, 3 ][self.dt] |
| |
| bw_stop = int([ 80, 160, 240, 320, 400 ][bw] * (T.DT_MS[self.dt] / 10)) |
| |
| xq = np.append(xq[:lastnz], np.zeros(len(xq) - lastnz)) |
| |
| i_nf = [ np.all(xq[k-nf_width:min(bw_stop, k+nf_width+1)] == 0) |
| for k in range(nf_start, bw_stop) ] |
| |
| return (i_nf, nf_start, bw_stop) |
| |
| |
| class SpectrumAnalysis(SpectrumQuantization): |
| |
| def __init__(self, dt, sr): |
| |
| super().__init__(dt, sr) |
| |
| self.reset_off = 0 |
| self.nbits_off = 0 |
| self.nbits_spec = 0 |
| self.nbits_est = 0 |
| |
| (self.g_idx, self.noise_factor, self.xq, self.lastnz, |
| self.nbits_residual_max, self.xg) = \ |
| (None, None, None, None, None, None) |
| |
| def estimate_gain(self, x, nbits_spec, nbits_off, g_off): |
| |
| nbits = int(nbits_spec + nbits_off + 0.5) |
| |
| ### Energy (dB) by 4 MDCT coefficients |
| |
| e = [ np.sum(x[4*k:4*(k+1)] ** 2) for k in range(len(x) // 4) ] |
| e = 10 * np.log10(2**-31 + np.array(e)) |
| |
| ### Compute gain index |
| |
| g_idx = 255 |
| |
| for i in range(8): |
| factor = 1 << (7 - i) |
| g_idx -= factor |
| tmp = 0 |
| iszero = 1 |
| |
| for ei in e[-1::-1]: |
| |
| if ei * 28/20 < g_idx + g_off: |
| if iszero == 0: |
| tmp += 2.7*28/20 |
| else: |
| if g_idx + g_off < (ei - 43) * 28/20: |
| tmp += 2*ei*28/20 - 2*(g_idx + g_off) - 36*28/20 |
| else: |
| tmp += ei*28/20 - (g_idx + g_off) + 7*28/20 |
| iszero = 0 |
| |
| if tmp > nbits * 1.4 * 28/20 and iszero == 0: |
| g_idx += factor |
| |
| ### Limit gain index |
| |
| x_max = np.amax(np.abs(x)) |
| if x_max > 0: |
| g_min = 28 * np.log10(x_max / (32768 - 0.375)) |
| g_min = np.ceil(g_min).astype(int) - g_off |
| reset_off = g_idx < g_min |
| else: |
| g_min = 0 |
| reset_off = True |
| |
| if reset_off: |
| g_idx = g_min |
| |
| return (g_idx + g_off, reset_off) |
| |
| def quantize(self, g_int, x): |
| |
| xg = x / 10 ** (g_int / 28) |
| |
| xq = np.where(xg < 0, np.ceil(xg - 0.375), np.floor(xg + 0.375)) |
| xq = xq.astype(int) |
| xq = np.fmin(np.fmax(xq, -32768), 32767) |
| |
| nz_pairs = np.any([ xq[::2] != 0, xq[1::2] != 0 ], axis=0) |
| lastnz = len(xq) - 2 * np.argmax(nz_pairs[-1::-1]) |
| if not np.any(nz_pairs): |
| lastnz = 0 |
| |
| return (xg, xq, lastnz) |
| |
| def compute_nbits(self, nbytes, x, lastnz, nbits_spec): |
| |
| mode = 1 if nbytes >= 20 * (3 + self.sr) else 0 |
| rate = 512 if nbytes > 20 * (1 + self.sr) else 0 |
| |
| nbits_est = 0 |
| nbits_trunc = 0 |
| nbits_lsb = 0 |
| lastnz_trunc = 2 |
| c = 0 |
| |
| for n in range(0, lastnz, 2): |
| t = c + rate |
| if n > len(x) // 2: |
| t += 256 |
| |
| a = abs(x[n ]) |
| b = abs(x[n+1]) |
| lev = 0 |
| while max(a, b) >= 4: |
| nbits_est += \ |
| T.AC_SPEC_BITS[T.AC_SPEC_LOOKUP[t + lev*1024]][16]; |
| if lev == 0 and mode == 1: |
| nbits_lsb += 2 |
| else: |
| nbits_est += 2 * 2048 |
| |
| a >>= 1 |
| b >>= 1 |
| lev = min(lev + 1, 3) |
| |
| nbits_est += \ |
| T.AC_SPEC_BITS[T.AC_SPEC_LOOKUP[t + lev*1024]][a + 4*b] |
| |
| a_lsb = abs(x[n ]) |
| b_lsb = abs(x[n+1]) |
| nbits_est += (min(a_lsb, 1) + min(b_lsb, 1)) * 2048 |
| if lev > 0 and mode == 1: |
| a_lsb >>= 1; |
| b_lsb >>= 1; |
| nbits_lsb += int(a_lsb == 0 and x[n ] != 0) |
| nbits_lsb += int(b_lsb == 0 and x[n+1] != 0) |
| |
| if (x[n] != 0 or x[n+1] != 0) and \ |
| (nbits_est <= nbits_spec * 2048): |
| lastnz_trunc = n + 2; |
| nbits_trunc = nbits_est |
| |
| t = 1 + (a + b) * (lev + 1) if lev <= 1 else 12 + lev; |
| c = (c & 15) * 16 + t; |
| |
| nbits_est = (nbits_est + 2047) // 2048 + nbits_lsb; |
| nbits_trunc = (nbits_trunc + 2047) // 2048 |
| |
| self.rate = rate |
| self.lsb_mode = mode == 1 and nbits_est > nbits_spec |
| |
| return (nbits_est, nbits_trunc, lastnz_trunc, self.lsb_mode) |
| |
| def adjust_gain(self, g_idx, nbits, nbits_spec): |
| |
| T1 = [ 80, 230, 380, 530, 680 ] |
| T2 = [ 500, 1025, 1550, 2075, 2600 ] |
| T3 = [ 850, 1700, 2550, 3400, 4250 ] |
| |
| sr = self.sr |
| |
| if nbits < T1[sr]: |
| delta = (nbits + 48) / 16 |
| |
| elif nbits < T2[sr]: |
| a = T1[sr] / 16 + 3 |
| b = T2[sr] / 48 |
| delta = a + (nbits - T1[sr]) * (b - a) / (T2[sr] - T1[sr]) |
| |
| elif nbits < T3[sr]: |
| delta = nbits / 48 |
| |
| else: |
| delta = T3[sr] / 48; |
| |
| delta = np.fix(delta + 0.5).astype(int) |
| |
| if (g_idx < 255 and nbits > nbits_spec) or \ |
| (g_idx > 0 and nbits < nbits_spec - (delta + 2)): |
| |
| if nbits < nbits_spec - (delta + 2): |
| return - 1 |
| |
| if g_idx == 254 or nbits < nbits_spec + delta: |
| return 1 |
| |
| else: |
| return 2 |
| |
| return 0 |
| |
| def estimate_noise(self, bw, xq, lastnz, x): |
| |
| (i_nf, nf_start, nf_stop) = self.get_noise_indices(bw, xq, lastnz) |
| |
| nf = 8 - 16 * sum(abs(x[nf_start:nf_stop] * i_nf)) / sum(i_nf) \ |
| if sum(i_nf) > 0 else 0 |
| |
| return min(max(np.rint(nf).astype(int), 0), 7) |
| |
| def run(self, |
| bw, nbytes, nbits_bw, nbits_ltpf, nbits_sns, nbits_tns, x): |
| |
| sr = self.sr |
| |
| ### Bit budget |
| |
| nbits_gain = 8 |
| nbits_nf = 3 |
| |
| nbits_ari = np.ceil(np.log2(len(x) / 2)).astype(int) |
| nbits_ari += 3 + min((8*nbytes - 1) // 1280, 2) |
| |
| nbits_spec = 8*nbytes - \ |
| nbits_bw - nbits_ltpf - nbits_sns - nbits_tns - \ |
| nbits_gain - nbits_nf - nbits_ari |
| |
| ### Global gain estimation |
| |
| nbits_off = self.nbits_off + self.nbits_spec - self.nbits_est |
| nbits_off = min(40, max(-40, nbits_off)) |
| |
| nbits_off = 0 if self.reset_off else \ |
| 0.8 * self.nbits_off + 0.2 * nbits_off |
| |
| g_off = self.get_gain_offset(nbytes) |
| |
| (g_int, self.reset_off) = \ |
| self.estimate_gain(x, nbits_spec, nbits_off, g_off) |
| self.nbits_off = nbits_off |
| self.nbits_spec = nbits_spec |
| |
| ### Quantization |
| |
| (xg, xq, lastnz) = self.quantize(g_int, x) |
| |
| (nbits_est, nbits_trunc, lastnz_trunc, _) = \ |
| self.compute_nbits(nbytes, xq, lastnz, nbits_spec) |
| |
| self.nbits_est = nbits_est |
| |
| ### Adjust gain and requantize |
| |
| g_adj = self.adjust_gain(g_int - g_off, nbits_est, nbits_spec) |
| |
| (xg, xq, lastnz) = self.quantize(g_adj, xg) |
| |
| (nbits_est, nbits_trunc, lastnz_trunc, lsb_mode) = \ |
| self.compute_nbits(nbytes, xq, lastnz, nbits_spec) |
| |
| self.g_idx = g_int + g_adj - g_off |
| self.xq = xq |
| self.lastnz = lastnz_trunc |
| |
| self.nbits_residual_max = nbits_spec - nbits_trunc + 4 |
| self.xg = xg |
| |
| ### Noise factor |
| |
| self.noise_factor = self.estimate_noise(bw, xq, lastnz, x) |
| |
| return (self.xq, self.lastnz, self.xg) |
| |
| def store(self, b): |
| |
| ne = T.NE[self.dt][self.sr] |
| nbits_lastnz = np.ceil(np.log2(ne/2)).astype(int) |
| |
| b.write_uint((self.lastnz >> 1) - 1, nbits_lastnz) |
| b.write_uint(self.lsb_mode, 1) |
| b.write_uint(self.g_idx, 8) |
| |
| def encode(self, bits): |
| |
| ### Noise factor |
| |
| bits.write_uint(self.noise_factor, 3) |
| |
| ### Quantized data |
| |
| lsbs = [] |
| |
| x = self.xq |
| c = 0 |
| |
| for n in range(0, self.lastnz, 2): |
| t = c + self.rate |
| if n > len(x) // 2: |
| t += 256 |
| |
| a = abs(x[n ]) |
| b = abs(x[n+1]) |
| lev = 0 |
| while max(a, b) >= 4: |
| |
| bits.ac_encode( |
| T.AC_SPEC_CUMFREQ[T.AC_SPEC_LOOKUP[t + lev*1024]][16], |
| T.AC_SPEC_FREQ[T.AC_SPEC_LOOKUP[t + lev*1024]][16]) |
| |
| if lev == 0 and self.lsb_mode: |
| lsb_0 = a & 1 |
| lsb_1 = b & 1 |
| else: |
| bits.write_bit(a & 1) |
| bits.write_bit(b & 1) |
| |
| a >>= 1 |
| b >>= 1 |
| lev = min(lev + 1, 3) |
| |
| bits.ac_encode( |
| T.AC_SPEC_CUMFREQ[T.AC_SPEC_LOOKUP[t + lev*1024]][a + 4*b], |
| T.AC_SPEC_FREQ[T.AC_SPEC_LOOKUP[t + lev*1024]][a + 4*b]) |
| |
| a_lsb = abs(x[n ]) |
| b_lsb = abs(x[n+1]) |
| if lev > 0 and self.lsb_mode: |
| a_lsb >>= 1 |
| b_lsb >>= 1 |
| |
| lsbs.append(lsb_0) |
| if a_lsb == 0 and x[n+0] != 0: |
| lsbs.append(int(x[n+0] < 0)) |
| |
| lsbs.append(lsb_1) |
| if b_lsb == 0 and x[n+1] != 0: |
| lsbs.append(int(x[n+1] < 0)) |
| |
| if a_lsb > 0: |
| bits.write_bit(int(x[n+0] < 0)) |
| |
| if b_lsb > 0: |
| bits.write_bit(int(x[n+1] < 0)) |
| |
| t = 1 + (a + b) * (lev + 1) if lev <= 1 else 12 + lev; |
| c = (c & 15) * 16 + t; |
| |
| ### Residual data |
| |
| if self.lsb_mode == 0: |
| nbits_residual = min(bits.get_bits_left(), self.nbits_residual_max) |
| |
| for i in range(len(self.xg)): |
| |
| if self.xq[i] == 0: |
| continue |
| |
| bits.write_bit(self.xg[i] >= self.xq[i]) |
| nbits_residual -= 1 |
| if nbits_residual <= 0: |
| break |
| |
| else: |
| nbits_residual = min(bits.get_bits_left(), len(lsbs)) |
| for lsb in lsbs[:nbits_residual]: |
| bits.write_bit(lsb) |
| |
| |
| class SpectrumSynthesis(SpectrumQuantization): |
| |
| def __init__(self, dt, sr): |
| |
| super().__init__(dt, sr) |
| |
| (self.lastnz, self.lsb_mode, self.g_idx) = \ |
| (None, None, None) |
| |
| def fill_noise(self, bw, x, lastnz, f_nf, nf_seed): |
| |
| (i_nf, nf_start, nf_stop) = self.get_noise_indices(bw, x, lastnz) |
| |
| k_nf = nf_start + np.argwhere(i_nf) |
| l_nf = (8 - f_nf)/16 |
| |
| for k in k_nf: |
| nf_seed = (13849 + nf_seed * 31821) & 0xffff |
| x[k] = [ -l_nf, l_nf ][nf_seed < 0x8000] |
| |
| return x |
| |
| def load(self, b): |
| |
| ne = T.NE[self.dt][self.sr] |
| nbits_lastnz = np.ceil(np.log2(ne/2)).astype(int) |
| |
| self.lastnz = (b.read_uint(nbits_lastnz) + 1) << 1 |
| self.lsb_mode = b.read_uint(1) |
| self.g_idx = b.read_uint(8) |
| |
| if self.lastnz > ne: |
| raise ValueError('Invalid count of coded samples') |
| |
| def decode(self, bits, bw, nbytes): |
| |
| ### Noise factor |
| |
| f_nf = bits.read_uint(3) |
| |
| ### Quantized data |
| |
| x = np.zeros(T.NE[self.dt][self.sr]) |
| rate = 512 if nbytes > 20 * (1 + self.sr) else 0 |
| |
| levs = np.zeros(len(x), dtype=np.intc) |
| c = 0 |
| |
| for n in range(0, self.lastnz, 2): |
| t = c + rate |
| if n > len(x) // 2: |
| t += 256 |
| |
| for lev in range(14): |
| |
| s = t + min(lev, 3) * 1024 |
| |
| sym = bits.ac_decode( |
| T.AC_SPEC_CUMFREQ[T.AC_SPEC_LOOKUP[s]], |
| T.AC_SPEC_FREQ[T.AC_SPEC_LOOKUP[s]]) |
| |
| if sym < 16: |
| break |
| |
| if self.lsb_mode == 0 or lev > 0: |
| x[n ] += bits.read_bit() << lev |
| x[n+1] += bits.read_bit() << lev |
| |
| if lev >= 14: |
| raise ValueError('Out of range value') |
| |
| a = sym % 4 |
| b = sym // 4 |
| |
| levs[n ] = lev |
| levs[n+1] = lev |
| |
| x[n ] += a << lev |
| x[n+1] += b << lev |
| |
| if x[n] and bits.read_bit(): |
| x[n] = -x[n] |
| |
| if x[n+1] and bits.read_bit(): |
| x[n+1] = -x[n+1] |
| |
| lev = min(lev, 3) |
| t = 1 + (a + b) * (lev + 1) if lev <= 1 else 12 + lev; |
| c = (c & 15) * 16 + t; |
| |
| ### Residual data |
| |
| nbits_residual = bits.get_bits_left() |
| if nbits_residual < 0: |
| raise ValueError('Out of bitstream') |
| |
| if self.lsb_mode == 0: |
| |
| xr = np.zeros(len(x), dtype=np.bool) |
| |
| for i in range(len(x)): |
| |
| if nbits_residual <= 0: |
| xr.resize(i) |
| break |
| |
| if x[i] == 0: |
| continue |
| |
| xr[i] = bits.read_bit() |
| nbits_residual -= 1 |
| |
| else: |
| |
| for i in range(len(levs)): |
| |
| if nbits_residual <= 0: |
| break |
| |
| if levs[i] <= 0: |
| continue |
| |
| lsb = bits.read_bit() |
| nbits_residual -= 1 |
| if not lsb: |
| continue |
| |
| sign = int(x[i] < 0) |
| |
| if x[i] == 0: |
| |
| if nbits_residual <= 0: |
| break |
| |
| sign = bits.read_bit() |
| nbits_residual -= 1 |
| |
| x[i] += [ 1, -1 ][sign] |
| |
| ### Set residual and noise |
| |
| nf_seed = sum(abs(x.astype(np.intc)) * range(len(x))) |
| |
| zero_frame = (self.lastnz <= 2 and x[0] == 0 and x[1] == 0 |
| and self.g_idx <= 0 and f_nf >= 7) |
| |
| if self.lsb_mode == 0: |
| |
| for i in range(len(xr)): |
| |
| if x[i] and xr[i] == 0: |
| x[i] += [ -0.1875, -0.3125 ][x[i] < 0] |
| elif x[i]: |
| x[i] += [ 0.1875, 0.3125 ][x[i] > 0] |
| |
| if not zero_frame: |
| x = self.fill_noise(bw, x, self.lastnz, f_nf, nf_seed) |
| |
| ### Rescale coefficients |
| |
| g_int = self.get_gain_offset(nbytes) + self.g_idx |
| x *= 10 ** (g_int / 28) |
| |
| return x |
| |
| |
| def initial_state(): |
| return { 'nbits_off' : 0.0, 'nbits_spare' : 0 } |
| |
| |
| ### ------------------------------------------------------------------------ ### |
| |
| def check_estimate_gain(rng, dt, sr): |
| |
| ne = T.I[dt][sr][-1] |
| ok = True |
| |
| analysis = SpectrumAnalysis(dt, sr) |
| |
| for i in range(10): |
| x = rng.random(ne) * i * 1e2 |
| |
| nbytes = 20 + int(rng.random() * 100) |
| nbits_budget = 8 * nbytes - int(rng.random() * 100) |
| nbits_off = rng.random() * 10 |
| g_off = 10 - int(rng.random() * 20) |
| |
| (g_int, reset_off) = \ |
| analysis.estimate_gain(x, nbits_budget, nbits_off, g_off) |
| |
| (g_int_c, reset_off_c) = lc3.spec_estimate_gain( |
| dt, sr, x, nbits_budget, nbits_off, -g_off) |
| |
| ok = ok and g_int_c == g_int |
| ok = ok and reset_off_c == reset_off |
| |
| return ok |
| |
| def check_quantization(rng, dt, sr): |
| |
| ne = T.I[dt][sr][-1] |
| ok = True |
| |
| analysis = SpectrumAnalysis(dt, sr) |
| |
| for g_int in range(-128, 128): |
| |
| x = rng.random(ne) * 1e2 |
| nbytes = 20 + int(rng.random() * 30) |
| |
| (xg, xq, nq) = analysis.quantize(g_int, x) |
| (xg_c, xq_c, nq_c) = lc3.spec_quantize(dt, sr, g_int, x) |
| |
| ok = ok and np.amax(np.abs(1 - xg_c/xg)) < 1e-6 |
| ok = ok and np.any(abs(xq_c - xq) < 1) |
| ok = ok and nq_c == nq |
| |
| return ok |
| |
| def check_compute_nbits(rng, dt, sr): |
| |
| ne = T.I[dt][sr][-1] |
| ok = True |
| |
| analysis = SpectrumAnalysis(dt, sr) |
| |
| for nbytes in range(20, 150): |
| |
| nbits_budget = nbytes * 8 - int(rng.random() * 100) |
| xq = (rng.random(ne) * 8).astype(int) |
| nq = ne // 2 + int(rng.random() * ne // 2) |
| |
| nq = nq - nq % 2 |
| if xq[nq-2] == 0 and xq[nq-1] == 0: |
| xq[nq-2] = 1 |
| |
| (nbits, nbits_trunc, nq_trunc, lsb_mode) = \ |
| analysis.compute_nbits(nbytes, xq, nq, nbits_budget) |
| |
| (nbits_c, nq_c, _) = \ |
| lc3.spec_compute_nbits(dt, sr, nbytes, xq, nq, 0) |
| |
| (nbits_trunc_c, nq_trunc_c, lsb_mode_c) = \ |
| lc3.spec_compute_nbits(dt, sr, nbytes, xq, nq, nbits_budget) |
| |
| ok = ok and nbits_c == nbits |
| ok = ok and nbits_trunc_c == nbits_trunc |
| ok = ok and nq_trunc_c == nq_trunc |
| ok = ok and lsb_mode_c == lsb_mode |
| |
| return ok |
| |
| def check_adjust_gain(rng, dt, sr): |
| |
| ne = T.I[dt][sr][-1] |
| ok = True |
| |
| analysis = SpectrumAnalysis(dt, sr) |
| |
| for g_idx in (0, 128, 254, 255): |
| for nbits in range(50, 5000, 5): |
| nbits_budget = int(nbits * (0.95 + (rng.random() * 0.1))) |
| |
| g_adj = analysis.adjust_gain(g_idx, nbits, nbits_budget) |
| |
| g_adj_c = lc3.spec_adjust_gain(sr, g_idx, nbits, nbits_budget) |
| |
| ok = ok and g_adj_c == g_adj |
| |
| return ok |
| |
| def check_unit(rng, dt, sr): |
| |
| ns = T.NS[dt][sr] |
| ne = T.I[dt][sr][-1] |
| ok = True |
| |
| state_c = initial_state() |
| |
| bwdet = m_bwdet.BandwidthDetector(dt, sr) |
| ltpf = m_ltpf.LtpfAnalysis(dt, sr) |
| tns = m_tns.TnsAnalysis(dt) |
| sns = m_sns.SnsAnalysis(dt, sr) |
| analysis = SpectrumAnalysis(dt, sr) |
| |
| nbytes = 100 |
| |
| for i in range(10): |
| |
| x = rng.random(ns) * 1e4 |
| e = rng.random(min(len(x), 64)) * 1e10 |
| |
| bwdet.run(e) |
| pitch_present = ltpf.run(x) |
| tns.run(x[:ne], sr, False, nbytes) |
| sns.run(e, False, x) |
| |
| (xq, nq, _) = analysis.run(sr, nbytes, bwdet.get_nbits(), |
| ltpf.get_nbits(), sns.get_nbits(), tns.get_nbits(), x[:ne]) |
| |
| (_, xq_c, side_c) = lc3.spec_analyze( |
| dt, sr, nbytes, pitch_present, tns.get_data(), state_c, x[:ne]) |
| |
| ok = ok and side_c['g_idx'] == analysis.g_idx |
| ok = ok and side_c['nq'] == nq |
| ok = ok and np.any(abs(xq_c - xq) < 1) |
| |
| return ok |
| |
| def check_noise(rng, dt, bw): |
| |
| ne = T.NE[dt][bw] |
| ok = True |
| |
| analysis = SpectrumAnalysis(dt, bw) |
| |
| for i in range(10): |
| |
| xq = ((rng.random(ne) - 0.5) * 10 ** (0.5)).astype(int) |
| nq = ne - int(rng.random() * 5) |
| x = rng.random(ne) * i * 1e-1 |
| |
| nf = analysis.estimate_noise(bw, xq, nq, x) |
| nf_c = lc3.spec_estimate_noise(dt, bw, xq, nq, x) |
| |
| ok = ok and nf_c == nf |
| |
| return ok |
| |
| def check_appendix_c(dt): |
| |
| sr = T.SRATE_16K |
| ne = T.NE[dt][sr] |
| ok = True |
| |
| state_c = initial_state() |
| |
| for i in range(len(C.X_F[dt])): |
| |
| g_int = lc3.spec_estimate_gain(dt, sr, C.X_F[dt][i], |
| C.NBITS_SPEC[dt][i], C.NBITS_OFFSET[dt][i], -C.GG_OFF[dt][i])[0] |
| ok = ok and g_int == C.GG_IND[dt][i] + C.GG_OFF[dt][i] |
| |
| (_, xq, nq) = lc3.spec_quantize(dt, sr, |
| C.GG_IND[dt][i] + C.GG_OFF[dt][i], C.X_F[dt][i]) |
| ok = ok and np.any((xq - C.X_Q[dt][i]) == 0) |
| ok = ok and nq == C.LASTNZ[dt][i] |
| |
| nbits = lc3.spec_compute_nbits(dt, sr, |
| C.NBYTES[dt], C.X_Q[dt][i], C.LASTNZ[dt][i], 0)[0] |
| ok = ok and nbits == C.NBITS_EST[dt][i] |
| |
| g_adj = lc3.spec_adjust_gain(sr, |
| C.GG_IND[dt][i], C.NBITS_EST[dt][i], C.NBITS_SPEC[dt][i]) |
| ok = ok and g_adj == C.GG_IND_ADJ[dt][i] - C.GG_IND[dt][i] |
| |
| if C.GG_IND_ADJ[dt][i] != C.GG_IND[dt][i]: |
| |
| (_, xq, nq) = lc3.spec_quantize(dt, sr, |
| C.GG_IND_ADJ[dt][i] + C.GG_OFF[dt][i], C.X_F[dt][i]) |
| lastnz = C.LASTNZ_REQ[dt][i] |
| ok = ok and np.any(((xq - C.X_Q_REQ[dt][i])[:lastnz]) == 0) |
| |
| tns_data = { |
| 'nfilters' : C.NUM_TNS_FILTERS[dt][i], |
| 'lpc_weighting' : [ True, True ], |
| 'rc_order' : [ C.RC_ORDER[dt][i][0], 0 ], |
| 'rc' : [ C.RC_I_1[dt][i] - 8, np.zeros(8, dtype = np.intc) ] |
| } |
| |
| (x, xq, side) = lc3.spec_analyze(dt, sr, C.NBYTES[dt], |
| C.PITCH_PRESENT[dt][i], tns_data, state_c, C.X_F[dt][i]) |
| |
| ok = ok and np.abs(state_c['nbits_off'] - C.NBITS_OFFSET[dt][i]) < 1e-5 |
| if C.GG_IND_ADJ[dt][i] != C.GG_IND[dt][i]: |
| xq = C.X_Q_REQ[dt][i] |
| nq = C.LASTNZ_REQ[dt][i] |
| ok = ok and side['g_idx'] == C.GG_IND_ADJ[dt][i] |
| ok = ok and side['nq'] == nq |
| ok = ok and np.any(((xq[:nq] - xq[:nq])) == 0) |
| else: |
| xq = C.X_Q[dt][i] |
| nq = C.LASTNZ[dt][i] |
| ok = ok and side['g_idx'] == C.GG_IND[dt][i] |
| ok = ok and side['nq'] == nq |
| ok = ok and np.any((xq[:nq] - C.X_Q[dt][i][:nq]) == 0) |
| ok = ok and side['lsb_mode'] == C.LSB_MODE[dt][i] |
| |
| gg = C.GG[dt][i] if C.GG_IND_ADJ[dt][i] == C.GG_IND[dt][i] \ |
| else C.GG_ADJ[dt][i] |
| |
| nf = lc3.spec_estimate_noise(dt, C.P_BW[dt][i], |
| xq, nq, C.X_F[dt][i] / gg) |
| ok = ok and nf == C.F_NF[dt][i] |
| |
| return ok |
| |
| def check(): |
| |
| rng = np.random.default_rng(1234) |
| ok = True |
| |
| for dt in range(T.NUM_DT): |
| for sr in range(T.NUM_SRATE): |
| ok = ok and check_estimate_gain(rng, dt, sr) |
| ok = ok and check_quantization(rng, dt, sr) |
| ok = ok and check_compute_nbits(rng, dt, sr) |
| ok = ok and check_adjust_gain(rng, dt, sr) |
| ok = ok and check_unit(rng, dt, sr) |
| ok = ok and check_noise(rng, dt, sr) |
| |
| for dt in range(T.NUM_DT): |
| ok = ok and check_appendix_c(dt) |
| |
| return ok |
| |
| ### ------------------------------------------------------------------------ ### |