res_pjsip_session.c: Fix build when TEST_FRAMEWORK is not defined
[asterisk/asterisk.git] / codecs / codec_speex.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19
20 /*! \file
21  *
22  * \brief Translate between signed linear and Speex (Open Codec)
23  *
24  * \note This work was motivated by Jeremy McNamara
25  * hacked to be configurable by anthm and bkw 9/28/2004
26  *
27  * \ingroup codecs
28  *
29  * The Speex library - http://www.speex.org
30  *
31  */
32
33 /*** MODULEINFO
34         <depend>speex</depend>
35         <depend>speex_preprocess</depend>
36         <use type="external">speexdsp</use>
37         <support_level>core</support_level>
38  ***/
39
40 #include "asterisk.h"
41
42 #include <speex/speex.h>
43
44 /* We require a post 1.1.8 version of Speex to enable preprocessing
45  * and better type handling
46  */
47 #ifdef _SPEEX_TYPES_H
48 #include <speex/speex_preprocess.h>
49 #endif
50
51 #include "asterisk/translate.h"
52 #include "asterisk/module.h"
53 #include "asterisk/config.h"
54 #include "asterisk/utils.h"
55 #include "asterisk/frame.h"
56 #include "asterisk/linkedlists.h"
57
58 /* For struct ast_rtp_rtcp_report and struct ast_rtp_rtcp_report_block */
59 #include "asterisk/rtp_engine.h"
60
61 /* codec variables */
62 static int quality = 3;
63 static int complexity = 2;
64 static int enhancement = 0;
65 static int vad = 0;
66 static int vbr = 0;
67 static float vbr_quality = 4;
68 static int abr = 0;
69 static int dtx = 0;     /* set to 1 to enable silence detection */
70 static int exp_rtcp_fb = 0;     /* set to 1 to use experimental RTCP feedback for changing bitrate */
71
72 static int preproc = 0;
73 static int pp_vad = 0;
74 static int pp_agc = 0;
75 static float pp_agc_level = 8000; /* XXX what is this 8000 ? */
76 static int pp_denoise = 0;
77 static int pp_dereverb = 0;
78 static float pp_dereverb_decay = 0.4;
79 static float pp_dereverb_level = 0.3;
80
81 #define TYPE_SILENCE     0x2
82 #define TYPE_HIGH        0x0
83 #define TYPE_LOW         0x1
84 #define TYPE_MASK        0x3
85
86 #define BUFFER_SAMPLES  8000
87 #define SPEEX_SAMPLES   160
88
89 /* Sample frame data */
90 #include "asterisk/slin.h"
91 #include "ex_speex.h"
92
93 struct speex_coder_pvt {
94         void *speex;
95         SpeexBits bits;
96         int framesize;
97         int silent_state;
98
99         int fraction_lost;
100         int quality;
101         int default_quality;
102
103 #ifdef _SPEEX_TYPES_H
104         SpeexPreprocessState *pp;
105         spx_int16_t buf[BUFFER_SAMPLES];
106 #else
107         int16_t buf[BUFFER_SAMPLES];    /* input, waiting to be compressed */
108 #endif
109 };
110
111 static int speex_encoder_construct(struct ast_trans_pvt *pvt, const SpeexMode *profile, int sampling_rate)
112 {
113         struct speex_coder_pvt *tmp = pvt->pvt;
114
115         if (!(tmp->speex = speex_encoder_init(profile)))
116                 return -1;
117
118         speex_bits_init(&tmp->bits);
119         speex_bits_reset(&tmp->bits);
120         speex_encoder_ctl(tmp->speex, SPEEX_GET_FRAME_SIZE, &tmp->framesize);
121         speex_encoder_ctl(tmp->speex, SPEEX_SET_COMPLEXITY, &complexity);
122 #ifdef _SPEEX_TYPES_H
123         if (preproc) {
124                 tmp->pp = speex_preprocess_state_init(tmp->framesize, sampling_rate);
125                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_VAD, &pp_vad);
126                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_AGC, &pp_agc);
127                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_AGC_LEVEL, &pp_agc_level);
128                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_DENOISE, &pp_denoise);
129                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_DEREVERB, &pp_dereverb);
130                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &pp_dereverb_decay);
131                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &pp_dereverb_level);
132         }
133 #endif
134         if (!abr && !vbr) {
135                 speex_encoder_ctl(tmp->speex, SPEEX_SET_QUALITY, &quality);
136                 if (vad)
137                         speex_encoder_ctl(tmp->speex, SPEEX_SET_VAD, &vad);
138         }
139         if (vbr) {
140                 speex_encoder_ctl(tmp->speex, SPEEX_SET_VBR, &vbr);
141                 speex_encoder_ctl(tmp->speex, SPEEX_SET_VBR_QUALITY, &vbr_quality);
142         }
143         if (abr)
144                 speex_encoder_ctl(tmp->speex, SPEEX_SET_ABR, &abr);
145         if (dtx)
146                 speex_encoder_ctl(tmp->speex, SPEEX_SET_DTX, &dtx);
147         tmp->silent_state = 0;
148
149         tmp->fraction_lost = 0;
150         tmp->default_quality = vbr ? vbr_quality : quality;
151         tmp->quality = tmp->default_quality;
152         ast_debug(3, "Default quality (%s): %d\n", vbr ? "vbr" : "cbr", tmp->default_quality);
153
154         return 0;
155 }
156
157 static int lintospeex_new(struct ast_trans_pvt *pvt)
158 {
159         return speex_encoder_construct(pvt, &speex_nb_mode, 8000);
160 }
161
162 static int lin16tospeexwb_new(struct ast_trans_pvt *pvt)
163 {
164         return speex_encoder_construct(pvt, &speex_wb_mode, 16000);
165 }
166
167 static int lin32tospeexuwb_new(struct ast_trans_pvt *pvt)
168 {
169         return speex_encoder_construct(pvt, &speex_uwb_mode, 32000);
170 }
171
172 static int speex_decoder_construct(struct ast_trans_pvt *pvt, const SpeexMode *profile)
173 {
174         struct speex_coder_pvt *tmp = pvt->pvt;
175
176         if (!(tmp->speex = speex_decoder_init(profile)))
177                 return -1;
178
179         speex_bits_init(&tmp->bits);
180         speex_decoder_ctl(tmp->speex, SPEEX_GET_FRAME_SIZE, &tmp->framesize);
181         if (enhancement)
182                 speex_decoder_ctl(tmp->speex, SPEEX_SET_ENH, &enhancement);
183
184         return 0;
185 }
186
187 static int speextolin_new(struct ast_trans_pvt *pvt)
188 {
189         return speex_decoder_construct(pvt, &speex_nb_mode);
190 }
191
192 static int speexwbtolin16_new(struct ast_trans_pvt *pvt)
193 {
194         return speex_decoder_construct(pvt, &speex_wb_mode);
195 }
196
197 static int speexuwbtolin32_new(struct ast_trans_pvt *pvt)
198 {
199         return speex_decoder_construct(pvt, &speex_uwb_mode);
200 }
201
202 /*! \brief convert and store into outbuf */
203 static int speextolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
204 {
205         struct speex_coder_pvt *tmp = pvt->pvt;
206
207         /* Assuming there's space left, decode into the current buffer at
208            the tail location.  Read in as many frames as there are */
209         int x;
210         int res;
211         int16_t *dst = pvt->outbuf.i16;
212         /* XXX fout is a temporary buffer, may have different types */
213 #ifdef _SPEEX_TYPES_H
214         spx_int16_t fout[1024];
215 #else
216         float fout[1024];
217 #endif
218
219         if (f->datalen == 0) {  /* Native PLC interpolation */
220                 if (pvt->samples + tmp->framesize > BUFFER_SAMPLES) {
221                         ast_log(LOG_WARNING, "Out of buffer space\n");
222                         return -1;
223                 }
224 #ifdef _SPEEX_TYPES_H
225                 speex_decode_int(tmp->speex, NULL, dst + pvt->samples);
226 #else
227                 speex_decode(tmp->speex, NULL, fout);
228                 for (x=0;x<tmp->framesize;x++) {
229                         dst[pvt->samples + x] = (int16_t)fout[x];
230                 }
231 #endif
232                 pvt->samples += tmp->framesize;
233                 pvt->datalen += 2 * tmp->framesize; /* 2 bytes/sample */
234                 return 0;
235         }
236
237         /* Read in bits */
238         speex_bits_read_from(&tmp->bits, f->data.ptr, f->datalen);
239         for (;;) {
240 #ifdef _SPEEX_TYPES_H
241                 res = speex_decode_int(tmp->speex, &tmp->bits, fout);
242 #else
243                 res = speex_decode(tmp->speex, &tmp->bits, fout);
244 #endif
245                 if (res < 0)
246                         break;
247                 if (pvt->samples + tmp->framesize > BUFFER_SAMPLES) {
248                         ast_log(LOG_WARNING, "Out of buffer space\n");
249                         return -1;
250                 }
251                 for (x = 0 ; x < tmp->framesize; x++)
252                         dst[pvt->samples + x] = (int16_t)fout[x];
253                 pvt->samples += tmp->framesize;
254                 pvt->datalen += 2 * tmp->framesize; /* 2 bytes/sample */
255         }
256         return 0;
257 }
258
259 /*! \brief store input frame in work buffer */
260 static int lintospeex_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
261 {
262         struct speex_coder_pvt *tmp = pvt->pvt;
263
264         /* XXX We should look at how old the rest of our stream is, and if it
265            is too old, then we should overwrite it entirely, otherwise we can
266            get artifacts of earlier talk that do not belong */
267         memcpy(tmp->buf + pvt->samples, f->data.ptr, f->datalen);
268         pvt->samples += f->samples;
269         return 0;
270 }
271
272 /*! \brief convert work buffer and produce output frame */
273 static struct ast_frame *lintospeex_frameout(struct ast_trans_pvt *pvt)
274 {
275         struct speex_coder_pvt *tmp = pvt->pvt;
276         struct ast_frame *result = NULL;
277         struct ast_frame *last = NULL;
278         int samples = 0; /* output samples */
279
280         while (pvt->samples >= tmp->framesize) {
281                 struct ast_frame *current;
282                 int is_speech = 1;
283
284                 speex_bits_reset(&tmp->bits);
285
286 #ifdef _SPEEX_TYPES_H
287                 /* Preprocess audio */
288                 if (preproc)
289                         is_speech = speex_preprocess(tmp->pp, tmp->buf + samples, NULL);
290                 /* Encode a frame of data */
291                 if (is_speech) {
292                         /* If DTX enabled speex_encode returns 0 during silence */
293                         is_speech = speex_encode_int(tmp->speex, tmp->buf + samples, &tmp->bits) || !dtx;
294                 } else {
295                         /* 5 zeros interpreted by Speex as silence (submode 0) */
296                         speex_bits_pack(&tmp->bits, 0, 5);
297                 }
298 #else
299                 {
300                         float fbuf[1024];
301                         int x;
302                         /* Convert to floating point */
303                         for (x = 0; x < tmp->framesize; x++)
304                                 fbuf[x] = tmp->buf[samples + x];
305                         /* Encode a frame of data */
306                         is_speech = speex_encode(tmp->speex, fbuf, &tmp->bits) || !dtx;
307                 }
308 #endif
309                 samples += tmp->framesize;
310                 pvt->samples -= tmp->framesize;
311
312                 /* Use AST_FRAME_CNG to signify the start of any silence period */
313                 if (is_speech) {
314                         int datalen = 0; /* output bytes */
315
316                         tmp->silent_state = 0;
317                         /* Terminate bit stream */
318                         speex_bits_pack(&tmp->bits, 15, 5);
319                         datalen = speex_bits_write(&tmp->bits, pvt->outbuf.c, pvt->t->buf_size);
320                         current = ast_trans_frameout(pvt, datalen, tmp->framesize);
321                 } else if (tmp->silent_state) {
322                         current = NULL;
323                 } else {
324                         struct ast_frame frm = {
325                                 .frametype = AST_FRAME_CNG,
326                                 .src = pvt->t->name,
327                         };
328
329                         /*
330                          * XXX I don't think the AST_FRAME_CNG code has ever
331                          * really worked for speex.  There doesn't seem to be
332                          * any consumers of the frame type.  Everyone that
333                          * references the type seems to pass the frame on.
334                          */
335                         tmp->silent_state = 1;
336
337                         /* XXX what now ? format etc... */
338                         current = ast_frisolate(&frm);
339                 }
340
341                 if (!current) {
342                         continue;
343                 } else if (last) {
344                         AST_LIST_NEXT(last, frame_list) = current;
345                 } else {
346                         result = current;
347                 }
348                 last = current;
349         }
350
351         /* Move the data at the end of the buffer to the front */
352         if (samples) {
353                 memmove(tmp->buf, tmp->buf + samples, pvt->samples * 2);
354         }
355
356         return result;
357 }
358
359 /*! \brief handle incoming RTCP feedback and possibly edit encoder settings */
360 static void lintospeex_feedback(struct ast_trans_pvt *pvt, struct ast_frame *feedback)
361 {
362         struct speex_coder_pvt *tmp = pvt->pvt;
363
364         struct ast_rtp_rtcp_report *rtcp_report;
365         struct ast_rtp_rtcp_report_block *report_block;
366
367         int fraction_lost;
368         int percent;
369         int bitrate;
370         int q;
371
372         if(!exp_rtcp_fb)
373                 return;
374
375         /* We only accept feedback information in the form of SR and RR reports */
376         if (feedback->subclass.integer != AST_RTP_RTCP_SR && feedback->subclass.integer != AST_RTP_RTCP_RR) {
377                 return;
378         }
379
380         rtcp_report = (struct ast_rtp_rtcp_report *)feedback->data.ptr;
381         if (rtcp_report->reception_report_count == 0)
382                 return;
383         report_block = rtcp_report->report_block[0];
384         fraction_lost = report_block->lost_count.fraction;
385         if (fraction_lost == tmp->fraction_lost)
386                 return;
387         /* Per RFC3550, fraction lost is defined to be the number of packets lost
388          * divided by the number of packets expected. Since it's a 8-bit value,
389          * and we want a percentage value, we multiply by 100 and divide by 256. */
390         percent = (fraction_lost*100)/256;
391         bitrate = 0;
392         q = -1;
393         ast_debug(3, "Fraction lost changed: %d --> %d percent loss\n", fraction_lost, percent);
394         /* Handle change */
395         speex_encoder_ctl(tmp->speex, SPEEX_GET_BITRATE, &bitrate);
396         ast_debug(3, "Current bitrate: %d\n", bitrate);
397         ast_debug(3, "Current quality: %d/%d\n", tmp->quality, tmp->default_quality);
398         /* FIXME BADLY Very ugly example of how this could be handled: probably sucks */
399         if (percent < 10) {
400                 /* Not that bad, default quality is fine */
401                 q = tmp->default_quality;
402         } else if (percent < 20) {
403                 /* Quite bad, let's go down a bit */
404                 q = tmp->default_quality-1;
405         } else if (percent < 30) {
406                 /* Very bad, let's go down even more */
407                 q = tmp->default_quality-2;
408         } else {
409                 /* Really bad, use the lowest quality possible */
410                 q = 0;
411         }
412         if (q < 0)
413                 q = 0;
414         if (q != tmp->quality) {
415                 ast_debug(3, "  -- Setting to %d\n", q);
416                 if (vbr) {
417                         float vbr_q = q;
418                         speex_encoder_ctl(tmp->speex, SPEEX_SET_VBR_QUALITY, &vbr_q);
419                 } else {
420                         speex_encoder_ctl(tmp->speex, SPEEX_SET_QUALITY, &q);
421                 }
422                 tmp->quality = q;
423         }
424         tmp->fraction_lost = fraction_lost;
425 }
426
427 static void speextolin_destroy(struct ast_trans_pvt *arg)
428 {
429         struct speex_coder_pvt *pvt = arg->pvt;
430
431         speex_decoder_destroy(pvt->speex);
432         speex_bits_destroy(&pvt->bits);
433 }
434
435 static void lintospeex_destroy(struct ast_trans_pvt *arg)
436 {
437         struct speex_coder_pvt *pvt = arg->pvt;
438 #ifdef _SPEEX_TYPES_H
439         if (preproc)
440                 speex_preprocess_state_destroy(pvt->pp);
441 #endif
442         speex_encoder_destroy(pvt->speex);
443         speex_bits_destroy(&pvt->bits);
444 }
445
446 static struct ast_translator speextolin = {
447         .name = "speextolin",
448         .src_codec = {
449                 .name = "speex",
450                 .type = AST_MEDIA_TYPE_AUDIO,
451                 .sample_rate = 8000,
452         },
453         .dst_codec = {
454                 .name = "slin",
455                 .type = AST_MEDIA_TYPE_AUDIO,
456                 .sample_rate = 8000,
457         },
458         .format = "slin",
459         .newpvt = speextolin_new,
460         .framein = speextolin_framein,
461         .destroy = speextolin_destroy,
462         .sample = speex_sample,
463         .desc_size = sizeof(struct speex_coder_pvt),
464         .buffer_samples = BUFFER_SAMPLES,
465         .buf_size = BUFFER_SAMPLES * 2,
466         .native_plc = 1,
467 };
468
469 static struct ast_translator lintospeex = {
470         .name = "lintospeex",
471         .src_codec = {
472                 .name = "slin",
473                 .type = AST_MEDIA_TYPE_AUDIO,
474                 .sample_rate = 8000,
475         },
476         .dst_codec = {
477                 .name = "speex",
478                 .type = AST_MEDIA_TYPE_AUDIO,
479                 .sample_rate = 8000,
480         },
481         .format = "speex",
482         .newpvt = lintospeex_new,
483         .framein = lintospeex_framein,
484         .frameout = lintospeex_frameout,
485         .feedback = lintospeex_feedback,
486         .destroy = lintospeex_destroy,
487         .sample = slin8_sample,
488         .desc_size = sizeof(struct speex_coder_pvt),
489         .buffer_samples = BUFFER_SAMPLES,
490         .buf_size = BUFFER_SAMPLES * 2, /* XXX maybe a lot less ? */
491 };
492
493 static struct ast_translator speexwbtolin16 = {
494         .name = "speexwbtolin16",
495         .src_codec = {
496                 .name = "speex",
497                 .type = AST_MEDIA_TYPE_AUDIO,
498                 .sample_rate = 16000,
499         },
500         .dst_codec = {
501                 .name = "slin",
502                 .type = AST_MEDIA_TYPE_AUDIO,
503                 .sample_rate = 16000,
504         },
505         .format = "slin16",
506         .newpvt = speexwbtolin16_new,
507         .framein = speextolin_framein,
508         .destroy = speextolin_destroy,
509         .sample = speex16_sample,
510         .desc_size = sizeof(struct speex_coder_pvt),
511         .buffer_samples = BUFFER_SAMPLES,
512         .buf_size = BUFFER_SAMPLES * 2,
513         .native_plc = 1,
514 };
515
516 static struct ast_translator lin16tospeexwb = {
517         .name = "lin16tospeexwb",
518         .src_codec = {
519                 .name = "slin",
520                 .type = AST_MEDIA_TYPE_AUDIO,
521                 .sample_rate = 16000,
522         },
523         .dst_codec = {
524                 .name = "speex",
525                 .type = AST_MEDIA_TYPE_AUDIO,
526                 .sample_rate = 16000,
527         },
528         .format = "speex16",
529         .newpvt = lin16tospeexwb_new,
530         .framein = lintospeex_framein,
531         .frameout = lintospeex_frameout,
532         .feedback = lintospeex_feedback,
533         .destroy = lintospeex_destroy,
534         .sample = slin16_sample,
535         .desc_size = sizeof(struct speex_coder_pvt),
536         .buffer_samples = BUFFER_SAMPLES,
537         .buf_size = BUFFER_SAMPLES * 2, /* XXX maybe a lot less ? */
538 };
539
540 static struct ast_translator speexuwbtolin32 = {
541         .name = "speexuwbtolin32",
542         .src_codec = {
543                 .name = "speex",
544                 .type = AST_MEDIA_TYPE_AUDIO,
545                 .sample_rate = 32000,
546         },
547         .dst_codec = {
548                 .name = "slin",
549                 .type = AST_MEDIA_TYPE_AUDIO,
550                 .sample_rate = 32000,
551         },
552         .format = "slin32",
553         .newpvt = speexuwbtolin32_new,
554         .framein = speextolin_framein,
555         .destroy = speextolin_destroy,
556         .desc_size = sizeof(struct speex_coder_pvt),
557         .buffer_samples = BUFFER_SAMPLES,
558         .buf_size = BUFFER_SAMPLES * 2,
559         .native_plc = 1,
560 };
561
562 static struct ast_translator lin32tospeexuwb = {
563         .name = "lin32tospeexuwb",
564         .src_codec = {
565                 .name = "slin",
566                 .type = AST_MEDIA_TYPE_AUDIO,
567                 .sample_rate = 32000,
568         },
569         .dst_codec = {
570                 .name = "speex",
571                 .type = AST_MEDIA_TYPE_AUDIO,
572                 .sample_rate = 32000,
573         },
574         .format = "speex32",
575         .newpvt = lin32tospeexuwb_new,
576         .framein = lintospeex_framein,
577         .frameout = lintospeex_frameout,
578         .feedback = lintospeex_feedback,
579         .destroy = lintospeex_destroy,
580         .desc_size = sizeof(struct speex_coder_pvt),
581         .buffer_samples = BUFFER_SAMPLES,
582         .buf_size = BUFFER_SAMPLES * 2, /* XXX maybe a lot less ? */
583 };
584
585 static int parse_config(int reload)
586 {
587         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
588         struct ast_config *cfg = ast_config_load("codecs.conf", config_flags);
589         struct ast_variable *var;
590         int res;
591         float res_f;
592
593         if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID)
594                 return 0;
595
596         for (var = ast_variable_browse(cfg, "speex"); var; var = var->next) {
597                 if (!strcasecmp(var->name, "quality")) {
598                         res = abs(atoi(var->value));
599                         if (res > -1 && res < 11) {
600                                 ast_verb(3, "CODEC SPEEX: Setting Quality to %d\n",res);
601                                 quality = res;
602                         } else
603                                 ast_log(LOG_ERROR,"Error Quality must be 0-10\n");
604                 } else if (!strcasecmp(var->name, "complexity")) {
605                         res = abs(atoi(var->value));
606                         if (res > -1 && res < 11) {
607                                 ast_verb(3, "CODEC SPEEX: Setting Complexity to %d\n",res);
608                                 complexity = res;
609                         } else
610                                 ast_log(LOG_ERROR,"Error! Complexity must be 0-10\n");
611                 } else if (!strcasecmp(var->name, "vbr_quality")) {
612                         if (sscanf(var->value, "%30f", &res_f) == 1 && res_f >= 0 && res_f <= 10) {
613                                 ast_verb(3, "CODEC SPEEX: Setting VBR Quality to %f\n",res_f);
614                                 vbr_quality = res_f;
615                         } else
616                                 ast_log(LOG_ERROR,"Error! VBR Quality must be 0-10\n");
617                 } else if (!strcasecmp(var->name, "abr_quality")) {
618                         ast_log(LOG_ERROR,"Error! ABR Quality setting obsolete, set ABR to desired bitrate\n");
619                 } else if (!strcasecmp(var->name, "enhancement")) {
620                         enhancement = ast_true(var->value) ? 1 : 0;
621                         ast_verb(3, "CODEC SPEEX: Perceptual Enhancement Mode. [%s]\n",enhancement ? "on" : "off");
622                 } else if (!strcasecmp(var->name, "vbr")) {
623                         vbr = ast_true(var->value) ? 1 : 0;
624                         ast_verb(3, "CODEC SPEEX: VBR Mode. [%s]\n",vbr ? "on" : "off");
625                 } else if (!strcasecmp(var->name, "abr")) {
626                         res = abs(atoi(var->value));
627                         if (res >= 0) {
628                                         if (res > 0)
629                                         ast_verb(3, "CODEC SPEEX: Setting ABR target bitrate to %d\n",res);
630                                         else
631                                         ast_verb(3, "CODEC SPEEX: Disabling ABR\n");
632                                 abr = res;
633                         } else
634                                 ast_log(LOG_ERROR,"Error! ABR target bitrate must be >= 0\n");
635                 } else if (!strcasecmp(var->name, "vad")) {
636                         vad = ast_true(var->value) ? 1 : 0;
637                         ast_verb(3, "CODEC SPEEX: VAD Mode. [%s]\n",vad ? "on" : "off");
638                 } else if (!strcasecmp(var->name, "dtx")) {
639                         dtx = ast_true(var->value) ? 1 : 0;
640                         ast_verb(3, "CODEC SPEEX: DTX Mode. [%s]\n",dtx ? "on" : "off");
641                 } else if (!strcasecmp(var->name, "preprocess")) {
642                         preproc = ast_true(var->value) ? 1 : 0;
643                         ast_verb(3, "CODEC SPEEX: Preprocessing. [%s]\n",preproc ? "on" : "off");
644                 } else if (!strcasecmp(var->name, "pp_vad")) {
645                         pp_vad = ast_true(var->value) ? 1 : 0;
646                         ast_verb(3, "CODEC SPEEX: Preprocessor VAD. [%s]\n",pp_vad ? "on" : "off");
647                 } else if (!strcasecmp(var->name, "pp_agc")) {
648                         pp_agc = ast_true(var->value) ? 1 : 0;
649                         ast_verb(3, "CODEC SPEEX: Preprocessor AGC. [%s]\n",pp_agc ? "on" : "off");
650                 } else if (!strcasecmp(var->name, "pp_agc_level")) {
651                         if (sscanf(var->value, "%30f", &res_f) == 1 && res_f >= 0) {
652                                 ast_verb(3, "CODEC SPEEX: Setting preprocessor AGC Level to %f\n",res_f);
653                                 pp_agc_level = res_f;
654                         } else
655                                 ast_log(LOG_ERROR,"Error! Preprocessor AGC Level must be >= 0\n");
656                 } else if (!strcasecmp(var->name, "pp_denoise")) {
657                         pp_denoise = ast_true(var->value) ? 1 : 0;
658                         ast_verb(3, "CODEC SPEEX: Preprocessor Denoise. [%s]\n",pp_denoise ? "on" : "off");
659                 } else if (!strcasecmp(var->name, "pp_dereverb")) {
660                         pp_dereverb = ast_true(var->value) ? 1 : 0;
661                         ast_verb(3, "CODEC SPEEX: Preprocessor Dereverb. [%s]\n",pp_dereverb ? "on" : "off");
662                 } else if (!strcasecmp(var->name, "pp_dereverb_decay")) {
663                         if (sscanf(var->value, "%30f", &res_f) == 1 && res_f >= 0) {
664                                 ast_verb(3, "CODEC SPEEX: Setting preprocessor Dereverb Decay to %f\n",res_f);
665                                 pp_dereverb_decay = res_f;
666                         } else
667                                 ast_log(LOG_ERROR,"Error! Preprocessor Dereverb Decay must be >= 0\n");
668                 } else if (!strcasecmp(var->name, "pp_dereverb_level")) {
669                         if (sscanf(var->value, "%30f", &res_f) == 1 && res_f >= 0) {
670                                 ast_verb(3, "CODEC SPEEX: Setting preprocessor Dereverb Level to %f\n",res_f);
671                                 pp_dereverb_level = res_f;
672                         } else
673                                 ast_log(LOG_ERROR,"Error! Preprocessor Dereverb Level must be >= 0\n");
674                 } else if (!strcasecmp(var->name, "experimental_rtcp_feedback")) {
675                         exp_rtcp_fb = ast_true(var->value) ? 1 : 0;
676                         ast_verb(3, "CODEC SPEEX: Experimental RTCP Feedback. [%s]\n",exp_rtcp_fb ? "on" : "off");
677                 }
678         }
679         ast_config_destroy(cfg);
680         return 0;
681 }
682
683 static int reload(void)
684 {
685         if (parse_config(1))
686                 return AST_MODULE_LOAD_DECLINE;
687         return AST_MODULE_LOAD_SUCCESS;
688 }
689
690 static int unload_module(void)
691 {
692         ast_unregister_translator(&speextolin);
693         ast_unregister_translator(&lintospeex);
694         ast_unregister_translator(&speexwbtolin16);
695         ast_unregister_translator(&lin16tospeexwb);
696         ast_unregister_translator(&speexuwbtolin32);
697         ast_unregister_translator(&lin32tospeexuwb);
698
699         return 0;
700 }
701
702 static int load_module(void)
703 {
704         int res = 0;
705
706         if (parse_config(0)) {
707                 return AST_MODULE_LOAD_DECLINE;
708         }
709
710         /* XXX It is most likely a bug in this module if we fail to register a translator */
711         res |= ast_register_translator(&speextolin);
712         res |= ast_register_translator(&lintospeex);
713         res |= ast_register_translator(&speexwbtolin16);
714         res |= ast_register_translator(&lin16tospeexwb);
715         res |= ast_register_translator(&speexuwbtolin32);
716         res |= ast_register_translator(&lin32tospeexuwb);
717         if (res) {
718                 unload_module();
719                 return AST_MODULE_LOAD_DECLINE;
720         }
721
722         return AST_MODULE_LOAD_SUCCESS;
723 }
724
725 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Speex Coder/Decoder",
726         .support_level = AST_MODULE_SUPPORT_CORE,
727         .load = load_module,
728         .unload = unload_module,
729         .reload = reload,
730 );