# [opus] Coarse energy predictor confusion

Ralph Giles giles at thaumas.net
Sat Jul 3 15:07:49 UTC 2021

Glad you figured it out!
-r

On July 3, 2021 4:51:57 AM PDT, Jake Taylor <yupferris at gmail.com> wrote:
>Hi again all,
>
>I've solved it (in fact the clues showed up in earlier notes, but I
>didn't
>put it together entirely until now)! I was making a crucial conceptual
>mistake. I assumed the output of the predictor was the *prediction*,
>however, this is incorrect - instead it outputs the *residual*
>directly.
>Under that interpretation, everything falls into place. So I no longer
>need
>help with this, and sorry for the noise while I figured it out!
>
>For the curious, once again, notes in pdf/lyx format are attached, as
>well
>as on my dropbox:
>pdf:
>https://www.dropbox.com/s/rzm08055hytjy6c/predictor-confusion-5.pdf?dl=0
><https://www.dropbox.com/s/l4tzyvq1mne8jan/predictor-confusion-4.pdf?dl=0>
>lyx:
>https://www.dropbox.com/s/vj1s3pbm8q914vf/predictor-confusion-5.lyx?dl=0
><https://www.dropbox.com/s/map3slkbnhyctui/predictor-confusion-4.lyx?dl=0>
>and plaintext below.
>
>Thanks again,
>Jake
>
>---
>
>Opus Coarse Energy Predictor Notes (Round 5)
>
>Jake Taylor (yupferris at gmail.com), 3 July 2021
>
>I've solved it - I was simply mistaken about what the output of
>the filters should be. Instead of the filters outputting the
>prediction, they output the residual directly. Under this
>interpretation, everything falls into place.
>
>According to the paper[0]/RFC[1], the 2D z-transform should be:
>
>A(z_{\ell},z_{b})=(1-\alpha
>z_{\ell}^{-1})\cdot\frac{1-z_{b}^{-1}}{1-\beta
>z_{b}^{-1}}
>
>I've simplified the source code (in unquant_coarse_energy in
>quant_bands.c in libopus 1.3.1[2]) to the following C (pseudo)code:
>
>void unquant_coarse_energy(float *e, int bands) {
>  float alpha = /* ... */;
>  float beta = /* ... */;
>  float prev = 0.0f;
>  for (int b = 0; b < bands; b++) {
>    float r = /* read from bitstream */;
>    e[i] = alpha * e[i] + prev + r;
>    prev = prev + (1 - beta) * r;
>  }
>}
>
>The goal is simple: extract difference equations from the code,
>derive the corresponding z-transforms, and then solve the final z
>-transform which should hopefully match what the paper/RFC list.
>
>The code is governed by the following difference equations:
>
>e[\ell,b]=\alpha e[\ell-1,b]+prev[\ell,b-1]+r[\ell,b]
>
>prev[\ell,b]=prev[\ell,b-1]+(1-\beta)r[\ell,b]
>
>The corresponding z-transforms are:
>
>E(z_{\ell},z_{b})=\alpha
>z_{\ell}^{-1}E(z_{\ell},z_{b})+z_{b}^{-1}Prev(z_{\ell},z_{b})+R(z_{\ell},z_{b})
>
>Prev(z_{\ell},z_{b})=z_{b}^{-1}Prev(z_{\ell},z_{b})+(1-\beta)R(z_{\ell},z_{b})
>
>We expect the domain of the predictor to be e (the 2D “energy
>plane”) and the range of the predictor to be r (the final coded
>residual). So, these two z-transforms should be all we need to
>reach the z-transform from the paper/RFC.
>
>First, let's simplify the latter equation by isolating Prev:
>
>Prev(z_{\ell},z_{b})=\frac{1-\beta}{1-z_{b}^{-1}}R(z_{\ell},z_{b})
>
>Now, we can substitute this definition in the first equation,
>which leaves only E and R signals (our expected domain and range,
>respectively):
>
>E(z_{\ell},z_{b})=\alpha
>z_{\ell}^{-1}E(z_{\ell},z_{b})+z_{b}^{-1}\cdot\frac{1-\beta}{1-z_{b}^{-1}}R(z_{\ell},z_{b})+R(z_{\ell},z_{b})
>
>Simplifying this yields:
>
>R(z_{\ell},z_{b})=(1-\alpha
>z_{\ell}^{-1})\cdot\frac{1-z_{b}^{-1}}{1-\beta
>z_{b}^{-1}}E(z_{\ell},z_{b})
>
>Or, equivalently:
>
>A(z_{\ell},z_{b})=(1-\alpha
>z_{\ell}^{-1})\cdot\frac{1-z_{b}^{-1}}{1-\beta
>z_{b}^{-1}}
>
>Just as expected!
>
>[0] https://arxiv.org/abs/1602.04845
>[1] https://datatracker.ietf.org/doc/html/rfc6716#section-4.3.2
>[2] https://opus-codec.org/release/stable/2019/04/12/libopus-1_3_1.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.xiph.org/pipermail/opus/attachments/20210703/c1d0304e/attachment.htm>