OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / media / libstagefright / codecs / amrwbenc / src / dtx.c
1 /*\r
2  ** Copyright 2003-2010, VisualOn, Inc.\r
3  **\r
4  ** Licensed under the Apache License, Version 2.0 (the "License");\r
5  ** you may not use this file except in compliance with the License.\r
6  ** You may obtain a copy of the License at\r
7  **\r
8  **     http://www.apache.org/licenses/LICENSE-2.0\r
9  **\r
10  ** Unless required by applicable law or agreed to in writing, software\r
11  ** distributed under the License is distributed on an "AS IS" BASIS,\r
12  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  ** See the License for the specific language governing permissions and\r
14  ** limitations under the License.\r
15  */\r
16 \r
17 /***********************************************************************\r
18 *       File: dtx.c                                                    *\r
19 *                                                                      *\r
20 *           Description:DTX functions                                  *\r
21 *                                                                      *\r
22 ************************************************************************/\r
23 \r
24 #include <stdio.h>\r
25 #include <stdlib.h>\r
26 #include "typedef.h"\r
27 #include "basic_op.h"\r
28 #include "oper_32b.h"\r
29 #include "math_op.h"\r
30 #include "cnst.h"\r
31 #include "acelp.h"                         /* prototype of functions    */\r
32 #include "bits.h"\r
33 #include "dtx.h"\r
34 #include "log2.h"\r
35 #include "mem_align.h"\r
36 \r
37 static void aver_isf_history(\r
38                 Word16 isf_old[],\r
39                 Word16 indices[],\r
40                 Word32 isf_aver[]\r
41                 );\r
42 \r
43 static void find_frame_indices(\r
44                 Word16 isf_old_tx[],\r
45                 Word16 indices[],\r
46                 dtx_encState * st\r
47                 );\r
48 \r
49 static Word16 dithering_control(\r
50                 dtx_encState * st\r
51                 );\r
52 \r
53 /* excitation energy adjustment depending on speech coder mode used, Q7 */\r
54 static Word16 en_adjust[9] =\r
55 {\r
56         230,                                   /* mode0 = 7k  :  -5.4dB  */\r
57         179,                                   /* mode1 = 9k  :  -4.2dB  */\r
58         141,                                   /* mode2 = 12k :  -3.3dB  */\r
59         128,                                   /* mode3 = 14k :  -3.0dB  */\r
60         122,                                   /* mode4 = 16k :  -2.85dB */\r
61         115,                                   /* mode5 = 18k :  -2.7dB  */\r
62         115,                                   /* mode6 = 20k :  -2.7dB  */\r
63         115,                                   /* mode7 = 23k :  -2.7dB  */\r
64         115                                    /* mode8 = 24k :  -2.7dB  */\r
65 };\r
66 \r
67 /**************************************************************************\r
68 *\r
69 * Function    : dtx_enc_init\r
70 *\r
71 **************************************************************************/\r
72 Word16 dtx_enc_init(dtx_encState ** st, Word16 isf_init[], VO_MEM_OPERATOR *pMemOP)\r
73 {\r
74         dtx_encState *s;\r
75 \r
76         if (st == (dtx_encState **) NULL)\r
77         {\r
78                 fprintf(stderr, "dtx_enc_init: invalid parameter\n");\r
79                 return -1;\r
80         }\r
81         *st = NULL;\r
82 \r
83         /* allocate memory */\r
84         if ((s = (dtx_encState *)mem_malloc(pMemOP, sizeof(dtx_encState), 32, VO_INDEX_ENC_AMRWB)) == NULL)\r
85         {\r
86                 fprintf(stderr, "dtx_enc_init: can not malloc state structure\n");\r
87                 return -1;\r
88         }\r
89         dtx_enc_reset(s, isf_init);\r
90         *st = s;\r
91         return 0;\r
92 }\r
93 \r
94 /**************************************************************************\r
95 *\r
96 * Function    : dtx_enc_reset\r
97 *\r
98 **************************************************************************/\r
99 Word16 dtx_enc_reset(dtx_encState * st, Word16 isf_init[])\r
100 {\r
101         Word32 i;\r
102 \r
103         if (st == (dtx_encState *) NULL)\r
104         {\r
105                 fprintf(stderr, "dtx_enc_reset: invalid parameter\n");\r
106                 return -1;\r
107         }\r
108         st->hist_ptr = 0;                      \r
109         st->log_en_index = 0;                  \r
110 \r
111         /* Init isf_hist[] */\r
112         for (i = 0; i < DTX_HIST_SIZE; i++)\r
113         {\r
114                 Copy(isf_init, &st->isf_hist[i * M], M);\r
115         }\r
116         st->cng_seed = RANDOM_INITSEED;       \r
117 \r
118         /* Reset energy history */\r
119         Set_zero(st->log_en_hist, DTX_HIST_SIZE);\r
120 \r
121         st->dtxHangoverCount = DTX_HANG_CONST; \r
122         st->decAnaElapsedCount = 32767;        \r
123 \r
124         for (i = 0; i < 28; i++)\r
125         {\r
126                 st->D[i] = 0;                      \r
127         }\r
128 \r
129         for (i = 0; i < DTX_HIST_SIZE - 1; i++)\r
130         {\r
131                 st->sumD[i] = 0;                   \r
132         }\r
133 \r
134         return 1;\r
135 }\r
136 \r
137 /**************************************************************************\r
138 *\r
139 * Function    : dtx_enc_exit\r
140 *\r
141 **************************************************************************/\r
142 void dtx_enc_exit(dtx_encState ** st, VO_MEM_OPERATOR *pMemOP)\r
143 {\r
144         if (st == NULL || *st == NULL)\r
145                 return;\r
146         /* deallocate memory */\r
147         mem_free(pMemOP, *st, VO_INDEX_ENC_AMRWB);\r
148         *st = NULL;\r
149         return;\r
150 }\r
151 \r
152 \r
153 /**************************************************************************\r
154 *\r
155 * Function    : dtx_enc\r
156 *\r
157 **************************************************************************/\r
158 Word16 dtx_enc(\r
159                 dtx_encState * st,                    /* i/o : State struct                                         */\r
160                 Word16 isf[M],                        /* o   : CN ISF vector                                        */\r
161                 Word16 * exc2,                        /* o   : CN excitation                                        */\r
162                 Word16 ** prms\r
163               )\r
164 {\r
165         Word32 i, j;\r
166         Word16 indice[7];\r
167         Word16 log_en, gain, level, exp, exp0, tmp;\r
168         Word16 log_en_int_e, log_en_int_m;\r
169         Word32 L_isf[M], ener32, level32;\r
170         Word16 isf_order[3];\r
171         Word16 CN_dith;\r
172 \r
173         /* VOX mode computation of SID parameters */\r
174         log_en = 0;\r
175         for (i = 0; i < M; i++)\r
176         {\r
177                 L_isf[i] = 0;\r
178         }\r
179         /* average energy and isf */\r
180         for (i = 0; i < DTX_HIST_SIZE; i++)\r
181         {\r
182                 /* Division by DTX_HIST_SIZE = 8 has been done in dtx_buffer. log_en is in Q10 */\r
183                 log_en = add(log_en, st->log_en_hist[i]);\r
184 \r
185         }\r
186         find_frame_indices(st->isf_hist, isf_order, st);\r
187         aver_isf_history(st->isf_hist, isf_order, L_isf);\r
188 \r
189         for (j = 0; j < M; j++)\r
190         {\r
191                 isf[j] = (Word16)(L_isf[j] >> 3);  /* divide by 8 */\r
192         }\r
193 \r
194         /* quantize logarithmic energy to 6 bits (-6 : 66 dB) which corresponds to -2:22 in log2(E).  */\r
195         /* st->log_en_index = (short)( (log_en + 2.0) * 2.625 ); */\r
196 \r
197         /* increase dynamics to 7 bits (Q8) */\r
198         log_en = (log_en >> 2);\r
199 \r
200         /* Add 2 in Q8 = 512 to get log2(E) between 0:24 */\r
201         log_en = add(log_en, 512);\r
202 \r
203         /* Multiply by 2.625 to get full 6 bit range. 2.625 = 21504 in Q13. The result is in Q6 */\r
204         log_en = mult(log_en, 21504);\r
205 \r
206         /* Quantize Energy */\r
207         st->log_en_index = shr(log_en, 6);\r
208 \r
209         if(st->log_en_index > 63)\r
210         {\r
211                 st->log_en_index = 63;\r
212         }\r
213         if (st->log_en_index < 0)\r
214         {\r
215                 st->log_en_index = 0;\r
216         }\r
217         /* Quantize ISFs */\r
218         Qisf_ns(isf, isf, indice);\r
219 \r
220 \r
221         Parm_serial(indice[0], 6, prms);\r
222         Parm_serial(indice[1], 6, prms);\r
223         Parm_serial(indice[2], 6, prms);\r
224         Parm_serial(indice[3], 5, prms);\r
225         Parm_serial(indice[4], 5, prms);\r
226 \r
227         Parm_serial((st->log_en_index), 6, prms);\r
228 \r
229         CN_dith = dithering_control(st);\r
230         Parm_serial(CN_dith, 1, prms);\r
231 \r
232         /* level = (float)( pow( 2.0f, (float)st->log_en_index / 2.625 - 2.0 ) );    */\r
233         /* log2(E) in Q9 (log2(E) lies in between -2:22) */\r
234         log_en = shl(st->log_en_index, 15 - 6);\r
235 \r
236         /* Divide by 2.625; log_en will be between 0:24  */\r
237         log_en = mult(log_en, 12483);\r
238         /* the result corresponds to log2(gain) in Q10 */\r
239 \r
240         /* Find integer part  */\r
241         log_en_int_e = (log_en >> 10);\r
242 \r
243         /* Find fractional part */\r
244         log_en_int_m = (Word16) (log_en & 0x3ff);\r
245         log_en_int_m = shl(log_en_int_m, 5);\r
246 \r
247         /* Subtract 2 from log_en in Q9, i.e divide the gain by 2 (energy by 4) */\r
248         /* Add 16 in order to have the result of pow2 in Q16 */\r
249         log_en_int_e = add(log_en_int_e, 16 - 1);\r
250 \r
251         level32 = Pow2(log_en_int_e, log_en_int_m); /* Q16 */\r
252         exp0 = norm_l(level32);\r
253         level32 = (level32 << exp0);        /* level in Q31 */\r
254         exp0 = (15 - exp0);\r
255         level = extract_h(level32);            /* level in Q15 */\r
256 \r
257         /* generate white noise vector */\r
258         for (i = 0; i < L_FRAME; i++)\r
259         {\r
260                 exc2[i] = (Random(&(st->cng_seed)) >> 4);\r
261         }\r
262 \r
263         /* gain = level / sqrt(ener) * sqrt(L_FRAME) */\r
264 \r
265         /* energy of generated excitation */\r
266         ener32 = Dot_product12(exc2, exc2, L_FRAME, &exp);\r
267 \r
268         Isqrt_n(&ener32, &exp);\r
269 \r
270         gain = extract_h(ener32);\r
271 \r
272         gain = mult(level, gain);              /* gain in Q15 */\r
273 \r
274         exp = add(exp0, exp);\r
275 \r
276         /* Multiply by sqrt(L_FRAME)=16, i.e. shift left by 4 */\r
277         exp += 4;\r
278 \r
279         for (i = 0; i < L_FRAME; i++)\r
280         {\r
281                 tmp = mult(exc2[i], gain);         /* Q0 * Q15 */\r
282                 exc2[i] = shl(tmp, exp); \r
283         }\r
284 \r
285         return 0;\r
286 }\r
287 \r
288 /**************************************************************************\r
289 *\r
290 * Function    : dtx_buffer Purpose     : handles the DTX buffer\r
291 *\r
292 **************************************************************************/\r
293 Word16 dtx_buffer(\r
294                 dtx_encState * st,                    /* i/o : State struct                    */\r
295                 Word16 isf_new[],                     /* i   : isf vector                      */\r
296                 Word32 enr,                           /* i   : residual energy (in L_FRAME)    */\r
297                 Word16 codec_mode\r
298                 )\r
299 {\r
300         Word16 log_en;\r
301 \r
302         Word16 log_en_e;\r
303         Word16 log_en_m;\r
304         st->hist_ptr = add(st->hist_ptr, 1); \r
305         if(st->hist_ptr == DTX_HIST_SIZE)\r
306         {\r
307                 st->hist_ptr = 0;\r
308         }\r
309         /* copy lsp vector into buffer */\r
310         Copy(isf_new, &st->isf_hist[st->hist_ptr * M], M);\r
311 \r
312         /* log_en = (float)log10(enr*0.0059322)/(float)log10(2.0f);  */\r
313         Log2(enr, &log_en_e, &log_en_m);\r
314 \r
315         /* convert exponent and mantissa to Word16 Q7. Q7 is used to simplify averaging in dtx_enc */\r
316         log_en = shl(log_en_e, 7);             /* Q7 */\r
317         log_en = add(log_en, shr(log_en_m, 15 - 7));\r
318 \r
319         /* Find energy per sample by multiplying with 0.0059322, i.e subtract log2(1/0.0059322) = 7.39722 The\r
320          * constant 0.0059322 takes into account windowings and analysis length from autocorrelation\r
321          * computations; 7.39722 in Q7 = 947  */\r
322         /* Subtract 3 dB = 0.99658 in log2(E) = 127 in Q7. */\r
323         /* log_en = sub( log_en, 947 + en_adjust[codec_mode] ); */\r
324 \r
325         /* Find energy per sample (divide by L_FRAME=256), i.e subtract log2(256) = 8.0  (1024 in Q7) */\r
326         /* Subtract 3 dB = 0.99658 in log2(E) = 127 in Q7. */\r
327 \r
328         log_en = sub(log_en, add(1024, en_adjust[codec_mode]));\r
329 \r
330         /* Insert into the buffer */\r
331         st->log_en_hist[st->hist_ptr] = log_en;\r
332         return 0;\r
333 }\r
334 \r
335 /**************************************************************************\r
336 *\r
337 * Function    : tx_dtx_handler Purpose     : adds extra speech hangover\r
338 *                                            to analyze speech on\r
339 *                                            the decoding side.\r
340 **************************************************************************/\r
341 void tx_dtx_handler(dtx_encState * st,     /* i/o : State struct           */\r
342                 Word16 vad_flag,                      /* i   : vad decision           */\r
343                 Word16 * usedMode                     /* i/o : mode changed or not    */\r
344                 )\r
345 {\r
346 \r
347         /* this state machine is in synch with the GSMEFR txDtx machine      */\r
348         st->decAnaElapsedCount = add(st->decAnaElapsedCount, 1); \r
349 \r
350         if (vad_flag != 0)\r
351         {\r
352                 st->dtxHangoverCount = DTX_HANG_CONST;\r
353         } else\r
354         {                                      /* non-speech */\r
355                 if (st->dtxHangoverCount == 0)\r
356                 {                                  /* out of decoder analysis hangover  */\r
357                         st->decAnaElapsedCount = 0;    \r
358                         *usedMode = MRDTX;            \r
359                 } else\r
360                 {                                  /* in possible analysis hangover */\r
361                         st->dtxHangoverCount = sub(st->dtxHangoverCount, 1);\r
362 \r
363                         /* decAnaElapsedCount + dtxHangoverCount < DTX_ELAPSED_FRAMES_THRESH */\r
364                         if (sub(add(st->decAnaElapsedCount, st->dtxHangoverCount),\r
365                                                 DTX_ELAPSED_FRAMES_THRESH) < 0)\r
366                         {\r
367                                 *usedMode = MRDTX;\r
368                                 /* if short time since decoder update, do not add extra HO */\r
369                         }\r
370                         /* else override VAD and stay in speech mode *usedMode and add extra hangover */\r
371                 }\r
372         }\r
373 \r
374         return;\r
375 }\r
376 \r
377 \r
378 \r
379 static void aver_isf_history(\r
380                 Word16 isf_old[],\r
381                 Word16 indices[],\r
382                 Word32 isf_aver[]\r
383                 )\r
384 {\r
385         Word32 i, j, k;\r
386         Word16 isf_tmp[2 * M];\r
387         Word32 L_tmp;\r
388 \r
389         /* Memorize in isf_tmp[][] the ISF vectors to be replaced by */\r
390         /* the median ISF vector prior to the averaging               */\r
391         for (k = 0; k < 2; k++)\r
392         {\r
393                 if ((indices[k] + 1) != 0)\r
394                 {\r
395                         for (i = 0; i < M; i++)\r
396                         {\r
397                                 isf_tmp[k * M + i] = isf_old[indices[k] * M + i];      \r
398                                 isf_old[indices[k] * M + i] = isf_old[indices[2] * M + i];    \r
399                         }\r
400                 }\r
401         }\r
402 \r
403         /* Perform the ISF averaging */\r
404         for (j = 0; j < M; j++)\r
405         {\r
406                 L_tmp = 0;                      \r
407 \r
408                 for (i = 0; i < DTX_HIST_SIZE; i++)\r
409                 {\r
410                         L_tmp = L_add(L_tmp, L_deposit_l(isf_old[i * M + j]));\r
411                 }\r
412                 isf_aver[j] = L_tmp;              \r
413         }\r
414 \r
415         /* Retrieve from isf_tmp[][] the ISF vectors saved prior to averaging */\r
416         for (k = 0; k < 2; k++)\r
417         {\r
418                 if ((indices[k] + 1) != 0)\r
419                 {\r
420                         for (i = 0; i < M; i++)\r
421                         {\r
422                                 isf_old[indices[k] * M + i] = isf_tmp[k * M + i];\r
423                         }\r
424                 }\r
425         }\r
426 \r
427         return;\r
428 }\r
429 \r
430 static void find_frame_indices(\r
431                 Word16 isf_old_tx[],\r
432                 Word16 indices[],\r
433                 dtx_encState * st\r
434                 )\r
435 {\r
436         Word32 L_tmp, summin, summax, summax2nd;\r
437         Word16 i, j, tmp;\r
438         Word16 ptr;\r
439 \r
440         /* Remove the effect of the oldest frame from the column */\r
441         /* sum sumD[0..DTX_HIST_SIZE-1]. sumD[DTX_HIST_SIZE] is    */\r
442         /* not updated since it will be removed later.           */\r
443 \r
444         tmp = DTX_HIST_SIZE_MIN_ONE;           \r
445         j = -1;                                \r
446         for (i = 0; i < DTX_HIST_SIZE_MIN_ONE; i++)\r
447         {\r
448                 j = add(j, tmp);\r
449                 st->sumD[i] = L_sub(st->sumD[i], st->D[j]);     \r
450                 tmp = sub(tmp, 1);\r
451         }\r
452 \r
453         /* Shift the column sum sumD. The element sumD[DTX_HIST_SIZE-1]    */\r
454         /* corresponding to the oldest frame is removed. The sum of     */\r
455         /* the distances between the latest isf and other isfs, */\r
456         /* i.e. the element sumD[0], will be computed during this call. */\r
457         /* Hence this element is initialized to zero.                   */\r
458 \r
459         for (i = DTX_HIST_SIZE_MIN_ONE; i > 0; i--)\r
460         {\r
461                 st->sumD[i] = st->sumD[i - 1];     \r
462         }\r
463         st->sumD[0] = 0;                       \r
464 \r
465         /* Remove the oldest frame from the distance matrix.           */\r
466         /* Note that the distance matrix is replaced by a one-         */\r
467         /* dimensional array to save static memory.                    */\r
468 \r
469         tmp = 0;                               \r
470         for (i = 27; i >= 12; i = (Word16) (i - tmp))\r
471         {\r
472                 tmp = add(tmp, 1);\r
473                 for (j = tmp; j > 0; j--)\r
474                 {\r
475                         st->D[i - j + 1] = st->D[i - j - tmp];   \r
476                 }\r
477         }\r
478 \r
479         /* Compute the first column of the distance matrix D            */\r
480         /* (squared Euclidean distances from isf1[] to isf_old_tx[][]). */\r
481 \r
482         ptr = st->hist_ptr;                 \r
483         for (i = 1; i < DTX_HIST_SIZE; i++)\r
484         {\r
485                 /* Compute the distance between the latest isf and the other isfs. */\r
486                 ptr = sub(ptr, 1);\r
487                 if (ptr < 0)\r
488                 {\r
489                         ptr = DTX_HIST_SIZE_MIN_ONE;   \r
490                 }\r
491                 L_tmp = 0;                         \r
492                 for (j = 0; j < M; j++)\r
493                 {\r
494                         tmp = sub(isf_old_tx[st->hist_ptr * M + j], isf_old_tx[ptr * M + j]);\r
495                         L_tmp = L_mac(L_tmp, tmp, tmp);\r
496                 }\r
497                 st->D[i - 1] = L_tmp;           \r
498 \r
499                 /* Update also the column sums. */\r
500                 st->sumD[0] = L_add(st->sumD[0], st->D[i - 1]); \r
501                 st->sumD[i] = L_add(st->sumD[i], st->D[i - 1]); \r
502         }\r
503 \r
504         /* Find the minimum and maximum distances */\r
505         summax = st->sumD[0];                  \r
506         summin = st->sumD[0];                  \r
507         indices[0] = 0;                        \r
508         indices[2] = 0;                        \r
509         for (i = 1; i < DTX_HIST_SIZE; i++)\r
510         {\r
511                 if (L_sub(st->sumD[i], summax) > 0)\r
512                 {\r
513                         indices[0] = i;                \r
514                         summax = st->sumD[i];          \r
515                 }\r
516                 if (L_sub(st->sumD[i], summin) < 0)\r
517                 {\r
518                         indices[2] = i;                \r
519                         summin = st->sumD[i];          \r
520                 }\r
521         }\r
522 \r
523         /* Find the second largest distance */\r
524         summax2nd = -2147483647L;              \r
525         indices[1] = -1;                       \r
526         for (i = 0; i < DTX_HIST_SIZE; i++)\r
527         {\r
528                 if ((L_sub(st->sumD[i], summax2nd) > 0) && (sub(i, indices[0]) != 0))\r
529                 {\r
530                         indices[1] = i;                \r
531                         summax2nd = st->sumD[i];       \r
532                 }\r
533         }\r
534 \r
535         for (i = 0; i < 3; i++)\r
536         {\r
537                 indices[i] = sub(st->hist_ptr, indices[i]);     \r
538                 if (indices[i] < 0)\r
539                 {\r
540                         indices[i] = add(indices[i], DTX_HIST_SIZE);       \r
541                 }\r
542         }\r
543 \r
544         /* If maximum distance/MED_THRESH is smaller than minimum distance */\r
545         /* then the median ISF vector replacement is not performed         */\r
546         tmp = norm_l(summax);\r
547         summax = (summax << tmp);\r
548         summin = (summin << tmp);\r
549         L_tmp = L_mult(voround(summax), INV_MED_THRESH);\r
550         if(L_tmp <= summin)\r
551         {\r
552                 indices[0] = -1; \r
553         }\r
554         /* If second largest distance/MED_THRESH is smaller than     */\r
555         /* minimum distance then the median ISF vector replacement is    */\r
556         /* not performed                                                 */\r
557         summax2nd = L_shl(summax2nd, tmp);\r
558         L_tmp = L_mult(voround(summax2nd), INV_MED_THRESH);\r
559         if(L_tmp <= summin)\r
560         {\r
561                 indices[1] = -1;                 \r
562         }\r
563         return;\r
564 }\r
565 \r
566 static Word16 dithering_control(\r
567                 dtx_encState * st\r
568                 )\r
569 {\r
570         Word16 tmp, mean, CN_dith, gain_diff;\r
571         Word32 i, ISF_diff;\r
572 \r
573         /* determine how stationary the spectrum of background noise is */\r
574         ISF_diff = 0;\r
575         for (i = 0; i < 8; i++)\r
576         {\r
577                 ISF_diff = L_add(ISF_diff, st->sumD[i]);\r
578         }\r
579         if ((ISF_diff >> 26) > 0)\r
580         {\r
581                 CN_dith = 1;\r
582         } else\r
583         {\r
584                 CN_dith = 0;\r
585         }\r
586 \r
587         /* determine how stationary the energy of background noise is */\r
588         mean = 0;\r
589         for (i = 0; i < DTX_HIST_SIZE; i++)\r
590         {\r
591                 mean = add(mean, st->log_en_hist[i]);\r
592         }\r
593         mean = (mean >> 3);\r
594         gain_diff = 0;\r
595         for (i = 0; i < DTX_HIST_SIZE; i++)\r
596         {\r
597                 tmp = abs_s(sub(st->log_en_hist[i], mean));\r
598                 gain_diff = add(gain_diff, tmp);\r
599         }\r
600         if (gain_diff > GAIN_THR)\r
601         {\r
602                 CN_dith = 1;\r
603         }\r
604         return CN_dith;\r
605 }\r