g729-sys 0.1.2

Rust implementation of G.729 codec
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
use crate::g729::basic_operations::*;
use crate::g729::ld8k::*;
// use crate::g729::fixed_point_math::*;
use crate::g729::adaptative_codebook_search::*;
use crate::g729::compute_adaptative_codebook_gain::*;
use crate::g729::compute_lp::*;
use crate::g729::compute_weighted_speech::*;
use crate::g729::find_open_loop_pitch_delay::*;
use crate::g729::fixed_codebook_search::*;
use crate::g729::gain_quantization::*;
use crate::g729::interpolate_q_lsp::*;
use crate::g729::lp2lsp_conversion::*;
use crate::g729::lp_synthesis_filter::lp_synthesis_filter;
use crate::g729::lsp_quantization::*;
use crate::g729::pre_processing::*;
use crate::g729::q_lsp_2_lp::*;
use crate::g729::utils::*;

pub struct EncoderChannelContext {
    /* buffers used in decoder bloc */
    /* Signal buffer mapping : 240 word16_t length */
    pub signal_buffer: [Word16; L_LP_ANALYSIS_WINDOW],

    // Indices for signal buffer
    // signalLastInputFrame index = L_LP_ANALYSIS_WINDOW - L_FRAME
    // signalCurrentFrame index = L_LP_ANALYSIS_WINDOW - L_SUBFRAME - L_FRAME
    pub previous_lsp_coefficients: [Word16; NB_LSP_COEFF],
    pub previous_q_lsp_coefficients: [Word16; NB_LSP_COEFF],

    pub weighted_input_signal: [Word16; MAXIMUM_INT_PITCH_DELAY + L_FRAME],
    pub excitation_vector: [Word16; L_PAST_EXCITATION + L_FRAME],

    pub target_signal: [Word16; NB_LSP_COEFF + L_SUBFRAME],

    pub last_quantized_adaptative_codebook_gain: Word16,

    /*** buffer used in preProcessing ***/
    pub pre_processing_state: PreProcessingState,

    /*** buffer used in LSPQuantization ***/
    pub previous_q_lsf: [[Word16; NB_LSP_COEFF]; MA_MAX_K],

    /*** buffer used in gainQuantization ***/
    pub previous_gain_prediction_error: [Word16; 4],
    // VAD/DTX context placeholders
    // pub vad_channel_context: Option<VADChannelContext>,
    // pub dtx_channel_context: Option<DTXChannelContext>,
}

const PREVIOUS_LSP_INITIAL_VALUES: [Word16; NB_LSP_COEFF] = [
    30000, 26000, 21000, 15000, 8000, 0, -8000, -15000, -21000, -26000,
];

// Indices
const SIGNAL_LAST_INPUT_FRAME_IDX: usize = L_LP_ANALYSIS_WINDOW - L_FRAME;
const SIGNAL_CURRENT_FRAME_IDX: usize = L_LP_ANALYSIS_WINDOW - L_SUBFRAME - L_FRAME;

impl EncoderChannelContext {
    pub fn new(_enable_vad: bool) -> Self {
        let mut ctx = EncoderChannelContext {
            signal_buffer: [0; L_LP_ANALYSIS_WINDOW],
            previous_lsp_coefficients: PREVIOUS_LSP_INITIAL_VALUES,
            previous_q_lsp_coefficients: PREVIOUS_LSP_INITIAL_VALUES,
            weighted_input_signal: [0; MAXIMUM_INT_PITCH_DELAY + L_FRAME],
            excitation_vector: [0; L_PAST_EXCITATION + L_FRAME],
            target_signal: [0; NB_LSP_COEFF + L_SUBFRAME],
            last_quantized_adaptative_codebook_gain: O2_IN_Q14,
            pre_processing_state: PreProcessingState::new(),
            previous_q_lsf: [[0; NB_LSP_COEFF]; MA_MAX_K],
            previous_gain_prediction_error: [0; 4],
        };

        // initPreProcessing
        // Already done by PreProcessingState::new()

        // initLSPQuantization
        init_lsp_quantization(&mut ctx.previous_q_lsf);

        // initGainQuantization
        // In C: initGainQuantization sets previousGainPredictionError to -14dB in Q10.
        // -14dB in Q10 is -14336? No.
        // Let's check initGainQuantization in gainQuantization.c
        // It sets it to -14336.
        for i in 0..4 {
            ctx.previous_gain_prediction_error[i] = -14336;
        }

        ctx
    }

