xref: /aosp_15_r20/external/libopus/dnn/dred_encoder.c (revision a58d3d2adb790c104798cd88c8a3aff4fa8b82cc)
1*a58d3d2aSXin Li /* Copyright (c) 2022 Amazon
2*a58d3d2aSXin Li    Written by Jan Buethe */
3*a58d3d2aSXin Li /*
4*a58d3d2aSXin Li    Redistribution and use in source and binary forms, with or without
5*a58d3d2aSXin Li    modification, are permitted provided that the following conditions
6*a58d3d2aSXin Li    are met:
7*a58d3d2aSXin Li 
8*a58d3d2aSXin Li    - Redistributions of source code must retain the above copyright
9*a58d3d2aSXin Li    notice, this list of conditions and the following disclaimer.
10*a58d3d2aSXin Li 
11*a58d3d2aSXin Li    - Redistributions in binary form must reproduce the above copyright
12*a58d3d2aSXin Li    notice, this list of conditions and the following disclaimer in the
13*a58d3d2aSXin Li    documentation and/or other materials provided with the distribution.
14*a58d3d2aSXin Li 
15*a58d3d2aSXin Li    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16*a58d3d2aSXin Li    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17*a58d3d2aSXin Li    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18*a58d3d2aSXin Li    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
19*a58d3d2aSXin Li    OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20*a58d3d2aSXin Li    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21*a58d3d2aSXin Li    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22*a58d3d2aSXin Li    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23*a58d3d2aSXin Li    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24*a58d3d2aSXin Li    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25*a58d3d2aSXin Li    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*a58d3d2aSXin Li */
27*a58d3d2aSXin Li 
28*a58d3d2aSXin Li #ifdef HAVE_CONFIG_H
29*a58d3d2aSXin Li #include "config.h"
30*a58d3d2aSXin Li #endif
31*a58d3d2aSXin Li 
32*a58d3d2aSXin Li #include <string.h>
33*a58d3d2aSXin Li 
34*a58d3d2aSXin Li #if 0
35*a58d3d2aSXin Li #include <stdio.h>
36*a58d3d2aSXin Li #include <math.h>
37*a58d3d2aSXin Li #endif
38*a58d3d2aSXin Li 
39*a58d3d2aSXin Li #include "dred_encoder.h"
40*a58d3d2aSXin Li #include "dred_coding.h"
41*a58d3d2aSXin Li #include "celt/entenc.h"
42*a58d3d2aSXin Li 
43*a58d3d2aSXin Li #include "dred_decoder.h"
44*a58d3d2aSXin Li #include "float_cast.h"
45*a58d3d2aSXin Li #include "os_support.h"
46*a58d3d2aSXin Li #include "celt/laplace.h"
47*a58d3d2aSXin Li #include "dred_rdovae_stats_data.h"
48*a58d3d2aSXin Li 
49*a58d3d2aSXin Li 
DRED_rdovae_init_encoder(RDOVAEEncState * enc_state)50*a58d3d2aSXin Li static void DRED_rdovae_init_encoder(RDOVAEEncState *enc_state)
51*a58d3d2aSXin Li {
52*a58d3d2aSXin Li     memset(enc_state, 0, sizeof(*enc_state));
53*a58d3d2aSXin Li }
54*a58d3d2aSXin Li 
dred_encoder_load_model(DREDEnc * enc,const void * data,int len)55*a58d3d2aSXin Li int dred_encoder_load_model(DREDEnc* enc, const void *data, int len)
56*a58d3d2aSXin Li {
57*a58d3d2aSXin Li     WeightArray *list;
58*a58d3d2aSXin Li     int ret;
59*a58d3d2aSXin Li     parse_weights(&list, data, len);
60*a58d3d2aSXin Li     ret = init_rdovaeenc(&enc->model, list);
61*a58d3d2aSXin Li     opus_free(list);
62*a58d3d2aSXin Li     if (ret == 0) {
63*a58d3d2aSXin Li       ret = lpcnet_encoder_load_model(&enc->lpcnet_enc_state, data, len);
64*a58d3d2aSXin Li     }
65*a58d3d2aSXin Li     if (ret == 0) enc->loaded = 1;
66*a58d3d2aSXin Li     return (ret == 0) ? OPUS_OK : OPUS_BAD_ARG;
67*a58d3d2aSXin Li }
68*a58d3d2aSXin Li 
dred_encoder_reset(DREDEnc * enc)69*a58d3d2aSXin Li void dred_encoder_reset(DREDEnc* enc)
70*a58d3d2aSXin Li {
71*a58d3d2aSXin Li     OPUS_CLEAR((char*)&enc->DREDENC_RESET_START,
72*a58d3d2aSXin Li               sizeof(DREDEnc)-
73*a58d3d2aSXin Li               ((char*)&enc->DREDENC_RESET_START - (char*)enc));
74*a58d3d2aSXin Li     enc->input_buffer_fill = DRED_SILK_ENCODER_DELAY;
75*a58d3d2aSXin Li     lpcnet_encoder_init(&enc->lpcnet_enc_state);
76*a58d3d2aSXin Li     DRED_rdovae_init_encoder(&enc->rdovae_enc);
77*a58d3d2aSXin Li }
78*a58d3d2aSXin Li 
dred_encoder_init(DREDEnc * enc,opus_int32 Fs,int channels)79*a58d3d2aSXin Li void dred_encoder_init(DREDEnc* enc, opus_int32 Fs, int channels)
80*a58d3d2aSXin Li {
81*a58d3d2aSXin Li     enc->Fs = Fs;
82*a58d3d2aSXin Li     enc->channels = channels;
83*a58d3d2aSXin Li     enc->loaded = 0;
84*a58d3d2aSXin Li #ifndef USE_WEIGHTS_FILE
85*a58d3d2aSXin Li     if (init_rdovaeenc(&enc->model, rdovaeenc_arrays) == 0) enc->loaded = 1;
86*a58d3d2aSXin Li #endif
87*a58d3d2aSXin Li     dred_encoder_reset(enc);
88*a58d3d2aSXin Li }
89*a58d3d2aSXin Li 
dred_process_frame(DREDEnc * enc,int arch)90*a58d3d2aSXin Li static void dred_process_frame(DREDEnc *enc, int arch)
91*a58d3d2aSXin Li {
92*a58d3d2aSXin Li     float feature_buffer[2 * 36];
93*a58d3d2aSXin Li     float input_buffer[2*DRED_NUM_FEATURES] = {0};
94*a58d3d2aSXin Li 
95*a58d3d2aSXin Li     celt_assert(enc->loaded);
96*a58d3d2aSXin Li     /* shift latents buffer */
97*a58d3d2aSXin Li     OPUS_MOVE(enc->latents_buffer + DRED_LATENT_DIM, enc->latents_buffer, (DRED_MAX_FRAMES - 1) * DRED_LATENT_DIM);
98*a58d3d2aSXin Li     OPUS_MOVE(enc->state_buffer + DRED_STATE_DIM, enc->state_buffer, (DRED_MAX_FRAMES - 1) * DRED_STATE_DIM);
99*a58d3d2aSXin Li 
100*a58d3d2aSXin Li     /* calculate LPCNet features */
101*a58d3d2aSXin Li     lpcnet_compute_single_frame_features_float(&enc->lpcnet_enc_state, enc->input_buffer, feature_buffer, arch);
102*a58d3d2aSXin Li     lpcnet_compute_single_frame_features_float(&enc->lpcnet_enc_state, enc->input_buffer + DRED_FRAME_SIZE, feature_buffer + 36, arch);
103*a58d3d2aSXin Li 
104*a58d3d2aSXin Li     /* prepare input buffer (discard LPC coefficients) */
105*a58d3d2aSXin Li     OPUS_COPY(input_buffer, feature_buffer, DRED_NUM_FEATURES);
106*a58d3d2aSXin Li     OPUS_COPY(input_buffer + DRED_NUM_FEATURES, feature_buffer + 36, DRED_NUM_FEATURES);
107*a58d3d2aSXin Li 
108*a58d3d2aSXin Li     /* run RDOVAE encoder */
109*a58d3d2aSXin Li     dred_rdovae_encode_dframe(&enc->rdovae_enc, &enc->model, enc->latents_buffer, enc->state_buffer, input_buffer, arch);
110*a58d3d2aSXin Li     enc->latents_buffer_fill = IMIN(enc->latents_buffer_fill+1, DRED_NUM_REDUNDANCY_FRAMES);
111*a58d3d2aSXin Li }
112*a58d3d2aSXin Li 
filter_df2t(const float * in,float * out,int len,float b0,const float * b,const float * a,int order,float * mem)113*a58d3d2aSXin Li void filter_df2t(const float *in, float *out, int len, float b0, const float *b, const float *a, int order, float *mem)
114*a58d3d2aSXin Li {
115*a58d3d2aSXin Li     int i;
116*a58d3d2aSXin Li     for (i=0;i<len;i++) {
117*a58d3d2aSXin Li         int j;
118*a58d3d2aSXin Li         float xi, yi, nyi;
119*a58d3d2aSXin Li         xi = in[i];
120*a58d3d2aSXin Li         yi = xi*b0 + mem[0];
121*a58d3d2aSXin Li         nyi = -yi;
122*a58d3d2aSXin Li         for (j=0;j<order;j++)
123*a58d3d2aSXin Li         {
124*a58d3d2aSXin Li            mem[j] = mem[j+1] + b[j]*xi + a[j]*nyi;
125*a58d3d2aSXin Li         }
126*a58d3d2aSXin Li         out[i] = yi;
127*a58d3d2aSXin Li         /*fprintf(stdout, "%f\n", out[i]);*/
128*a58d3d2aSXin Li     }
129*a58d3d2aSXin Li }
130*a58d3d2aSXin Li 
131*a58d3d2aSXin Li #define MAX_DOWNMIX_BUFFER (960*2)
dred_convert_to_16k(DREDEnc * enc,const float * in,int in_len,float * out,int out_len)132*a58d3d2aSXin Li static void dred_convert_to_16k(DREDEnc *enc, const float *in, int in_len, float *out, int out_len)
133*a58d3d2aSXin Li {
134*a58d3d2aSXin Li     float downmix[MAX_DOWNMIX_BUFFER];
135*a58d3d2aSXin Li     int i;
136*a58d3d2aSXin Li     int up;
137*a58d3d2aSXin Li     celt_assert(enc->channels*in_len <= MAX_DOWNMIX_BUFFER);
138*a58d3d2aSXin Li     celt_assert(in_len * (opus_int32)16000 == out_len * enc->Fs);
139*a58d3d2aSXin Li     switch(enc->Fs) {
140*a58d3d2aSXin Li         case 8000:
141*a58d3d2aSXin Li             up = 2;
142*a58d3d2aSXin Li             break;
143*a58d3d2aSXin Li         case 12000:
144*a58d3d2aSXin Li             up = 4;
145*a58d3d2aSXin Li             break;
146*a58d3d2aSXin Li         case 16000:
147*a58d3d2aSXin Li             up = 1;
148*a58d3d2aSXin Li             break;
149*a58d3d2aSXin Li         case 24000:
150*a58d3d2aSXin Li             up = 2;
151*a58d3d2aSXin Li             break;
152*a58d3d2aSXin Li         case 48000:
153*a58d3d2aSXin Li             up = 1;
154*a58d3d2aSXin Li             break;
155*a58d3d2aSXin Li         default:
156*a58d3d2aSXin Li             celt_assert(0);
157*a58d3d2aSXin Li     }
158*a58d3d2aSXin Li     OPUS_CLEAR(downmix, up*in_len);
159*a58d3d2aSXin Li     if (enc->channels == 1) {
160*a58d3d2aSXin Li         for (i=0;i<in_len;i++) downmix[up*i] = FLOAT2INT16(up*in[i]);
161*a58d3d2aSXin Li     } else {
162*a58d3d2aSXin Li         for (i=0;i<in_len;i++) downmix[up*i] = FLOAT2INT16(.5*up*(in[2*i]+in[2*i+1]));
163*a58d3d2aSXin Li     }
164*a58d3d2aSXin Li     if (enc->Fs == 16000) {
165*a58d3d2aSXin Li         OPUS_COPY(out, downmix, out_len);
166*a58d3d2aSXin Li     } else if (enc->Fs == 48000 || enc->Fs == 24000) {
167*a58d3d2aSXin Li         /* ellip(7, .2, 70, 7750/24000) */
168*a58d3d2aSXin Li 
169*a58d3d2aSXin Li         static const float filter_b[8] = { 0.005873358047f,  0.012980854831f, 0.014531340042f,  0.014531340042f, 0.012980854831f,  0.005873358047f, 0.004523418224f, 0.f};
170*a58d3d2aSXin Li         static const float filter_a[8] = {-3.878718597768f, 7.748834257468f, -9.653651699533f, 8.007342726666f, -4.379450178552f, 1.463182111810f, -0.231720677804f, 0.f};
171*a58d3d2aSXin Li         float b0 = 0.004523418224f;
172*a58d3d2aSXin Li         filter_df2t(downmix, downmix, up*in_len, b0, filter_b, filter_a, RESAMPLING_ORDER, enc->resample_mem);
173*a58d3d2aSXin Li         for (i=0;i<out_len;i++) out[i] = downmix[3*i];
174*a58d3d2aSXin Li     } else if (enc->Fs == 12000) {
175*a58d3d2aSXin Li         /* ellip(7, .2, 70, 7750/24000) */
176*a58d3d2aSXin Li         static const float filter_b[8] = {-0.001017101081f,  0.003673127243f,   0.001009165267f,  0.001009165267f,  0.003673127243f, -0.001017101081f,  0.002033596776f, 0.f};
177*a58d3d2aSXin Li         static const float filter_a[8] = {-4.930414411612f, 11.291643096504f, -15.322037343815f, 13.216403930898f, -7.220409219553f,  2.310550142771f, -0.334338618782f, 0.f};
178*a58d3d2aSXin Li         float b0 = 0.002033596776f;
179*a58d3d2aSXin Li         filter_df2t(downmix, downmix, up*in_len, b0, filter_b, filter_a, RESAMPLING_ORDER, enc->resample_mem);
180*a58d3d2aSXin Li         for (i=0;i<out_len;i++) out[i] = downmix[3*i];
181*a58d3d2aSXin Li     } else if (enc->Fs == 8000) {
182*a58d3d2aSXin Li         /* ellip(7, .2, 70, 3900/8000) */
183*a58d3d2aSXin Li         static const float filter_b[8] = { 0.081670120929f, 0.180401598565f,  0.259391051971f, 0.259391051971f,  0.180401598565f, 0.081670120929f,  0.020109185709f, 0.f};
184*a58d3d2aSXin Li         static const float filter_a[8] = {-1.393651933659f, 2.609789872676f, -2.403541968806f, 2.056814957331f, -1.148908574570f, 0.473001413788f, -0.110359852412f, 0.f};
185*a58d3d2aSXin Li         float b0 = 0.020109185709f;
186*a58d3d2aSXin Li         filter_df2t(downmix, out, out_len, b0, filter_b, filter_a, RESAMPLING_ORDER, enc->resample_mem);
187*a58d3d2aSXin Li     } else {
188*a58d3d2aSXin Li         celt_assert(0);
189*a58d3d2aSXin Li     }
190*a58d3d2aSXin Li }
191*a58d3d2aSXin Li 
dred_compute_latents(DREDEnc * enc,const float * pcm,int frame_size,int extra_delay,int arch)192*a58d3d2aSXin Li void dred_compute_latents(DREDEnc *enc, const float *pcm, int frame_size, int extra_delay, int arch)
193*a58d3d2aSXin Li {
194*a58d3d2aSXin Li     int curr_offset16k;
195*a58d3d2aSXin Li     int frame_size16k = frame_size * 16000 / enc->Fs;
196*a58d3d2aSXin Li     celt_assert(enc->loaded);
197*a58d3d2aSXin Li     curr_offset16k = 40 + extra_delay*16000/enc->Fs - enc->input_buffer_fill;
198*a58d3d2aSXin Li     enc->dred_offset = (int)floor((curr_offset16k+20.f)/40.f);
199*a58d3d2aSXin Li     enc->latent_offset = 0;
200*a58d3d2aSXin Li     while (frame_size16k > 0) {
201*a58d3d2aSXin Li         int process_size16k;
202*a58d3d2aSXin Li         int process_size;
203*a58d3d2aSXin Li         process_size16k = IMIN(2*DRED_FRAME_SIZE, frame_size16k);
204*a58d3d2aSXin Li         process_size = process_size16k * enc->Fs / 16000;
205*a58d3d2aSXin Li         dred_convert_to_16k(enc, pcm, process_size, &enc->input_buffer[enc->input_buffer_fill], process_size16k);
206*a58d3d2aSXin Li         enc->input_buffer_fill += process_size16k;
207*a58d3d2aSXin Li         if (enc->input_buffer_fill >= 2*DRED_FRAME_SIZE)
208*a58d3d2aSXin Li         {
209*a58d3d2aSXin Li             curr_offset16k += 320;
210*a58d3d2aSXin Li             dred_process_frame(enc, arch);
211*a58d3d2aSXin Li             enc->input_buffer_fill -= 2*DRED_FRAME_SIZE;
212*a58d3d2aSXin Li             OPUS_MOVE(&enc->input_buffer[0], &enc->input_buffer[2*DRED_FRAME_SIZE], enc->input_buffer_fill);
213*a58d3d2aSXin Li             /* 15 ms (6*2.5 ms) is the ideal offset for DRED because it corresponds to our vocoder look-ahead. */
214*a58d3d2aSXin Li             if (enc->dred_offset < 6) {
215*a58d3d2aSXin Li                 enc->dred_offset += 8;
216*a58d3d2aSXin Li             } else {
217*a58d3d2aSXin Li                 enc->latent_offset++;
218*a58d3d2aSXin Li             }
219*a58d3d2aSXin Li         }
220*a58d3d2aSXin Li 
221*a58d3d2aSXin Li         pcm += process_size;
222*a58d3d2aSXin Li         frame_size16k -= process_size16k;
223*a58d3d2aSXin Li     }
224*a58d3d2aSXin Li }
225*a58d3d2aSXin Li 
dred_encode_latents(ec_enc * enc,const float * x,const opus_uint8 * scale,const opus_uint8 * dzone,const opus_uint8 * r,const opus_uint8 * p0,int dim,int arch)226*a58d3d2aSXin Li static void dred_encode_latents(ec_enc *enc, const float *x, const opus_uint8 *scale, const opus_uint8 *dzone, const opus_uint8 *r, const opus_uint8 *p0, int dim, int arch) {
227*a58d3d2aSXin Li     int i;
228*a58d3d2aSXin Li     int q[IMAX(DRED_LATENT_DIM,DRED_STATE_DIM)];
229*a58d3d2aSXin Li     float xq[IMAX(DRED_LATENT_DIM,DRED_STATE_DIM)];
230*a58d3d2aSXin Li     float delta[IMAX(DRED_LATENT_DIM,DRED_STATE_DIM)];
231*a58d3d2aSXin Li     float deadzone[IMAX(DRED_LATENT_DIM,DRED_STATE_DIM)];
232*a58d3d2aSXin Li     float eps = .1f;
233*a58d3d2aSXin Li     /* This is split into multiple loops (with temporary arrays) so that the compiler
234*a58d3d2aSXin Li        can vectorize all of it, and so we can call the vector tanh(). */
235*a58d3d2aSXin Li     for (i=0;i<dim;i++) {
236*a58d3d2aSXin Li         delta[i] = dzone[i]*(1.f/256.f);
237*a58d3d2aSXin Li         xq[i] = x[i]*scale[i]*(1.f/256.f);
238*a58d3d2aSXin Li         deadzone[i] = xq[i]/(delta[i]+eps);
239*a58d3d2aSXin Li     }
240*a58d3d2aSXin Li     compute_activation(deadzone, deadzone, dim, ACTIVATION_TANH, arch);
241*a58d3d2aSXin Li     for (i=0;i<dim;i++) {
242*a58d3d2aSXin Li         xq[i] = xq[i] - delta[i]*deadzone[i];
243*a58d3d2aSXin Li         q[i] = (int)floor(.5f+xq[i]);
244*a58d3d2aSXin Li     }
245*a58d3d2aSXin Li     for (i=0;i<dim;i++) {
246*a58d3d2aSXin Li         /* Make the impossible actually impossible. */
247*a58d3d2aSXin Li         if (r[i] == 0 || p0[i] == 255) q[i] = 0;
248*a58d3d2aSXin Li         else ec_laplace_encode_p0(enc, q[i], p0[i]<<7, r[i]<<7);
249*a58d3d2aSXin Li     }
250*a58d3d2aSXin Li }
251*a58d3d2aSXin Li 
dred_voice_active(const unsigned char * activity_mem,int offset)252*a58d3d2aSXin Li static int dred_voice_active(const unsigned char *activity_mem, int offset) {
253*a58d3d2aSXin Li     int i;
254*a58d3d2aSXin Li     for (i=0;i<16;i++) {
255*a58d3d2aSXin Li         if (activity_mem[8*offset + i] == 1) return 1;
256*a58d3d2aSXin Li     }
257*a58d3d2aSXin Li     return 0;
258*a58d3d2aSXin Li }
259*a58d3d2aSXin Li 
dred_encode_silk_frame(DREDEnc * enc,unsigned char * buf,int max_chunks,int max_bytes,int q0,int dQ,int qmax,unsigned char * activity_mem,int arch)260*a58d3d2aSXin Li int dred_encode_silk_frame(DREDEnc *enc, unsigned char *buf, int max_chunks, int max_bytes, int q0, int dQ, int qmax, unsigned char *activity_mem, int arch) {
261*a58d3d2aSXin Li     ec_enc ec_encoder;
262*a58d3d2aSXin Li 
263*a58d3d2aSXin Li     int q_level;
264*a58d3d2aSXin Li     int i;
265*a58d3d2aSXin Li     int offset;
266*a58d3d2aSXin Li     int ec_buffer_fill;
267*a58d3d2aSXin Li     int state_qoffset;
268*a58d3d2aSXin Li     ec_enc ec_bak;
269*a58d3d2aSXin Li     int prev_active=0;
270*a58d3d2aSXin Li     int latent_offset;
271*a58d3d2aSXin Li     int extra_dred_offset=0;
272*a58d3d2aSXin Li     int dred_encoded=0;
273*a58d3d2aSXin Li     int delayed_dred=0;
274*a58d3d2aSXin Li     int total_offset;
275*a58d3d2aSXin Li 
276*a58d3d2aSXin Li     latent_offset = enc->latent_offset;
277*a58d3d2aSXin Li     /* Delaying new DRED data when just out of silence because we already have the
278*a58d3d2aSXin Li        main Opus payload for that frame. */
279*a58d3d2aSXin Li     if (activity_mem[0] && enc->last_extra_dred_offset>0) {
280*a58d3d2aSXin Li         latent_offset = enc->last_extra_dred_offset;
281*a58d3d2aSXin Li         delayed_dred = 1;
282*a58d3d2aSXin Li         enc->last_extra_dred_offset = 0;
283*a58d3d2aSXin Li     }
284*a58d3d2aSXin Li     while (latent_offset < enc->latents_buffer_fill && !dred_voice_active(activity_mem, latent_offset)) {
285*a58d3d2aSXin Li        latent_offset++;
286*a58d3d2aSXin Li        extra_dred_offset++;
287*a58d3d2aSXin Li     }
288*a58d3d2aSXin Li     if (!delayed_dred) enc->last_extra_dred_offset = extra_dred_offset;
289*a58d3d2aSXin Li 
290*a58d3d2aSXin Li     /* entropy coding of state and latents */
291*a58d3d2aSXin Li     ec_enc_init(&ec_encoder, buf, max_bytes);
292*a58d3d2aSXin Li     ec_enc_uint(&ec_encoder, q0, 16);
293*a58d3d2aSXin Li     ec_enc_uint(&ec_encoder, dQ, 8);
294*a58d3d2aSXin Li     total_offset = 16 - (enc->dred_offset - extra_dred_offset*8);
295*a58d3d2aSXin Li     celt_assert(total_offset>=0);
296*a58d3d2aSXin Li     if (total_offset > 31) {
297*a58d3d2aSXin Li        ec_enc_uint(&ec_encoder, 1, 2);
298*a58d3d2aSXin Li        ec_enc_uint(&ec_encoder, total_offset>>5, 256);
299*a58d3d2aSXin Li        ec_enc_uint(&ec_encoder, total_offset&31, 32);
300*a58d3d2aSXin Li     } else {
301*a58d3d2aSXin Li        ec_enc_uint(&ec_encoder, 0, 2);
302*a58d3d2aSXin Li        ec_enc_uint(&ec_encoder, total_offset, 32);
303*a58d3d2aSXin Li     }
304*a58d3d2aSXin Li     celt_assert(qmax >= q0);
305*a58d3d2aSXin Li     if (q0 < 14 && dQ > 0) {
306*a58d3d2aSXin Li       int nvals;
307*a58d3d2aSXin Li       /* If you want to use qmax == q0, you should have set dQ = 0. */
308*a58d3d2aSXin Li       celt_assert(qmax > q0);
309*a58d3d2aSXin Li       nvals = 15 - (q0 + 1);
310*a58d3d2aSXin Li       ec_encode(&ec_encoder, qmax >= 15 ? 0 : nvals + qmax - (q0 + 1),
311*a58d3d2aSXin Li         qmax >= 15 ? nvals : nvals + qmax - q0, 2*nvals);
312*a58d3d2aSXin Li     }
313*a58d3d2aSXin Li     state_qoffset = q0*DRED_STATE_DIM;
314*a58d3d2aSXin Li     dred_encode_latents(
315*a58d3d2aSXin Li         &ec_encoder,
316*a58d3d2aSXin Li         &enc->state_buffer[latent_offset*DRED_STATE_DIM],
317*a58d3d2aSXin Li         dred_state_quant_scales_q8 + state_qoffset,
318*a58d3d2aSXin Li         dred_state_dead_zone_q8 + state_qoffset,
319*a58d3d2aSXin Li         dred_state_r_q8 + state_qoffset,
320*a58d3d2aSXin Li         dred_state_p0_q8 + state_qoffset,
321*a58d3d2aSXin Li         DRED_STATE_DIM,
322*a58d3d2aSXin Li         arch);
323*a58d3d2aSXin Li     if (ec_tell(&ec_encoder) > 8*max_bytes) {
324*a58d3d2aSXin Li       return 0;
325*a58d3d2aSXin Li     }
326*a58d3d2aSXin Li     ec_bak = ec_encoder;
327*a58d3d2aSXin Li     for (i = 0; i < IMIN(2*max_chunks, enc->latents_buffer_fill-latent_offset-1); i += 2)
328*a58d3d2aSXin Li     {
329*a58d3d2aSXin Li         int active;
330*a58d3d2aSXin Li         q_level = compute_quantizer(q0, dQ, qmax, i/2);
331*a58d3d2aSXin Li         offset = q_level * DRED_LATENT_DIM;
332*a58d3d2aSXin Li 
333*a58d3d2aSXin Li         dred_encode_latents(
334*a58d3d2aSXin Li             &ec_encoder,
335*a58d3d2aSXin Li             enc->latents_buffer + (i+latent_offset) * DRED_LATENT_DIM,
336*a58d3d2aSXin Li             dred_latent_quant_scales_q8 + offset,
337*a58d3d2aSXin Li             dred_latent_dead_zone_q8 + offset,
338*a58d3d2aSXin Li             dred_latent_r_q8 + offset,
339*a58d3d2aSXin Li             dred_latent_p0_q8 + offset,
340*a58d3d2aSXin Li             DRED_LATENT_DIM,
341*a58d3d2aSXin Li             arch
342*a58d3d2aSXin Li         );
343*a58d3d2aSXin Li         if (ec_tell(&ec_encoder) > 8*max_bytes) {
344*a58d3d2aSXin Li           /* If we haven't been able to code one chunk, give up on DRED completely. */
345*a58d3d2aSXin Li           if (i==0) return 0;
346*a58d3d2aSXin Li           break;
347*a58d3d2aSXin Li         }
348*a58d3d2aSXin Li         active = dred_voice_active(activity_mem, i+latent_offset);
349*a58d3d2aSXin Li         if (active || prev_active) {
350*a58d3d2aSXin Li            ec_bak = ec_encoder;
351*a58d3d2aSXin Li            dred_encoded = i+2;
352*a58d3d2aSXin Li         }
353*a58d3d2aSXin Li         prev_active = active;
354*a58d3d2aSXin Li     }
355*a58d3d2aSXin Li     /* Avoid sending empty DRED packets. */
356*a58d3d2aSXin Li     if (dred_encoded==0 || (dred_encoded<=2 && extra_dred_offset)) return 0;
357*a58d3d2aSXin Li     ec_encoder = ec_bak;
358*a58d3d2aSXin Li 
359*a58d3d2aSXin Li     ec_buffer_fill = (ec_tell(&ec_encoder)+7)/8;
360*a58d3d2aSXin Li     ec_enc_shrink(&ec_encoder, ec_buffer_fill);
361*a58d3d2aSXin Li     ec_enc_done(&ec_encoder);
362*a58d3d2aSXin Li     return ec_buffer_fill;
363*a58d3d2aSXin Li }
364