Skip to main content

g729_sys/g729/
encoder.rs

1use crate::g729::basic_operations::*;
2use crate::g729::ld8k::*;
3// use crate::g729::fixed_point_math::*;
4use crate::g729::adaptative_codebook_search::*;
5use crate::g729::compute_adaptative_codebook_gain::*;
6use crate::g729::compute_lp::*;
7use crate::g729::compute_weighted_speech::*;
8use crate::g729::find_open_loop_pitch_delay::*;
9use crate::g729::fixed_codebook_search::*;
10use crate::g729::gain_quantization::*;
11use crate::g729::interpolate_q_lsp::*;
12use crate::g729::lp2lsp_conversion::*;
13use crate::g729::lp_synthesis_filter::lp_synthesis_filter;
14use crate::g729::lsp_quantization::*;
15use crate::g729::pre_processing::*;
16use crate::g729::q_lsp_2_lp::*;
17use crate::g729::utils::*;
18
19pub struct EncoderChannelContext {
20    /* buffers used in decoder bloc */
21    /* Signal buffer mapping : 240 word16_t length */
22    pub signal_buffer: [Word16; L_LP_ANALYSIS_WINDOW],
23
24    // Indices for signal buffer
25    // signalLastInputFrame index = L_LP_ANALYSIS_WINDOW - L_FRAME
26    // signalCurrentFrame index = L_LP_ANALYSIS_WINDOW - L_SUBFRAME - L_FRAME
27    pub previous_lsp_coefficients: [Word16; NB_LSP_COEFF],
28    pub previous_q_lsp_coefficients: [Word16; NB_LSP_COEFF],
29
30    pub weighted_input_signal: [Word16; MAXIMUM_INT_PITCH_DELAY + L_FRAME],
31    pub excitation_vector: [Word16; L_PAST_EXCITATION + L_FRAME],
32
33    pub target_signal: [Word16; NB_LSP_COEFF + L_SUBFRAME],
34
35    pub last_quantized_adaptative_codebook_gain: Word16,
36
37    /*** buffer used in preProcessing ***/
38    pub pre_processing_state: PreProcessingState,
39
40    /*** buffer used in LSPQuantization ***/
41    pub previous_q_lsf: [[Word16; NB_LSP_COEFF]; MA_MAX_K],
42
43    /*** buffer used in gainQuantization ***/
44    pub previous_gain_prediction_error: [Word16; 4],
45    // VAD/DTX context placeholders
46    // pub vad_channel_context: Option<VADChannelContext>,
47    // pub dtx_channel_context: Option<DTXChannelContext>,
48}
49
50const PREVIOUS_LSP_INITIAL_VALUES: [Word16; NB_LSP_COEFF] = [
51    30000, 26000, 21000, 15000, 8000, 0, -8000, -15000, -21000, -26000,
52];
53
54// Indices
55const SIGNAL_LAST_INPUT_FRAME_IDX: usize = L_LP_ANALYSIS_WINDOW - L_FRAME;
56const SIGNAL_CURRENT_FRAME_IDX: usize = L_LP_ANALYSIS_WINDOW - L_SUBFRAME - L_FRAME;
57
58impl EncoderChannelContext {
59    pub fn new(_enable_vad: bool) -> Self {
60        let mut ctx = EncoderChannelContext {
61            signal_buffer: [0; L_LP_ANALYSIS_WINDOW],
62            previous_lsp_coefficients: PREVIOUS_LSP_INITIAL_VALUES,
63            previous_q_lsp_coefficients: PREVIOUS_LSP_INITIAL_VALUES,
64            weighted_input_signal: [0; MAXIMUM_INT_PITCH_DELAY + L_FRAME],
65            excitation_vector: [0; L_PAST_EXCITATION + L_FRAME],
66            target_signal: [0; NB_LSP_COEFF + L_SUBFRAME],
67            last_quantized_adaptative_codebook_gain: O2_IN_Q14,
68            pre_processing_state: PreProcessingState::new(),
69            previous_q_lsf: [[0; NB_LSP_COEFF]; MA_MAX_K],
70            previous_gain_prediction_error: [0; 4],
71        };
72
73        // initPreProcessing
74        // Already done by PreProcessingState::new()
75
76        // initLSPQuantization
77        init_lsp_quantization(&mut ctx.previous_q_lsf);
78
79        // initGainQuantization
80        // In C: initGainQuantization sets previousGainPredictionError to -14dB in Q10.
81        // -14dB in Q10 is -14336? No.
82        // Let's check initGainQuantization in gainQuantization.c
83        // It sets it to -14336.
84        for i in 0..4 {
85            ctx.previous_gain_prediction_error[i] = -14336;
86        }
87
88        ctx
89    }
90
91    pub fn encode(
92        &mut self,
93        input_frame: &[Word16],
94        bit_stream: &mut [u8],
95        bit_stream_length: &mut u8,
96    ) {
97        let mut parameters = [0u16; NB_PARAMETERS];
98
99        // internal buffers
100        let mut lp_coefficients = [0i16; NB_LSP_COEFF];
101        let _lsf_coefficients = [0i16; NB_LSP_COEFF];
102        let mut q_lp_coefficients = [0i16; 2 * NB_LSP_COEFF];
103        let mut weighted_q_lp_coefficients = [0i16; 2 * NB_LSP_COEFF];
104        let mut lsp_coefficients = [0i16; NB_LSP_COEFF];
105        let mut q_lsp_coefficients = [0i16; NB_LSP_COEFF];
106        let mut interpolated_q_lsp = [0i16; NB_LSP_COEFF];
107
108        let mut parameters_index = 4;
109        let mut impulse_response_input = [0i16; L_SUBFRAME];
110
111        // VAD placeholders
112        let mut reflection_coefficients = [0i32; NB_LSP_COEFF];
113        let mut auto_correlation_coefficients = [0i32; NB_LSP_COEFF + 3];
114        let mut no_lag_auto_correlation_coefficients = [0i32; NB_LSP_COEFF + 3];
115        let mut auto_correlation_coefficients_scale = 0i8;
116
117        // Pre-processing
118        // preProcessing(encoderChannelContext, inputFrame, encoderChannelContext->signalLastInputFrame);
119
120        self.pre_processing_state.pre_processing(
121            input_frame,
122            &mut self.signal_buffer[SIGNAL_LAST_INPUT_FRAME_IDX..],
123        );
124
125        // Compute LP
126        // computeLP(encoderChannelContext->signalBuffer, LPCoefficients, reflectionCoefficients, ...);
127        // signalBuffer is used as input.
128        compute_lp(
129            &self.signal_buffer,
130            &mut lp_coefficients,
131            &mut reflection_coefficients,
132            &mut auto_correlation_coefficients,
133            &mut no_lag_auto_correlation_coefficients,
134            &mut auto_correlation_coefficients_scale,
135            NB_LSP_COEFF + 1, // VAD disabled for now
136        );
137
138        // LP to LSP
139        if !lp2lsp_conversion(&lp_coefficients, &mut lsp_coefficients) {
140            lsp_coefficients.copy_from_slice(&self.previous_lsp_coefficients);
141        }
142
143        // VAD check would go here.
144        *bit_stream_length = 10;
145
146        // LSP Quantization
147        lsp_quantization(
148            &mut self.previous_q_lsf,
149            &mut lsp_coefficients,
150            &mut q_lsp_coefficients,
151            &mut parameters,
152        );
153        // Interpolate qLSP
154        interpolate_q_lsp(
155            &self.previous_q_lsp_coefficients,
156            &q_lsp_coefficients,
157            &mut interpolated_q_lsp,
158        );
159
160        // Update previous qLSP
161        self.previous_q_lsp_coefficients
162            .copy_from_slice(&q_lsp_coefficients);
163
164        // qLSP to LP
165        // first subframe
166        q_lsp_2_lp(&interpolated_q_lsp, &mut q_lp_coefficients[0..NB_LSP_COEFF]);
167        // second subframe
168        q_lsp_2_lp(&q_lsp_coefficients, &mut q_lp_coefficients[NB_LSP_COEFF..]);
169
170        // Compute weighted qLP
171        for i in 0..2 * NB_LSP_COEFF {
172            // weightedqLPCoefficients[i] = qLPCoefficients[i]*Gamma^(i%10+1)
173            // We can use a helper or just loop.
174            // The C code unrolls it.
175            let gamma_idx = i % NB_LSP_COEFF;
176            let gamma = match gamma_idx {
177                0 => GAMMA_E1,
178                1 => GAMMA_E2,
179                2 => GAMMA_E3,
180                3 => GAMMA_E4,
181                4 => GAMMA_E5,
182                5 => GAMMA_E6,
183                6 => GAMMA_E7,
184                7 => GAMMA_E8,
185                8 => GAMMA_E9,
186                9 => GAMMA_E10,
187                _ => 0,
188            };
189            weighted_q_lp_coefficients[i] = mult16_16_p15(q_lp_coefficients[i], gamma) as Word16;
190        }
191
192        // Compute weighted speech
193        // computeWeightedSpeech(encoderChannelContext->signalCurrentFrame, qLPCoefficients, weightedqLPCoefficients, &(encoderChannelContext->weightedInputSignal[MAXIMUM_INT_PITCH_DELAY]), &(encoderChannelContext->excitationVector[L_PAST_EXCITATION]));
194        // signalCurrentFrame is at SIGNAL_CURRENT_FRAME_IDX.
195        // weightedInputSignal output starts at MAXIMUM_INT_PITCH_DELAY.
196        // excitationVector output starts at L_PAST_EXCITATION.
197
198        compute_weighted_speech(
199            &self.signal_buffer[SIGNAL_CURRENT_FRAME_IDX - NB_LSP_COEFF..],
200            &q_lp_coefficients,
201            &weighted_q_lp_coefficients,
202            &mut self.weighted_input_signal[MAXIMUM_INT_PITCH_DELAY - NB_LSP_COEFF..],
203            &mut self.excitation_vector[L_PAST_EXCITATION..],
204        );
205
206        // Open loop pitch search
207        let open_loop_pitch_delay = find_open_loop_pitch_delay(&self.weighted_input_signal);
208        let mut int_pitch_delay_min = open_loop_pitch_delay as i16 - 3;
209        if int_pitch_delay_min < 20 {
210            int_pitch_delay_min = 20;
211        }
212        let mut int_pitch_delay_max = int_pitch_delay_min + 6;
213        if int_pitch_delay_max > MAXIMUM_INT_PITCH_DELAY as i16 {
214            int_pitch_delay_max = MAXIMUM_INT_PITCH_DELAY as i16;
215            int_pitch_delay_min = MAXIMUM_INT_PITCH_DELAY as i16 - 6;
216        }
217
218        // Subframe loop
219        impulse_response_input[0] = ONE_IN_Q12 as Word16;
220        for i in 1..L_SUBFRAME {
221            impulse_response_input[i] = 0;
222        }
223
224        let mut lp_coefficients_index = 0;
225
226        for subframe_index in (0..L_FRAME).step_by(L_SUBFRAME) {
227            let mut int_pitch_delay: i16 = 0;
228            let mut frac_pitch_delay: i16 = 0;
229            let adaptative_codebook_gain: Word16;
230
231            let mut impulse_response_buffer = [0i16; NB_LSP_COEFF + L_SUBFRAME];
232            let mut filtered_adaptative_codebook_vector = [0i16; NB_LSP_COEFF + L_SUBFRAME];
233            let mut gain_quantization_xy: Word64 = 0;
234            let mut gain_quantization_yy: Word64 = 0;
235            let mut fixed_codebook_vector = [0i16; L_SUBFRAME];
236            let mut convolved_fixed_codebook_vector = [0i16; L_SUBFRAME];
237            let mut quantized_adaptative_codebook_gain: Word16 = 0;
238            let mut quantized_fixed_codebook_gain: Word16 = 0;
239
240            // Compute impulse response
241            // synthesisFilter(impulseResponseInput, &(weightedqLPCoefficients[LPCoefficientsIndex]), &(impulseResponseBuffer[NB_LSP_COEFF]));
242            // In Rust, synthesis_filter takes the full buffer and writes to the end.
243            lp_synthesis_filter(
244                &impulse_response_input,
245                &weighted_q_lp_coefficients[lp_coefficients_index..],
246                &mut impulse_response_buffer,
247            );
248
249            // Compute target signal
250            // synthesisFilter( &(encoderChannelContext->excitationVector[L_PAST_EXCITATION+subframeIndex]), &(weightedqLPCoefficients[LPCoefficientsIndex]), &(encoderChannelContext->targetSignal[NB_LSP_COEFF]));
251            lp_synthesis_filter(
252                &self.excitation_vector[L_PAST_EXCITATION + subframe_index
253                    ..L_PAST_EXCITATION + subframe_index + L_SUBFRAME],
254                &weighted_q_lp_coefficients[lp_coefficients_index..],
255                &mut self.target_signal,
256            );
257
258            // Adaptative Codebook Search
259            let mut param_pitch_delay: u16 = 0;
260            adaptative_codebook_search(
261                &mut self.excitation_vector,
262                L_PAST_EXCITATION + subframe_index,
263                &mut int_pitch_delay_min,
264                &mut int_pitch_delay_max,
265                &impulse_response_buffer[NB_LSP_COEFF..],
266                &self.target_signal[NB_LSP_COEFF..],
267                &mut int_pitch_delay,
268                &mut frac_pitch_delay,
269                &mut param_pitch_delay,
270                subframe_index as u16,
271            );
272            parameters[parameters_index] = param_pitch_delay;
273
274            // Compute adaptative codebook gain
275            // synthesisFilter(&(encoderChannelContext->excitationVector[L_PAST_EXCITATION + subframeIndex]), &(weightedqLPCoefficients[LPCoefficientsIndex]), &(filteredAdaptativeCodebookVector[NB_LSP_COEFF]));
276            lp_synthesis_filter(
277                &self.excitation_vector[L_PAST_EXCITATION + subframe_index
278                    ..L_PAST_EXCITATION + subframe_index + L_SUBFRAME],
279                &weighted_q_lp_coefficients[lp_coefficients_index..],
280                &mut filtered_adaptative_codebook_vector,
281            );
282
283            adaptative_codebook_gain = compute_adaptative_codebook_gain(
284                &self.target_signal[NB_LSP_COEFF..],
285                &filtered_adaptative_codebook_vector[NB_LSP_COEFF..],
286                &mut gain_quantization_xy,
287                &mut gain_quantization_yy,
288            );
289
290            parameters_index += 1;
291            if subframe_index == 0 {
292                parameters[parameters_index] = compute_parity(parameters[parameters_index - 1]);
293                parameters_index += 1;
294            }
295
296            // Fixed Codebook Search
297            let mut param_fixed_codebook_idx: u16 = 0;
298            let mut param_fixed_codebook_sign: u16 = 0;
299
300            fixed_codebook_search(
301                &self.target_signal[NB_LSP_COEFF..],
302                &mut impulse_response_buffer[NB_LSP_COEFF..],
303                int_pitch_delay,
304                self.last_quantized_adaptative_codebook_gain,
305                &filtered_adaptative_codebook_vector[NB_LSP_COEFF..],
306                adaptative_codebook_gain,
307                &mut param_fixed_codebook_idx,
308                &mut param_fixed_codebook_sign,
309                &mut fixed_codebook_vector,
310                &mut convolved_fixed_codebook_vector,
311            );
312            parameters[parameters_index] = param_fixed_codebook_idx;
313            parameters[parameters_index + 1] = param_fixed_codebook_sign;
314            parameters_index += 2;
315
316            // Gain Quantization
317            let mut param_gain_stage1: u16 = 0;
318            let mut param_gain_stage2: u16 = 0;
319
320            gain_quantization(
321                &self.target_signal[NB_LSP_COEFF..],
322                &filtered_adaptative_codebook_vector[NB_LSP_COEFF..],
323                &convolved_fixed_codebook_vector,
324                &fixed_codebook_vector,
325                gain_quantization_xy,
326                gain_quantization_yy,
327                &mut self.previous_gain_prediction_error,
328                &mut quantized_adaptative_codebook_gain,
329                &mut quantized_fixed_codebook_gain,
330                &mut param_gain_stage1,
331                &mut param_gain_stage2,
332            );
333            parameters[parameters_index] = param_gain_stage1;
334            parameters[parameters_index + 1] = param_gain_stage2;
335            parameters_index += 2;
336
337            // Memory updates
338            lp_coefficients_index += NB_LSP_COEFF;
339            self.last_quantized_adaptative_codebook_gain = quantized_adaptative_codebook_gain;
340            if self.last_quantized_adaptative_codebook_gain > ONE_POINT_2_IN_Q14 {
341                self.last_quantized_adaptative_codebook_gain = ONE_POINT_2_IN_Q14;
342            }
343            if self.last_quantized_adaptative_codebook_gain < O2_IN_Q14 {
344                self.last_quantized_adaptative_codebook_gain = O2_IN_Q14;
345            }
346
347            // Compute excitation
348            for i in 0..L_SUBFRAME {
349                self.excitation_vector[L_PAST_EXCITATION + subframe_index + i] = saturate(
350                    pshr(
351                        add32(
352                            mult16_16(
353                                self.excitation_vector[L_PAST_EXCITATION + subframe_index + i],
354                                quantized_adaptative_codebook_gain,
355                            ),
356                            mult16_16(fixed_codebook_vector[i], quantized_fixed_codebook_gain),
357                        ),
358                        14,
359                    ),
360                    MAX_INT16 as Word32,
361                )
362                    as Word16;
363            }
364
365            // Update targetSignal memory
366            let quantized_adaptative_codebook_gain_q13 =
367                pshr(quantized_adaptative_codebook_gain as Word32, 1) as Word16;
368            for i in 0..NB_LSP_COEFF {
369                let acc = mac16_16(
370                    mult16_16(
371                        quantized_adaptative_codebook_gain_q13,
372                        filtered_adaptative_codebook_vector[L_SUBFRAME + i],
373                    ),
374                    quantized_fixed_codebook_gain,
375                    convolved_fixed_codebook_vector[L_SUBFRAME - NB_LSP_COEFF + i],
376                );
377                self.target_signal[i] = saturate(
378                    sub32(self.target_signal[L_SUBFRAME + i] as Word32, pshr(acc, 13)),
379                    MAX_INT16 as Word32,
380                ) as Word16;
381            }
382        }
383
384        // Frame basis memory updates
385        // shift left by L_FRAME the signal buffer
386        self.signal_buffer.copy_within(L_FRAME.., 0);
387
388        // update previousLSP coefficient buffer
389        self.previous_lsp_coefficients
390            .copy_from_slice(&lsp_coefficients);
391        self.previous_q_lsp_coefficients
392            .copy_from_slice(&q_lsp_coefficients);
393
394        // shift left by L_FRAME the weightedInputSignal buffer
395        self.weighted_input_signal.copy_within(L_FRAME.., 0);
396
397        // shift left by L_FRAME the excitationVector
398        self.excitation_vector.copy_within(L_FRAME.., 0);
399
400        // Convert array of parameters into bitStream
401        parameters_array_2_bit_stream(&parameters, bit_stream);
402    }
403}
404
405pub struct Encoder {
406    context: EncoderChannelContext,
407}
408
409impl Encoder {
410    pub fn new(enable_vad: bool) -> Self {
411        Encoder {
412            context: EncoderChannelContext::new(enable_vad),
413        }
414    }
415
416    pub fn encode(
417        &mut self,
418        input_frame: &[i16],
419        bit_stream: &mut [u8],
420        bit_stream_length: &mut u8,
421    ) {
422        self.context
423            .encode(input_frame, bit_stream, bit_stream_length);
424    }
425}