    pub fn encode(
        &mut self,
        input_frame: &[Word16],
        bit_stream: &mut [u8],
        bit_stream_length: &mut u8,
    ) {
        let mut parameters = [0u16; NB_PARAMETERS];

        // internal buffers
        let mut lp_coefficients = [0i16; NB_LSP_COEFF];
        let _lsf_coefficients = [0i16; NB_LSP_COEFF];
        let mut q_lp_coefficients = [0i16; 2 * NB_LSP_COEFF];
        let mut weighted_q_lp_coefficients = [0i16; 2 * NB_LSP_COEFF];
        let mut lsp_coefficients = [0i16; NB_LSP_COEFF];
        let mut q_lsp_coefficients = [0i16; NB_LSP_COEFF];
        let mut interpolated_q_lsp = [0i16; NB_LSP_COEFF];

        let mut parameters_index = 4;
        let mut impulse_response_input = [0i16; L_SUBFRAME];

        // VAD placeholders
        let mut reflection_coefficients = [0i32; NB_LSP_COEFF];
        let mut auto_correlation_coefficients = [0i32; NB_LSP_COEFF + 3];
        let mut no_lag_auto_correlation_coefficients = [0i32; NB_LSP_COEFF + 3];
        let mut auto_correlation_coefficients_scale = 0i8;

        // Pre-processing
        // preProcessing(encoderChannelContext, inputFrame, encoderChannelContext->signalLastInputFrame);

        self.pre_processing_state.pre_processing(
            input_frame,
            &mut self.signal_buffer[SIGNAL_LAST_INPUT_FRAME_IDX..],
        );

        // Compute LP
        // computeLP(encoderChannelContext->signalBuffer, LPCoefficients, reflectionCoefficients, ...);
        // signalBuffer is used as input.
        compute_lp(
            &self.signal_buffer,
            &mut lp_coefficients,
            &mut reflection_coefficients,
            &mut auto_correlation_coefficients,
            &mut no_lag_auto_correlation_coefficients,
            &mut auto_correlation_coefficients_scale,
            NB_LSP_COEFF + 1, // VAD disabled for now
        );

        // LP to LSP
        if !lp2lsp_conversion(&lp_coefficients, &mut lsp_coefficients) {
            lsp_coefficients.copy_from_slice(&self.previous_lsp_coefficients);
        }

        // VAD check would go here.
        *bit_stream_length = 10;

        // LSP Quantization
        lsp_quantization(
            &mut self.previous_q_lsf,
            &mut lsp_coefficients,
            &mut q_lsp_coefficients,
            &mut parameters,
        );
        // Interpolate qLSP
        interpolate_q_lsp(
            &self.previous_q_lsp_coefficients,
            &q_lsp_coefficients,
            &mut interpolated_q_lsp,
        );

        // Update previous qLSP
        self.previous_q_lsp_coefficients
            .copy_from_slice(&q_lsp_coefficients);

        // qLSP to LP
        // first subframe
        q_lsp_2_lp(&interpolated_q_lsp, &mut q_lp_coefficients[0..NB_LSP_COEFF]);
        // second subframe
        q_lsp_2_lp(&q_lsp_coefficients, &mut q_lp_coefficients[NB_LSP_COEFF..]);

        // Compute weighted qLP
        for i in 0..2 * NB_LSP_COEFF {
            // weightedqLPCoefficients[i] = qLPCoefficients[i]*Gamma^(i%10+1)
            // We can use a helper or just loop.
            // The C code unrolls it.
            let gamma_idx = i % NB_LSP_COEFF;
            let gamma = match gamma_idx {
                0 => GAMMA_E1,
                1 => GAMMA_E2,
                2 => GAMMA_E3,
                3 => GAMMA_E4,
                4 => GAMMA_E5,
                5 => GAMMA_E6,
                6 => GAMMA_E7,
                7 => GAMMA_E8,
                8 => GAMMA_E9,
                9 => GAMMA_E10,
                _ => 0,
            };
            weighted_q_lp_coefficients[i] = mult16_16_p15(q_lp_coefficients[i], gamma) as Word16;
        }

        // Compute weighted speech
        // computeWeightedSpeech(encoderChannelContext->signalCurrentFrame, qLPCoefficients, weightedqLPCoefficients, &(encoderChannelContext->weightedInputSignal[MAXIMUM_INT_PITCH_DELAY]), &(encoderChannelContext->excitationVector[L_PAST_EXCITATION]));
        // signalCurrentFrame is at SIGNAL_CURRENT_FRAME_IDX.
        // weightedInputSignal output starts at MAXIMUM_INT_PITCH_DELAY.
        // excitationVector output starts at L_PAST_EXCITATION.

        compute_weighted_speech(
            &self.signal_buffer[SIGNAL_CURRENT_FRAME_IDX - NB_LSP_COEFF..],
            &q_lp_coefficients,
            &weighted_q_lp_coefficients,
            &mut self.weighted_input_signal[MAXIMUM_INT_PITCH_DELAY - NB_LSP_COEFF..],
            &mut self.excitation_vector[L_PAST_EXCITATION..],
        );

        // Open loop pitch search
        let open_loop_pitch_delay = find_open_loop_pitch_delay(&self.weighted_input_signal);
        let mut int_pitch_delay_min = open_loop_pitch_delay as i16 - 3;
        if int_pitch_delay_min < 20 {
            int_pitch_delay_min = 20;
        }
        let mut int_pitch_delay_max = int_pitch_delay_min + 6;
        if int_pitch_delay_max > MAXIMUM_INT_PITCH_DELAY as i16 {
            int_pitch_delay_max = MAXIMUM_INT_PITCH_DELAY as i16;
            int_pitch_delay_min = MAXIMUM_INT_PITCH_DELAY as i16 - 6;
        }

        // Subframe loop
        impulse_response_input[0] = ONE_IN_Q12 as Word16;
        for i in 1..L_SUBFRAME {
            impulse_response_input[i] = 0;
        }

        let mut lp_coefficients_index = 0;

        for subframe_index in (0..L_FRAME).step_by(L_SUBFRAME) {
            let mut int_pitch_delay: i16 = 0;
            let mut frac_pitch_delay: i16 = 0;
            let adaptative_codebook_gain: Word16;

            let mut impulse_response_buffer = [0i16; NB_LSP_COEFF + L_SUBFRAME];
            let mut filtered_adaptative_codebook_vector = [0i16; NB_LSP_COEFF + L_SUBFRAME];
            let mut gain_quantization_xy: Word64 = 0;
            let mut gain_quantization_yy: Word64 = 0;
            let mut fixed_codebook_vector = [0i16; L_SUBFRAME];
            let mut convolved_fixed_codebook_vector = [0i16; L_SUBFRAME];
            let mut quantized_adaptative_codebook_gain: Word16 = 0;
            let mut quantized_fixed_codebook_gain: Word16 = 0;

            // Compute impulse response
            // synthesisFilter(impulseResponseInput, &(weightedqLPCoefficients[LPCoefficientsIndex]), &(impulseResponseBuffer[NB_LSP_COEFF]));
            // In Rust, synthesis_filter takes the full buffer and writes to the end.
            lp_synthesis_filter(
                &impulse_response_input,
                &weighted_q_lp_coefficients[lp_coefficients_index..],
                &mut impulse_response_buffer,
            );

            // Compute target signal
            // synthesisFilter( &(encoderChannelContext->excitationVector[L_PAST_EXCITATION+subframeIndex]), &(weightedqLPCoefficients[LPCoefficientsIndex]), &(encoderChannelContext->targetSignal[NB_LSP_COEFF]));
            lp_synthesis_filter(
                &self.excitation_vector[L_PAST_EXCITATION + subframe_index
                    ..L_PAST_EXCITATION + subframe_index + L_SUBFRAME],
                &weighted_q_lp_coefficients[lp_coefficients_index..],
                &mut self.target_signal,
            );

            // Adaptative Codebook Search
            let mut param_pitch_delay: u16 = 0;
            adaptative_codebook_search(
                &mut self.excitation_vector,
                L_PAST_EXCITATION + subframe_index,
                &mut int_pitch_delay_min,
                &mut int_pitch_delay_max,
                &impulse_response_buffer[NB_LSP_COEFF..],
                &self.target_signal[NB_LSP_COEFF..],
                &mut int_pitch_delay,
                &mut frac_pitch_delay,
                &mut param_pitch_delay,
                subframe_index as u16,
            );
            parameters[parameters_index] = param_pitch_delay;

            // Compute adaptative codebook gain
            // synthesisFilter(&(encoderChannelContext->excitationVector[L_PAST_EXCITATION + subframeIndex]), &(weightedqLPCoefficients[LPCoefficientsIndex]), &(filteredAdaptativeCodebookVector[NB_LSP_COEFF]));
            lp_synthesis_filter(
                &self.excitation_vector[L_PAST_EXCITATION + subframe_index
                    ..L_PAST_EXCITATION + subframe_index + L_SUBFRAME],
                &weighted_q_lp_coefficients[lp_coefficients_index..],
                &mut filtered_adaptative_codebook_vector,
            );

            adaptative_codebook_gain = compute_adaptative_codebook_gain(
                &self.target_signal[NB_LSP_COEFF..],
                &filtered_adaptative_codebook_vector[NB_LSP_COEFF..],
                &mut gain_quantization_xy,
                &mut gain_quantization_yy,
            );

            parameters_index += 1;
            if subframe_index == 0 {
                parameters[parameters_index] = compute_parity(parameters[parameters_index - 1]);
                parameters_index += 1;
            }

            // Fixed Codebook Search
            let mut param_fixed_codebook_idx: u16 = 0;
            let mut param_fixed_codebook_sign: u16 = 0;

            fixed_codebook_search(
                &self.target_signal[NB_LSP_COEFF..],
                &mut impulse_response_buffer[NB_LSP_COEFF..],
                int_pitch_delay,
                self.last_quantized_adaptative_codebook_gain,
                &filtered_adaptative_codebook_vector[NB_LSP_COEFF..],
                adaptative_codebook_gain,
                &mut param_fixed_codebook_idx,
                &mut param_fixed_codebook_sign,
                &mut fixed_codebook_vector,
                &mut convolved_fixed_codebook_vector,
            );
            parameters[parameters_index] = param_fixed_codebook_idx;
            parameters[parameters_index + 1] = param_fixed_codebook_sign;
            parameters_index += 2;

            // Gain Quantization
            let mut param_gain_stage1: u16 = 0;
            let mut param_gain_stage2: u16 = 0;

            gain_quantization(
                &self.target_signal[NB_LSP_COEFF..],
                &filtered_adaptative_codebook_vector[NB_LSP_COEFF..],
                &convolved_fixed_codebook_vector,
                &fixed_codebook_vector,
                gain_quantization_xy,
                gain_quantization_yy,
                &mut self.previous_gain_prediction_error,
                &mut quantized_adaptative_codebook_gain,
                &mut quantized_fixed_codebook_gain,
                &mut param_gain_stage1,
                &mut param_gain_stage2,
            );
            parameters[parameters_index] = param_gain_stage1;
            parameters[parameters_index + 1] = param_gain_stage2;
            parameters_index += 2;

            // Memory updates
            lp_coefficients_index += NB_LSP_COEFF;
            self.last_quantized_adaptative_codebook_gain = quantized_adaptative_codebook_gain;
            if self.last_quantized_adaptative_codebook_gain > ONE_POINT_2_IN_Q14 {
                self.last_quantized_adaptative_codebook_gain = ONE_POINT_2_IN_Q14;
            }
            if self.last_quantized_adaptative_codebook_gain < O2_IN_Q14 {
                self.last_quantized_adaptative_codebook_gain = O2_IN_Q14;
            }

            // Compute excitation
            for i in 0..L_SUBFRAME {
                self.excitation_vector[L_PAST_EXCITATION + subframe_index + i] = saturate(
                    pshr(
                        add32(
                            mult16_16(
                                self.excitation_vector[L_PAST_EXCITATION + subframe_index + i],
                                quantized_adaptative_codebook_gain,
                            ),
                            mult16_16(fixed_codebook_vector[i], quantized_fixed_codebook_gain),
                        ),
                        14,
                    ),
                    MAX_INT16 as Word32,
                )
                    as Word16;
            }

            // Update targetSignal memory
            let quantized_adaptative_codebook_gain_q13 =
                pshr(quantized_adaptative_codebook_gain as Word32, 1) as Word16;
            for i in 0..NB_LSP_COEFF {
                let acc = mac16_16(
                    mult16_16(
                        quantized_adaptative_codebook_gain_q13,
                        filtered_adaptative_codebook_vector[L_SUBFRAME + i],
                    ),
                    quantized_fixed_codebook_gain,
                    convolved_fixed_codebook_vector[L_SUBFRAME - NB_LSP_COEFF + i],
                );
                self.target_signal[i] = saturate(
                    sub32(self.target_signal[L_SUBFRAME + i] as Word32, pshr(acc, 13)),
                    MAX_INT16 as Word32,
                ) as Word16;
            }
        }

        // Frame basis memory updates
        // shift left by L_FRAME the signal buffer
        self.signal_buffer.copy_within(L_FRAME.., 0);

        // update previousLSP coefficient buffer
        self.previous_lsp_coefficients
            .copy_from_slice(&lsp_coefficients);
        self.previous_q_lsp_coefficients
            .copy_from_slice(&q_lsp_coefficients);

        // shift left by L_FRAME the weightedInputSignal buffer
        self.weighted_input_signal.copy_within(L_FRAME.., 0);

        // shift left by L_FRAME the excitationVector
        self.excitation_vector.copy_within(L_FRAME.., 0);

        // Convert array of parameters into bitStream
        parameters_array_2_bit_stream(&parameters, bit_stream);
    }
}

pub struct Encoder {
    context: EncoderChannelContext,
}

impl Encoder {
    pub fn new(enable_vad: bool) -> Self {
        Encoder {
            context: EncoderChannelContext::new(enable_vad),
        }
    }

    pub fn encode(
        &mut self,
        input_frame: &[i16],
        bit_stream: &mut [u8],
        bit_stream_length: &mut u8,
    ) {
        self.context
            .encode(input_frame, bit_stream, bit_stream_length);
    }
}