1 /***********************************************************************
2 Copyright (c) 2006-2011, Skype Limited. All rights reserved.
3 Redistribution and use in source and binary forms, with or without
4 modification, are permitted provided that the following conditions
5 are met:
6 - Redistributions of source code must retain the above copyright notice,
7 this list of conditions and the following disclaimer.
8 - Redistributions in binary form must reproduce the above copyright
9 notice, this list of conditions and the following disclaimer in the
10 documentation and/or other materials provided with the distribution.
11 - Neither the name of Internet Society, IETF or IETF Trust, nor the
12 names of specific contributors, may be used to endorse or promote
13 products derived from this software without specific prior written
14 permission.
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 POSSIBILITY OF SUCH DAMAGE.
26 ***********************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <stdlib.h>
33 #include "main_FLP.h"
34 #include "tuning_parameters.h"
35
36 /* Low Bitrate Redundancy (LBRR) encoding. Reuse all parameters but encode with lower bitrate */
37 static OPUS_INLINE void silk_LBRR_encode_FLP(
38 silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */
39 silk_encoder_control_FLP *psEncCtrl, /* I/O Encoder control FLP */
40 const silk_float xfw[], /* I Input signal */
41 opus_int condCoding /* I The type of conditional coding used so far for this frame */
42 );
43
silk_encode_do_VAD_FLP(silk_encoder_state_FLP * psEnc,opus_int activity)44 void silk_encode_do_VAD_FLP(
45 silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */
46 opus_int activity /* I Decision of Opus voice activity detector */
47 )
48 {
49 const opus_int activity_threshold = SILK_FIX_CONST( SPEECH_ACTIVITY_DTX_THRES, 8 );
50
51 /****************************/
52 /* Voice Activity Detection */
53 /****************************/
54 silk_VAD_GetSA_Q8( &psEnc->sCmn, psEnc->sCmn.inputBuf + 1, psEnc->sCmn.arch );
55 /* If Opus VAD is inactive and Silk VAD is active: lower Silk VAD to just under the threshold */
56 if( activity == VAD_NO_ACTIVITY && psEnc->sCmn.speech_activity_Q8 >= activity_threshold ) {
57 psEnc->sCmn.speech_activity_Q8 = activity_threshold - 1;
58 }
59
60 /**************************************************/
61 /* Convert speech activity into VAD and DTX flags */
62 /**************************************************/
63 if( psEnc->sCmn.speech_activity_Q8 < activity_threshold ) {
64 psEnc->sCmn.indices.signalType = TYPE_NO_VOICE_ACTIVITY;
65 psEnc->sCmn.noSpeechCounter++;
66 if( psEnc->sCmn.noSpeechCounter <= NB_SPEECH_FRAMES_BEFORE_DTX ) {
67 psEnc->sCmn.inDTX = 0;
68 } else if( psEnc->sCmn.noSpeechCounter > MAX_CONSECUTIVE_DTX + NB_SPEECH_FRAMES_BEFORE_DTX ) {
69 psEnc->sCmn.noSpeechCounter = NB_SPEECH_FRAMES_BEFORE_DTX;
70 psEnc->sCmn.inDTX = 0;
71 }
72 psEnc->sCmn.VAD_flags[ psEnc->sCmn.nFramesEncoded ] = 0;
73 } else {
74 psEnc->sCmn.noSpeechCounter = 0;
75 psEnc->sCmn.inDTX = 0;
76 psEnc->sCmn.indices.signalType = TYPE_UNVOICED;
77 psEnc->sCmn.VAD_flags[ psEnc->sCmn.nFramesEncoded ] = 1;
78 }
79 }
80
81 /****************/
82 /* Encode frame */
83 /****************/
silk_encode_frame_FLP(silk_encoder_state_FLP * psEnc,opus_int32 * pnBytesOut,ec_enc * psRangeEnc,opus_int condCoding,opus_int maxBits,opus_int useCBR)84 opus_int silk_encode_frame_FLP(
85 silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */
86 opus_int32 *pnBytesOut, /* O Number of payload bytes; */
87 ec_enc *psRangeEnc, /* I/O compressor data structure */
88 opus_int condCoding, /* I The type of conditional coding to use */
89 opus_int maxBits, /* I If > 0: maximum number of output bits */
90 opus_int useCBR /* I Flag to force constant-bitrate operation */
91 )
92 {
93 silk_encoder_control_FLP sEncCtrl;
94 opus_int i, iter, maxIter, found_upper, found_lower, ret = 0;
95 silk_float *x_frame, *res_pitch_frame;
96 silk_float res_pitch[ 2 * MAX_FRAME_LENGTH + LA_PITCH_MAX ];
97 ec_enc sRangeEnc_copy, sRangeEnc_copy2;
98 silk_nsq_state sNSQ_copy, sNSQ_copy2;
99 opus_int32 seed_copy, nBits, nBits_lower, nBits_upper, gainMult_lower, gainMult_upper;
100 opus_int32 gainsID, gainsID_lower, gainsID_upper;
101 opus_int16 gainMult_Q8;
102 opus_int16 ec_prevLagIndex_copy;
103 opus_int ec_prevSignalType_copy;
104 opus_int8 LastGainIndex_copy2;
105 opus_int32 pGains_Q16[ MAX_NB_SUBFR ];
106 opus_uint8 ec_buf_copy[ 1275 ];
107 opus_int gain_lock[ MAX_NB_SUBFR ] = {0};
108 opus_int16 best_gain_mult[ MAX_NB_SUBFR ];
109 opus_int best_sum[ MAX_NB_SUBFR ];
110 opus_int bits_margin;
111
112 /* For CBR, 5 bits below budget is close enough. For VBR, allow up to 25% below the cap if we initially busted the budget. */
113 bits_margin = useCBR ? 5 : maxBits/4;
114 /* This is totally unnecessary but many compilers (including gcc) are too dumb to realise it */
115 LastGainIndex_copy2 = nBits_lower = nBits_upper = gainMult_lower = gainMult_upper = 0;
116
117 psEnc->sCmn.indices.Seed = psEnc->sCmn.frameCounter++ & 3;
118
119 /**************************************************************/
120 /* Set up Input Pointers, and insert frame in input buffer */
121 /**************************************************************/
122 /* pointers aligned with start of frame to encode */
123 x_frame = psEnc->x_buf + psEnc->sCmn.ltp_mem_length; /* start of frame to encode */
124 res_pitch_frame = res_pitch + psEnc->sCmn.ltp_mem_length; /* start of pitch LPC residual frame */
125
126 /***************************************/
127 /* Ensure smooth bandwidth transitions */
128 /***************************************/
129 silk_LP_variable_cutoff( &psEnc->sCmn.sLP, psEnc->sCmn.inputBuf + 1, psEnc->sCmn.frame_length );
130
131 /*******************************************/
132 /* Copy new frame to front of input buffer */
133 /*******************************************/
134 silk_short2float_array( x_frame + LA_SHAPE_MS * psEnc->sCmn.fs_kHz, psEnc->sCmn.inputBuf + 1, psEnc->sCmn.frame_length );
135
136 /* Add tiny signal to avoid high CPU load from denormalized floating point numbers */
137 for( i = 0; i < 8; i++ ) {
138 x_frame[ LA_SHAPE_MS * psEnc->sCmn.fs_kHz + i * ( psEnc->sCmn.frame_length >> 3 ) ] += ( 1 - ( i & 2 ) ) * 1e-6f;
139 }
140
141 if( !psEnc->sCmn.prefillFlag ) {
142 /*****************************************/
143 /* Find pitch lags, initial LPC analysis */
144 /*****************************************/
145 silk_find_pitch_lags_FLP( psEnc, &sEncCtrl, res_pitch, x_frame, psEnc->sCmn.arch );
146
147 /************************/
148 /* Noise shape analysis */
149 /************************/
150 silk_noise_shape_analysis_FLP( psEnc, &sEncCtrl, res_pitch_frame, x_frame );
151
152 /***************************************************/
153 /* Find linear prediction coefficients (LPC + LTP) */
154 /***************************************************/
155 silk_find_pred_coefs_FLP( psEnc, &sEncCtrl, res_pitch_frame, x_frame, condCoding );
156
157 /****************************************/
158 /* Process gains */
159 /****************************************/
160 silk_process_gains_FLP( psEnc, &sEncCtrl, condCoding );
161
162 /****************************************/
163 /* Low Bitrate Redundant Encoding */
164 /****************************************/
165 silk_LBRR_encode_FLP( psEnc, &sEncCtrl, x_frame, condCoding );
166
167 /* Loop over quantizer and entroy coding to control bitrate */
168 maxIter = 6;
169 gainMult_Q8 = SILK_FIX_CONST( 1, 8 );
170 found_lower = 0;
171 found_upper = 0;
172 gainsID = silk_gains_ID( psEnc->sCmn.indices.GainsIndices, psEnc->sCmn.nb_subfr );
173 gainsID_lower = -1;
174 gainsID_upper = -1;
175 /* Copy part of the input state */
176 silk_memcpy( &sRangeEnc_copy, psRangeEnc, sizeof( ec_enc ) );
177 silk_memcpy( &sNSQ_copy, &psEnc->sCmn.sNSQ, sizeof( silk_nsq_state ) );
178 seed_copy = psEnc->sCmn.indices.Seed;
179 ec_prevLagIndex_copy = psEnc->sCmn.ec_prevLagIndex;
180 ec_prevSignalType_copy = psEnc->sCmn.ec_prevSignalType;
181 for( iter = 0; ; iter++ ) {
182 if( gainsID == gainsID_lower ) {
183 nBits = nBits_lower;
184 } else if( gainsID == gainsID_upper ) {
185 nBits = nBits_upper;
186 } else {
187 /* Restore part of the input state */
188 if( iter > 0 ) {
189 silk_memcpy( psRangeEnc, &sRangeEnc_copy, sizeof( ec_enc ) );
190 silk_memcpy( &psEnc->sCmn.sNSQ, &sNSQ_copy, sizeof( silk_nsq_state ) );
191 psEnc->sCmn.indices.Seed = seed_copy;
192 psEnc->sCmn.ec_prevLagIndex = ec_prevLagIndex_copy;
193 psEnc->sCmn.ec_prevSignalType = ec_prevSignalType_copy;
194 }
195
196 /*****************************************/
197 /* Noise shaping quantization */
198 /*****************************************/
199 silk_NSQ_wrapper_FLP( psEnc, &sEncCtrl, &psEnc->sCmn.indices, &psEnc->sCmn.sNSQ, psEnc->sCmn.pulses, x_frame );
200
201 if ( iter == maxIter && !found_lower ) {
202 silk_memcpy( &sRangeEnc_copy2, psRangeEnc, sizeof( ec_enc ) );
203 }
204
205 /****************************************/
206 /* Encode Parameters */
207 /****************************************/
208 silk_encode_indices( &psEnc->sCmn, psRangeEnc, psEnc->sCmn.nFramesEncoded, 0, condCoding );
209
210 /****************************************/
211 /* Encode Excitation Signal */
212 /****************************************/
213 silk_encode_pulses( psRangeEnc, psEnc->sCmn.indices.signalType, psEnc->sCmn.indices.quantOffsetType,
214 psEnc->sCmn.pulses, psEnc->sCmn.frame_length );
215
216 nBits = ec_tell( psRangeEnc );
217
218 /* If we still bust after the last iteration, do some damage control. */
219 if ( iter == maxIter && !found_lower && nBits > maxBits ) {
220 silk_memcpy( psRangeEnc, &sRangeEnc_copy2, sizeof( ec_enc ) );
221
222 /* Keep gains the same as the last frame. */
223 psEnc->sShape.LastGainIndex = sEncCtrl.lastGainIndexPrev;
224 for ( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) {
225 psEnc->sCmn.indices.GainsIndices[ i ] = 4;
226 }
227 if (condCoding != CODE_CONDITIONALLY) {
228 psEnc->sCmn.indices.GainsIndices[ 0 ] = sEncCtrl.lastGainIndexPrev;
229 }
230 psEnc->sCmn.ec_prevLagIndex = ec_prevLagIndex_copy;
231 psEnc->sCmn.ec_prevSignalType = ec_prevSignalType_copy;
232 /* Clear all pulses. */
233 for ( i = 0; i < psEnc->sCmn.frame_length; i++ ) {
234 psEnc->sCmn.pulses[ i ] = 0;
235 }
236
237 silk_encode_indices( &psEnc->sCmn, psRangeEnc, psEnc->sCmn.nFramesEncoded, 0, condCoding );
238
239 silk_encode_pulses( psRangeEnc, psEnc->sCmn.indices.signalType, psEnc->sCmn.indices.quantOffsetType,
240 psEnc->sCmn.pulses, psEnc->sCmn.frame_length );
241
242 nBits = ec_tell( psRangeEnc );
243 }
244
245 if( useCBR == 0 && iter == 0 && nBits <= maxBits ) {
246 break;
247 }
248 }
249
250 if( iter == maxIter ) {
251 if( found_lower && ( gainsID == gainsID_lower || nBits > maxBits ) ) {
252 /* Restore output state from earlier iteration that did meet the bitrate budget */
253 silk_memcpy( psRangeEnc, &sRangeEnc_copy2, sizeof( ec_enc ) );
254 celt_assert( sRangeEnc_copy2.offs <= 1275 );
255 silk_memcpy( psRangeEnc->buf, ec_buf_copy, sRangeEnc_copy2.offs );
256 silk_memcpy( &psEnc->sCmn.sNSQ, &sNSQ_copy2, sizeof( silk_nsq_state ) );
257 psEnc->sShape.LastGainIndex = LastGainIndex_copy2;
258 }
259 break;
260 }
261
262 if( nBits > maxBits ) {
263 if( found_lower == 0 && iter >= 2 ) {
264 /* Adjust the quantizer's rate/distortion tradeoff and discard previous "upper" results */
265 sEncCtrl.Lambda = silk_max_float(sEncCtrl.Lambda*1.5f, 1.5f);
266 /* Reducing dithering can help us hit the target. */
267 psEnc->sCmn.indices.quantOffsetType = 0;
268 found_upper = 0;
269 gainsID_upper = -1;
270 } else {
271 found_upper = 1;
272 nBits_upper = nBits;
273 gainMult_upper = gainMult_Q8;
274 gainsID_upper = gainsID;
275 }
276 } else if( nBits < maxBits - bits_margin ) {
277 found_lower = 1;
278 nBits_lower = nBits;
279 gainMult_lower = gainMult_Q8;
280 if( gainsID != gainsID_lower ) {
281 gainsID_lower = gainsID;
282 /* Copy part of the output state */
283 silk_memcpy( &sRangeEnc_copy2, psRangeEnc, sizeof( ec_enc ) );
284 celt_assert( psRangeEnc->offs <= 1275 );
285 silk_memcpy( ec_buf_copy, psRangeEnc->buf, psRangeEnc->offs );
286 silk_memcpy( &sNSQ_copy2, &psEnc->sCmn.sNSQ, sizeof( silk_nsq_state ) );
287 LastGainIndex_copy2 = psEnc->sShape.LastGainIndex;
288 }
289 } else {
290 /* Close enough */
291 break;
292 }
293
294 if ( !found_lower && nBits > maxBits ) {
295 int j;
296 for ( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) {
297 int sum=0;
298 for ( j = i*psEnc->sCmn.subfr_length; j < (i+1)*psEnc->sCmn.subfr_length; j++ ) {
299 sum += abs( psEnc->sCmn.pulses[j] );
300 }
301 if ( iter == 0 || (sum < best_sum[i] && !gain_lock[i]) ) {
302 best_sum[i] = sum;
303 best_gain_mult[i] = gainMult_Q8;
304 } else {
305 gain_lock[i] = 1;
306 }
307 }
308 }
309 if( ( found_lower & found_upper ) == 0 ) {
310 /* Adjust gain according to high-rate rate/distortion curve */
311 if( nBits > maxBits ) {
312 gainMult_Q8 = silk_min_32( 1024, gainMult_Q8*3/2 );
313 } else {
314 gainMult_Q8 = silk_max_32( 64, gainMult_Q8*4/5 );
315 }
316 } else {
317 /* Adjust gain by interpolating */
318 gainMult_Q8 = gainMult_lower + ( ( gainMult_upper - gainMult_lower ) * ( maxBits - nBits_lower ) ) / ( nBits_upper - nBits_lower );
319 /* New gain multplier must be between 25% and 75% of old range (note that gainMult_upper < gainMult_lower) */
320 if( gainMult_Q8 > silk_ADD_RSHIFT32( gainMult_lower, gainMult_upper - gainMult_lower, 2 ) ) {
321 gainMult_Q8 = silk_ADD_RSHIFT32( gainMult_lower, gainMult_upper - gainMult_lower, 2 );
322 } else
323 if( gainMult_Q8 < silk_SUB_RSHIFT32( gainMult_upper, gainMult_upper - gainMult_lower, 2 ) ) {
324 gainMult_Q8 = silk_SUB_RSHIFT32( gainMult_upper, gainMult_upper - gainMult_lower, 2 );
325 }
326 }
327
328 for( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) {
329 opus_int16 tmp;
330 if ( gain_lock[i] ) {
331 tmp = best_gain_mult[i];
332 } else {
333 tmp = gainMult_Q8;
334 }
335 pGains_Q16[ i ] = silk_LSHIFT_SAT32( silk_SMULWB( sEncCtrl.GainsUnq_Q16[ i ], tmp ), 8 );
336 }
337
338 /* Quantize gains */
339 psEnc->sShape.LastGainIndex = sEncCtrl.lastGainIndexPrev;
340 silk_gains_quant( psEnc->sCmn.indices.GainsIndices, pGains_Q16,
341 &psEnc->sShape.LastGainIndex, condCoding == CODE_CONDITIONALLY, psEnc->sCmn.nb_subfr );
342
343 /* Unique identifier of gains vector */
344 gainsID = silk_gains_ID( psEnc->sCmn.indices.GainsIndices, psEnc->sCmn.nb_subfr );
345
346 /* Overwrite unquantized gains with quantized gains and convert back to Q0 from Q16 */
347 for( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) {
348 sEncCtrl.Gains[ i ] = pGains_Q16[ i ] / 65536.0f;
349 }
350 }
351 }
352
353 /* Update input buffer */
354 silk_memmove( psEnc->x_buf, &psEnc->x_buf[ psEnc->sCmn.frame_length ],
355 ( psEnc->sCmn.ltp_mem_length + LA_SHAPE_MS * psEnc->sCmn.fs_kHz ) * sizeof( silk_float ) );
356
357 /* Exit without entropy coding */
358 if( psEnc->sCmn.prefillFlag ) {
359 /* No payload */
360 *pnBytesOut = 0;
361 return ret;
362 }
363
364 /* Parameters needed for next frame */
365 psEnc->sCmn.prevLag = sEncCtrl.pitchL[ psEnc->sCmn.nb_subfr - 1 ];
366 psEnc->sCmn.prevSignalType = psEnc->sCmn.indices.signalType;
367
368 /****************************************/
369 /* Finalize payload */
370 /****************************************/
371 psEnc->sCmn.first_frame_after_reset = 0;
372 /* Payload size */
373 *pnBytesOut = silk_RSHIFT( ec_tell( psRangeEnc ) + 7, 3 );
374
375 return ret;
376 }
377
378 /* Low-Bitrate Redundancy (LBRR) encoding. Reuse all parameters but encode excitation at lower bitrate */
silk_LBRR_encode_FLP(silk_encoder_state_FLP * psEnc,silk_encoder_control_FLP * psEncCtrl,const silk_float xfw[],opus_int condCoding)379 static OPUS_INLINE void silk_LBRR_encode_FLP(
380 silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */
381 silk_encoder_control_FLP *psEncCtrl, /* I/O Encoder control FLP */
382 const silk_float xfw[], /* I Input signal */
383 opus_int condCoding /* I The type of conditional coding used so far for this frame */
384 )
385 {
386 opus_int k;
387 opus_int32 Gains_Q16[ MAX_NB_SUBFR ];
388 silk_float TempGains[ MAX_NB_SUBFR ];
389 SideInfoIndices *psIndices_LBRR = &psEnc->sCmn.indices_LBRR[ psEnc->sCmn.nFramesEncoded ];
390 silk_nsq_state sNSQ_LBRR;
391
392 /*******************************************/
393 /* Control use of inband LBRR */
394 /*******************************************/
395 if( psEnc->sCmn.LBRR_enabled && psEnc->sCmn.speech_activity_Q8 > SILK_FIX_CONST( LBRR_SPEECH_ACTIVITY_THRES, 8 ) ) {
396 psEnc->sCmn.LBRR_flags[ psEnc->sCmn.nFramesEncoded ] = 1;
397
398 /* Copy noise shaping quantizer state and quantization indices from regular encoding */
399 silk_memcpy( &sNSQ_LBRR, &psEnc->sCmn.sNSQ, sizeof( silk_nsq_state ) );
400 silk_memcpy( psIndices_LBRR, &psEnc->sCmn.indices, sizeof( SideInfoIndices ) );
401
402 /* Save original gains */
403 silk_memcpy( TempGains, psEncCtrl->Gains, psEnc->sCmn.nb_subfr * sizeof( silk_float ) );
404
405 if( psEnc->sCmn.nFramesEncoded == 0 || psEnc->sCmn.LBRR_flags[ psEnc->sCmn.nFramesEncoded - 1 ] == 0 ) {
406 /* First frame in packet or previous frame not LBRR coded */
407 psEnc->sCmn.LBRRprevLastGainIndex = psEnc->sShape.LastGainIndex;
408
409 /* Increase Gains to get target LBRR rate */
410 psIndices_LBRR->GainsIndices[ 0 ] += psEnc->sCmn.LBRR_GainIncreases;
411 psIndices_LBRR->GainsIndices[ 0 ] = silk_min_int( psIndices_LBRR->GainsIndices[ 0 ], N_LEVELS_QGAIN - 1 );
412 }
413
414 /* Decode to get gains in sync with decoder */
415 silk_gains_dequant( Gains_Q16, psIndices_LBRR->GainsIndices,
416 &psEnc->sCmn.LBRRprevLastGainIndex, condCoding == CODE_CONDITIONALLY, psEnc->sCmn.nb_subfr );
417
418 /* Overwrite unquantized gains with quantized gains and convert back to Q0 from Q16 */
419 for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) {
420 psEncCtrl->Gains[ k ] = Gains_Q16[ k ] * ( 1.0f / 65536.0f );
421 }
422
423 /*****************************************/
424 /* Noise shaping quantization */
425 /*****************************************/
426 silk_NSQ_wrapper_FLP( psEnc, psEncCtrl, psIndices_LBRR, &sNSQ_LBRR,
427 psEnc->sCmn.pulses_LBRR[ psEnc->sCmn.nFramesEncoded ], xfw );
428
429 /* Restore original gains */
430 silk_memcpy( psEncCtrl->Gains, TempGains, psEnc->sCmn.nb_subfr * sizeof( silk_float ) );
431 }
432 }
433