Mode doc
[opus.git] / libcelt / celt.c
1 /* (C) 2007 Jean-Marc Valin, CSIRO
2 */
3 /*
4    Redistribution and use in source and binary forms, with or without
5    modification, are permitted provided that the following conditions
6    are met:
7    
8    - Redistributions of source code must retain the above copyright
9    notice, this list of conditions and the following disclaimer.
10    
11    - Redistributions in binary form must reproduce the above copyright
12    notice, this list of conditions and the following disclaimer in the
13    documentation and/or other materials provided with the distribution.
14    
15    - Neither the name of the Xiph.org Foundation nor the names of its
16    contributors may be used to endorse or promote products derived from
17    this software without specific prior written permission.
18    
19    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
23    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 #include "os_support.h"
37 #include "mdct.h"
38 #include <math.h>
39 #include "celt.h"
40 #include "pitch.h"
41 #include "kiss_fftr.h"
42 #include "bands.h"
43 #include "modes.h"
44 #include "entcode.h"
45 #include "quant_pitch.h"
46 #include "quant_bands.h"
47 #include "psy.h"
48 #include "rate.h"
49
50 #define MAX_PERIOD 1024
51
52 #ifndef M_PI
53 #define M_PI 3.14159263
54 #endif
55
56 /** Encoder state 
57  @brief Encoder state
58  */
59 struct CELTEncoder {
60    const CELTMode *mode;     /**< Mode used by the encoder */
61    int frame_size;
62    int block_size;
63    int nb_blocks;
64    int overlap;
65    int channels;
66    int Fs;
67    
68    ec_byte_buffer buf;
69    ec_enc         enc;
70
71    float preemph;
72    float *preemph_memE;
73    float *preemph_memD;
74    
75    mdct_lookup mdct_lookup;
76    kiss_fftr_cfg fft;
77    struct PsyDecay psy;
78    
79    float *window;
80    float *in_mem;
81    float *mdct_overlap;
82    float *out_mem;
83
84    float *oldBandE;
85 };
86
87
88
89 CELTEncoder *celt_encoder_create(const CELTMode *mode)
90 {
91    int i, N, B, C, N4;
92    CELTEncoder *st;
93    N = mode->mdctSize;
94    B = mode->nbMdctBlocks;
95    C = mode->nbChannels;
96    st = celt_alloc(sizeof(CELTEncoder));
97    
98    st->mode = mode;
99    st->frame_size = B*N;
100    st->block_size = N;
101    st->nb_blocks  = B;
102    st->overlap = mode->overlap;
103    st->Fs = 44100;
104
105    N4 = (N-st->overlap)/2;
106    ec_byte_writeinit(&st->buf);
107    ec_enc_init(&st->enc,&st->buf);
108
109    mdct_init(&st->mdct_lookup, 2*N);
110    st->fft = kiss_fftr_alloc(MAX_PERIOD*C, 0, 0);
111    psydecay_init(&st->psy, MAX_PERIOD*C/2, st->Fs);
112    
113    st->window = celt_alloc(2*N*sizeof(float));
114    st->in_mem = celt_alloc(N*C*sizeof(float));
115    st->mdct_overlap = celt_alloc(N*C*sizeof(float));
116    st->out_mem = celt_alloc(MAX_PERIOD*C*sizeof(float));
117    for (i=0;i<2*N;i++)
118       st->window[i] = 0;
119    for (i=0;i<st->overlap;i++)
120       st->window[N4+i] = st->window[2*N-N4-i-1] 
121             = sin(.5*M_PI* sin(.5*M_PI*(i+.5)/st->overlap) * sin(.5*M_PI*(i+.5)/st->overlap));
122    for (i=0;i<2*N4;i++)
123       st->window[N-N4+i] = 1;
124    st->oldBandE = celt_alloc(C*mode->nbEBands*sizeof(float));
125
126    st->preemph = 0.8;
127    st->preemph_memE = celt_alloc(C*sizeof(float));;
128    st->preemph_memD = celt_alloc(C*sizeof(float));;
129
130    return st;
131 }
132
133 void celt_encoder_destroy(CELTEncoder *st)
134 {
135    if (st == NULL)
136    {
137       celt_warning("NULL passed to celt_encoder_destroy");
138       return;
139    }
140    ec_byte_writeclear(&st->buf);
141
142    mdct_clear(&st->mdct_lookup);
143    kiss_fft_free(st->fft);
144    psydecay_clear(&st->psy);
145
146    celt_free(st->window);
147    celt_free(st->in_mem);
148    celt_free(st->mdct_overlap);
149    celt_free(st->out_mem);
150    
151    celt_free(st->oldBandE);
152    
153    celt_free(st->preemph_memE);
154    celt_free(st->preemph_memD);
155    
156    celt_free(st);
157 }
158
159
160 static float compute_mdcts(mdct_lookup *mdct_lookup, float *window, float *in, float *out, int N, int B, int C)
161 {
162    int i, c;
163    float E = 1e-15;
164    VARDECL(float *x);
165    VARDECL(float *tmp);
166    ALLOC(x, 2*N, float);
167    ALLOC(tmp, N, float);
168    for (c=0;c<C;c++)
169    {
170       for (i=0;i<B;i++)
171       {
172          int j;
173          for (j=0;j<2*N;j++)
174          {
175             x[j] = window[j]*in[C*i*N+C*j+c];
176             E += x[j]*x[j];
177          }
178          mdct_forward(mdct_lookup, x, tmp);
179          /* Interleaving the sub-frames */
180          for (j=0;j<N;j++)
181             out[C*B*j+C*i+c] = tmp[j];
182       }
183    }
184    return E;
185 }
186
187 static void compute_inv_mdcts(mdct_lookup *mdct_lookup, float *window, float *X, float *out_mem, float *mdct_overlap, int N, int overlap, int B, int C)
188 {
189    int i, c, N4;
190    VARDECL(float *x);
191    VARDECL(float *tmp);
192    ALLOC(x, 2*N, float);
193    ALLOC(tmp, N, float);
194    N4 = (N-overlap)/2;
195    for (c=0;c<C;c++)
196    {
197       for (i=0;i<B;i++)
198       {
199          int j;
200          /* De-interleaving the sub-frames */
201          for (j=0;j<N;j++)
202             tmp[j] = X[C*B*j+C*i+c];
203          mdct_backward(mdct_lookup, tmp, x);
204          for (j=0;j<2*N;j++)
205             x[j] = window[j]*x[j];
206          for (j=0;j<overlap;j++)
207             out_mem[C*(MAX_PERIOD+(i-B)*N)+C*j+c] = x[N4+j]+mdct_overlap[C*j+c];
208          for (j=0;j<2*N4;j++)
209             out_mem[C*(MAX_PERIOD+(i-B)*N)+C*(j+overlap)+c] = x[j+N4+overlap];
210          for (j=0;j<overlap;j++)
211             mdct_overlap[C*j+c] = x[N+N4+j];
212       }
213    }
214 }
215
216 int celt_encode(CELTEncoder *st, celt_int16_t *pcm, unsigned char *compressed, int nbCompressedBytes)
217 {
218    int i, c, N, B, C, N4;
219    int has_pitch;
220    int pitch_index;
221    float curr_power, pitch_power;
222    VARDECL(float *in);
223    VARDECL(float *X);
224    VARDECL(float *P);
225    VARDECL(float *mask);
226    VARDECL(float *bandE);
227    VARDECL(float *gains);
228    N = st->block_size;
229    B = st->nb_blocks;
230    C = st->mode->nbChannels;
231    ALLOC(in, (B+1)*C*N, float);
232    ALLOC(X, B*C*N, float);         /**< Interleaved signal MDCTs */
233    ALLOC(P, B*C*N, float);         /**< Interleaved pitch MDCTs*/
234    ALLOC(mask, B*C*N, float);      /**< Masking curve */
235    ALLOC(bandE,st->mode->nbEBands*C, float);
236    ALLOC(gains,st->mode->nbPBands, float);
237    
238    N4 = (N-st->overlap)/2;
239
240    for (c=0;c<C;c++)
241    {
242       for (i=0;i<N4;i++)
243          in[C*i+c] = 0;
244       for (i=0;i<st->overlap;i++)
245          in[C*(i+N4)+c] = st->in_mem[C*i+c];
246       for (i=0;i<B*N;i++)
247       {
248          float tmp = pcm[C*i+c];
249          in[C*(i+st->overlap+N4)+c] = tmp - st->preemph*st->preemph_memE[c];
250          st->preemph_memE[c] = tmp;
251       }
252       for (i=N*(B+1)-N4;i<N*(B+1);i++)
253          in[C*i+c] = 0;
254       for (i=0;i<st->overlap;i++)
255          st->in_mem[C*i+c] = in[C*(N*(B+1)-N4-st->overlap+i)+c];
256    }
257    /*for (i=0;i<(B+1)*C*N;i++) printf ("%f(%d) ", in[i], i); printf ("\n");*/
258    /* Compute MDCTs */
259    curr_power = compute_mdcts(&st->mdct_lookup, st->window, in, X, N, B, C);
260
261 #if 0 /* Mask disabled until it can be made to do something useful */
262    compute_mdct_masking(X, mask, B*C*N, st->Fs);
263
264    /* Invert and stretch the mask to length of X 
265       For some reason, I get better results by using the sqrt instead,
266       although there's no valid reason to. Must investigate further */
267    for (i=0;i<B*C*N;i++)
268       mask[i] = 1/(.1+mask[i]);
269 #else
270    for (i=0;i<B*C*N;i++)
271       mask[i] = 1;
272 #endif
273    /* Pitch analysis */
274    for (c=0;c<C;c++)
275    {
276       for (i=0;i<N;i++)
277       {
278          in[C*i+c] *= st->window[i];
279          in[C*(B*N+i)+c] *= st->window[N+i];
280       }
281    }
282    find_spectral_pitch(st->fft, &st->psy, in, st->out_mem, MAX_PERIOD, (B+1)*N, C, &pitch_index);
283    
284    /* Compute MDCTs of the pitch part */
285    pitch_power = compute_mdcts(&st->mdct_lookup, st->window, st->out_mem+pitch_index*C, P, N, B, C);
286    
287    /*printf ("%f %f\n", curr_power, pitch_power);*/
288    /*int j;
289    for (j=0;j<B*N;j++)
290       printf ("%f ", X[j]);
291    for (j=0;j<B*N;j++)
292       printf ("%f ", P[j]);
293    printf ("\n");*/
294
295    /* Band normalisation */
296    compute_band_energies(st->mode, X, bandE);
297    normalise_bands(st->mode, X, bandE);
298    /*for (i=0;i<st->mode->nbEBands;i++)printf("%f ", bandE[i]);printf("\n");*/
299    /*for (i=0;i<N*B*C;i++)printf("%f ", X[i]);printf("\n");*/
300
301    quant_energy(st->mode, bandE, st->oldBandE, nbCompressedBytes*8/3, &st->enc);
302
303    if (C==2)
304    {
305       stereo_mix(st->mode, X, bandE, 1);
306    }
307
308    /* Check if we can safely use the pitch (i.e. effective gain isn't too high) */
309    if (curr_power + 1e5f < 10.f*pitch_power)
310    {
311       /* Normalise the pitch vector as well (discard the energies) */
312       VARDECL(float *bandEp);
313       ALLOC(bandEp, st->mode->nbEBands*st->mode->nbChannels, float);
314       compute_band_energies(st->mode, P, bandEp);
315       normalise_bands(st->mode, P, bandEp);
316
317       if (C==2)
318          stereo_mix(st->mode, P, bandE, 1);
319       /* Simulates intensity stereo */
320       /*for (i=30;i<N*B;i++)
321          X[i*C+1] = P[i*C+1] = 0;*/
322
323       /* Pitch prediction */
324       compute_pitch_gain(st->mode, X, P, gains, bandE);
325       has_pitch = quant_pitch(gains, st->mode->nbPBands, &st->enc);
326       if (has_pitch)
327          ec_enc_uint(&st->enc, pitch_index, MAX_PERIOD-(B+1)*N);
328    } else {
329       /* No pitch, so we just pretend we found a gain of zero */
330       for (i=0;i<st->mode->nbPBands;i++)
331          gains[i] = 0;
332       ec_enc_uint(&st->enc, 0, 128);
333       for (i=0;i<B*C*N;i++)
334          P[i] = 0;
335    }
336    
337
338    pitch_quant_bands(st->mode, X, P, gains);
339
340    /*for (i=0;i<B*N;i++) printf("%f ",P[i]);printf("\n");*/
341    /* Compute residual that we're going to encode */
342    for (i=0;i<B*C*N;i++)
343       X[i] -= P[i];
344
345    /*float sum=0;
346    for (i=0;i<B*N;i++)
347       sum += X[i]*X[i];
348    printf ("%f\n", sum);*/
349    /* Residual quantisation */
350    quant_bands(st->mode, X, P, mask, nbCompressedBytes*8, &st->enc);
351    
352    if (C==2)
353       stereo_mix(st->mode, X, bandE, -1);
354
355    renormalise_bands(st->mode, X);
356    /* Synthesis */
357    denormalise_bands(st->mode, X, bandE);
358
359
360    CELT_MOVE(st->out_mem, st->out_mem+C*B*N, C*(MAX_PERIOD-B*N));
361
362    compute_inv_mdcts(&st->mdct_lookup, st->window, X, st->out_mem, st->mdct_overlap, N, st->overlap, B, C);
363    /* De-emphasis and put everything back at the right place in the synthesis history */
364    for (c=0;c<C;c++)
365    {
366       for (i=0;i<B;i++)
367       {
368          int j;
369          for (j=0;j<N;j++)
370          {
371             float tmp = st->out_mem[C*(MAX_PERIOD+(i-B)*N)+C*j+c] + st->preemph*st->preemph_memD[c];
372             st->preemph_memD[c] = tmp;
373             if (tmp > 32767) tmp = 32767;
374             if (tmp < -32767) tmp = -32767;
375             pcm[C*i*N+C*j+c] = (short)floor(.5+tmp);
376          }
377       }
378    }
379    
380    if (ec_enc_tell(&st->enc, 0) < nbCompressedBytes*8 - 7)
381       celt_warning_int ("many unused bits: ", nbCompressedBytes*8-ec_enc_tell(&st->enc, 0));
382    /*printf ("%d\n", ec_enc_tell(&st->enc, 0)-8*nbCompressedBytes);*/
383    /* Finishing the stream with a 0101... pattern so that the decoder can check is everything's right */
384    {
385       int val = 0;
386       while (ec_enc_tell(&st->enc, 0) < nbCompressedBytes*8)
387       {
388          ec_enc_uint(&st->enc, val, 2);
389          val = 1-val;
390       }
391    }
392    ec_enc_done(&st->enc);
393    {
394       unsigned char *data;
395       int nbBytes = ec_byte_bytes(&st->buf);
396       if (nbBytes > nbCompressedBytes)
397       {
398          celt_warning_int ("got too many bytes:", nbBytes);
399          return CELT_INTERNAL_ERROR;
400       }
401       /*printf ("%d\n", *nbBytes);*/
402       data = ec_byte_get_buffer(&st->buf);
403       for (i=0;i<nbBytes;i++)
404          compressed[i] = data[i];
405       for (;i<nbCompressedBytes;i++)
406          compressed[i] = 0;
407    }
408    /* Reset the packing for the next encoding */
409    ec_byte_reset(&st->buf);
410    ec_enc_init(&st->enc,&st->buf);
411
412    return nbCompressedBytes;
413 }
414
415
416 /****************************************************************************/
417 /*                                                                          */
418 /*                                DECODER                                   */
419 /*                                                                          */
420 /****************************************************************************/
421
422
423 /** Decoder state 
424  @brief Decoder state
425  */
426 struct CELTDecoder {
427    const CELTMode *mode;
428    int frame_size;
429    int block_size;
430    int nb_blocks;
431    int overlap;
432
433    ec_byte_buffer buf;
434    ec_enc         enc;
435
436    float preemph;
437    float *preemph_memD;
438    
439    mdct_lookup mdct_lookup;
440    
441    float *window;
442    float *mdct_overlap;
443    float *out_mem;
444
445    float *oldBandE;
446    
447    int last_pitch_index;
448 };
449
450 CELTDecoder *celt_decoder_create(const CELTMode *mode)
451 {
452    int i, N, B, C, N4;
453    CELTDecoder *st;
454    N = mode->mdctSize;
455    B = mode->nbMdctBlocks;
456    C = mode->nbChannels;
457    st = celt_alloc(sizeof(CELTDecoder));
458    
459    st->mode = mode;
460    st->frame_size = B*N;
461    st->block_size = N;
462    st->nb_blocks  = B;
463    st->overlap = mode->overlap;
464
465    N4 = (N-st->overlap)/2;
466    
467    mdct_init(&st->mdct_lookup, 2*N);
468    
469    st->window = celt_alloc(2*N*sizeof(float));
470    st->mdct_overlap = celt_alloc(N*C*sizeof(float));
471    st->out_mem = celt_alloc(MAX_PERIOD*C*sizeof(float));
472
473    for (i=0;i<2*N;i++)
474       st->window[i] = 0;
475    for (i=0;i<st->overlap;i++)
476       st->window[N4+i] = st->window[2*N-N4-i-1] 
477             = sin(.5*M_PI* sin(.5*M_PI*(i+.5)/st->overlap) * sin(.5*M_PI*(i+.5)/st->overlap));
478    for (i=0;i<2*N4;i++)
479       st->window[N-N4+i] = 1;
480    
481    st->oldBandE = celt_alloc(C*mode->nbEBands*sizeof(float));
482
483    st->preemph = 0.8;
484    st->preemph_memD = celt_alloc(C*sizeof(float));;
485
486    st->last_pitch_index = 0;
487    return st;
488 }
489
490 void celt_decoder_destroy(CELTDecoder *st)
491 {
492    if (st == NULL)
493    {
494       celt_warning("NULL passed to celt_encoder_destroy");
495       return;
496    }
497
498    mdct_clear(&st->mdct_lookup);
499
500    celt_free(st->window);
501    celt_free(st->mdct_overlap);
502    celt_free(st->out_mem);
503    
504    celt_free(st->oldBandE);
505    
506    celt_free(st->preemph_memD);
507
508    celt_free(st);
509 }
510
511 static void celt_decode_lost(CELTDecoder *st, short *pcm)
512 {
513    int i, c, N, B, C;
514    int pitch_index;
515    VARDECL(float *X);
516    N = st->block_size;
517    B = st->nb_blocks;
518    C = st->mode->nbChannels;
519    ALLOC(X,C*B*N, float);         /**< Interleaved signal MDCTs */
520    
521    pitch_index = st->last_pitch_index;
522    
523    /* Use the pitch MDCT as the "guessed" signal */
524    compute_mdcts(&st->mdct_lookup, st->window, st->out_mem+pitch_index*C, X, N, B, C);
525
526    CELT_MOVE(st->out_mem, st->out_mem+C*B*N, C*(MAX_PERIOD-B*N));
527    /* Compute inverse MDCTs */
528    compute_inv_mdcts(&st->mdct_lookup, st->window, X, st->out_mem, st->mdct_overlap, N, st->overlap, B, C);
529
530    for (c=0;c<C;c++)
531    {
532       for (i=0;i<B;i++)
533       {
534          int j;
535          for (j=0;j<N;j++)
536          {
537             float tmp = st->out_mem[C*(MAX_PERIOD+(i-B)*N)+C*j+c] + st->preemph*st->preemph_memD[c];
538             st->preemph_memD[c] = tmp;
539             if (tmp > 32767) tmp = 32767;
540             if (tmp < -32767) tmp = -32767;
541             pcm[C*i*N+C*j+c] = (short)floor(.5+tmp);
542          }
543       }
544    }
545 }
546
547 int celt_decode(CELTDecoder *st, unsigned char *data, int len, celt_int16_t *pcm)
548 {
549    int i, c, N, B, C;
550    int has_pitch;
551    int pitch_index;
552    ec_dec dec;
553    ec_byte_buffer buf;
554    VARDECL(float *X);
555    VARDECL(float *P);
556    VARDECL(float *bandE);
557    VARDECL(float *gains);
558    N = st->block_size;
559    B = st->nb_blocks;
560    C = st->mode->nbChannels;
561    
562    ALLOC(X, C*B*N, float);         /**< Interleaved signal MDCTs */
563    ALLOC(P, C*B*N, float);         /**< Interleaved pitch MDCTs*/
564    ALLOC(bandE, st->mode->nbEBands*C, float);
565    ALLOC(gains, st->mode->nbPBands, float);
566    
567    if (data == NULL)
568    {
569       celt_decode_lost(st, pcm);
570       return 0;
571    }
572    
573    ec_byte_readinit(&buf,data,len);
574    ec_dec_init(&dec,&buf);
575    
576    /* Get band energies */
577    unquant_energy(st->mode, bandE, st->oldBandE, len*8/3, &dec);
578    
579    /* Get the pitch gains */
580    has_pitch = unquant_pitch(gains, st->mode->nbPBands, &dec);
581    
582    /* Get the pitch index */
583    if (has_pitch)
584    {
585       pitch_index = ec_dec_uint(&dec, MAX_PERIOD-(B+1)*N);
586       st->last_pitch_index = pitch_index;
587    } else {
588       /* FIXME: We could be more intelligent here and just not compute the MDCT */
589       pitch_index = 0;
590    }
591    
592    /* Pitch MDCT */
593    compute_mdcts(&st->mdct_lookup, st->window, st->out_mem+pitch_index*C, P, N, B, C);
594
595    {
596       VARDECL(float *bandEp);
597       ALLOC(bandEp, st->mode->nbEBands*C, float);
598       compute_band_energies(st->mode, P, bandEp);
599       normalise_bands(st->mode, P, bandEp);
600    }
601
602    if (C==2)
603       stereo_mix(st->mode, P, bandE, 1);
604
605    /* Apply pitch gains */
606    pitch_quant_bands(st->mode, X, P, gains);
607
608    /* Decode fixed codebook and merge with pitch */
609    unquant_bands(st->mode, X, P, len*8, &dec);
610
611    if (C==2)
612       stereo_mix(st->mode, X, bandE, -1);
613
614    renormalise_bands(st->mode, X);
615    
616    /* Synthesis */
617    denormalise_bands(st->mode, X, bandE);
618
619
620    CELT_MOVE(st->out_mem, st->out_mem+C*B*N, C*(MAX_PERIOD-B*N));
621    /* Compute inverse MDCTs */
622    compute_inv_mdcts(&st->mdct_lookup, st->window, X, st->out_mem, st->mdct_overlap, N, st->overlap, B, C);
623
624    for (c=0;c<C;c++)
625    {
626       for (i=0;i<B;i++)
627       {
628          int j;
629          for (j=0;j<N;j++)
630          {
631             float tmp = st->out_mem[C*(MAX_PERIOD+(i-B)*N)+C*j+c] + st->preemph*st->preemph_memD[c];
632             st->preemph_memD[c] = tmp;
633             if (tmp > 32767) tmp = 32767;
634             if (tmp < -32767) tmp = -32767;
635             pcm[C*i*N+C*j+c] = (short)floor(.5+tmp);
636          }
637       }
638    }
639
640    {
641       int val = 0;
642       while (ec_dec_tell(&dec, 0) < len*8)
643       {
644          if (ec_dec_uint(&dec, 2) != val)
645          {
646             celt_warning("decode error");
647             return CELT_CORRUPTED_DATA;
648          }
649          val = 1-val;
650       }
651    }
652
653    return 0;
654    /*printf ("\n");*/
655 }
656