c91070d9e18359c45a68307daf95624cc6004097
[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 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
43
44 #include <speex/speex.h>
45
46 /* We require a post 1.1.8 version of Speex to enable preprocessing
47  * and better type handling
48  */   
49 #ifdef _SPEEX_TYPES_H
50 #include <speex/speex_preprocess.h>
51 #endif
52
53 #include "asterisk/translate.h"
54 #include "asterisk/module.h"
55 #include "asterisk/config.h"
56 #include "asterisk/utils.h"
57
58 /* codec variables */
59 static int quality = 3;
60 static int complexity = 2;
61 static int enhancement = 0;
62 static int vad = 0;
63 static int vbr = 0;
64 static float vbr_quality = 4;
65 static int abr = 0;
66 static int dtx = 0;     /* set to 1 to enable silence detection */
67
68 static int preproc = 0;
69 static int pp_vad = 0;
70 static int pp_agc = 0;
71 static float pp_agc_level = 8000; /* XXX what is this 8000 ? */
72 static int pp_denoise = 0;
73 static int pp_dereverb = 0;
74 static float pp_dereverb_decay = 0.4;
75 static float pp_dereverb_level = 0.3;
76
77 #define TYPE_SILENCE     0x2
78 #define TYPE_HIGH        0x0
79 #define TYPE_LOW         0x1
80 #define TYPE_MASK        0x3
81
82 #define BUFFER_SAMPLES  8000
83 #define SPEEX_SAMPLES   160
84
85 /* Sample frame data */
86 #include "asterisk/slin.h"
87 #include "ex_speex.h"
88
89 struct speex_coder_pvt {
90         void *speex;
91         SpeexBits bits;
92         int framesize;
93         int silent_state;
94 #ifdef _SPEEX_TYPES_H
95         SpeexPreprocessState *pp;
96         spx_int16_t buf[BUFFER_SAMPLES];
97 #else
98         int16_t buf[BUFFER_SAMPLES];    /* input, waiting to be compressed */
99 #endif
100 };
101
102 static int speex_encoder_construct(struct ast_trans_pvt *pvt, const SpeexMode *profile, int sampling_rate)
103 {
104         struct speex_coder_pvt *tmp = pvt->pvt;
105
106         if (!(tmp->speex = speex_encoder_init(profile)))
107                 return -1;
108
109         speex_bits_init(&tmp->bits);
110         speex_bits_reset(&tmp->bits);
111         speex_encoder_ctl(tmp->speex, SPEEX_GET_FRAME_SIZE, &tmp->framesize);
112         speex_encoder_ctl(tmp->speex, SPEEX_SET_COMPLEXITY, &complexity);
113 #ifdef _SPEEX_TYPES_H
114         if (preproc) {
115                 tmp->pp = speex_preprocess_state_init(tmp->framesize, sampling_rate);
116                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_VAD, &pp_vad);
117                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_AGC, &pp_agc);
118                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_AGC_LEVEL, &pp_agc_level);
119                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_DENOISE, &pp_denoise);
120                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_DEREVERB, &pp_dereverb);
121                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &pp_dereverb_decay);
122                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &pp_dereverb_level);
123         }
124 #endif
125         if (!abr && !vbr) {
126                 speex_encoder_ctl(tmp->speex, SPEEX_SET_QUALITY, &quality);
127                 if (vad)
128                         speex_encoder_ctl(tmp->speex, SPEEX_SET_VAD, &vad);
129         }
130         if (vbr) {
131                 speex_encoder_ctl(tmp->speex, SPEEX_SET_VBR, &vbr);
132                 speex_encoder_ctl(tmp->speex, SPEEX_SET_VBR_QUALITY, &vbr_quality);
133         }
134         if (abr)
135                 speex_encoder_ctl(tmp->speex, SPEEX_SET_ABR, &abr);
136         if (dtx)
137                 speex_encoder_ctl(tmp->speex, SPEEX_SET_DTX, &dtx); 
138         tmp->silent_state = 0;
139
140         return 0;
141 }
142
143 static int lintospeex_new(struct ast_trans_pvt *pvt)
144 {
145         return speex_encoder_construct(pvt, &speex_nb_mode, 8000);
146 }
147
148 static int lin16tospeexwb_new(struct ast_trans_pvt *pvt)
149 {
150         return speex_encoder_construct(pvt, &speex_wb_mode, 16000);
151 }
152
153 static int lin32tospeexuwb_new(struct ast_trans_pvt *pvt)
154 {
155         return speex_encoder_construct(pvt, &speex_uwb_mode, 32000);
156 }
157
158 static int speex_decoder_construct(struct ast_trans_pvt *pvt, const SpeexMode *profile)
159 {
160         struct speex_coder_pvt *tmp = pvt->pvt;
161         
162         if (!(tmp->speex = speex_decoder_init(profile)))
163                 return -1;
164
165         speex_bits_init(&tmp->bits);
166         speex_decoder_ctl(tmp->speex, SPEEX_GET_FRAME_SIZE, &tmp->framesize);
167         if (enhancement)
168                 speex_decoder_ctl(tmp->speex, SPEEX_SET_ENH, &enhancement);
169
170         return 0;
171 }
172
173 static int speextolin_new(struct ast_trans_pvt *pvt)
174 {
175         return speex_decoder_construct(pvt, &speex_nb_mode);
176 }
177
178 static int speexwbtolin16_new(struct ast_trans_pvt *pvt)
179 {
180         return speex_decoder_construct(pvt, &speex_wb_mode);
181 }
182
183 static int speexuwbtolin32_new(struct ast_trans_pvt *pvt)
184 {
185         return speex_decoder_construct(pvt, &speex_uwb_mode);
186 }
187
188 /*! \brief convert and store into outbuf */
189 static int speextolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
190 {
191         struct speex_coder_pvt *tmp = pvt->pvt;
192
193         /* Assuming there's space left, decode into the current buffer at
194            the tail location.  Read in as many frames as there are */
195         int x;
196         int res;
197         int16_t *dst = pvt->outbuf.i16;
198         /* XXX fout is a temporary buffer, may have different types */
199 #ifdef _SPEEX_TYPES_H
200         spx_int16_t fout[1024];
201 #else
202         float fout[1024];
203 #endif
204
205         if (f->datalen == 0) {  /* Native PLC interpolation */
206                 if (pvt->samples + tmp->framesize > BUFFER_SAMPLES) {
207                         ast_log(LOG_WARNING, "Out of buffer space\n");
208                         return -1;
209                 }
210 #ifdef _SPEEX_TYPES_H
211                 speex_decode_int(tmp->speex, NULL, dst + pvt->samples);
212 #else
213                 speex_decode(tmp->speex, NULL, fout);
214                 for (x=0;x<tmp->framesize;x++) {
215                         dst[pvt->samples + x] = (int16_t)fout[x];
216                 }
217 #endif
218                 pvt->samples += tmp->framesize;
219                 pvt->datalen += 2 * tmp->framesize; /* 2 bytes/sample */
220                 return 0;
221         }
222
223         /* Read in bits */
224         speex_bits_read_from(&tmp->bits, f->data.ptr, f->datalen);
225         for (;;) {
226 #ifdef _SPEEX_TYPES_H
227                 res = speex_decode_int(tmp->speex, &tmp->bits, fout);
228 #else
229                 res = speex_decode(tmp->speex, &tmp->bits, fout);
230 #endif
231                 if (res < 0)
232                         break;
233                 if (pvt->samples + tmp->framesize > BUFFER_SAMPLES) {
234                         ast_log(LOG_WARNING, "Out of buffer space\n");
235                         return -1;
236                 }
237                 for (x = 0 ; x < tmp->framesize; x++)
238                         dst[pvt->samples + x] = (int16_t)fout[x];
239                 pvt->samples += tmp->framesize;
240                 pvt->datalen += 2 * tmp->framesize; /* 2 bytes/sample */
241         }
242         return 0;
243 }
244
245 /*! \brief store input frame in work buffer */
246 static int lintospeex_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
247 {
248         struct speex_coder_pvt *tmp = pvt->pvt;
249
250         /* XXX We should look at how old the rest of our stream is, and if it
251            is too old, then we should overwrite it entirely, otherwise we can
252            get artifacts of earlier talk that do not belong */
253         memcpy(tmp->buf + pvt->samples, f->data.ptr, f->datalen);
254         pvt->samples += f->samples;
255         return 0;
256 }
257
258 /*! \brief convert work buffer and produce output frame */
259 static struct ast_frame *lintospeex_frameout(struct ast_trans_pvt *pvt)
260 {
261         struct speex_coder_pvt *tmp = pvt->pvt;
262         int is_speech=1;
263         int datalen = 0;        /* output bytes */
264         int samples = 0;        /* output samples */
265
266         /* We can't work on anything less than a frame in size */
267         if (pvt->samples < tmp->framesize)
268                 return NULL;
269         speex_bits_reset(&tmp->bits);
270         while (pvt->samples >= tmp->framesize) {
271 #ifdef _SPEEX_TYPES_H
272                 /* Preprocess audio */
273                 if (preproc)
274                         is_speech = speex_preprocess(tmp->pp, tmp->buf + samples, NULL);
275                 /* Encode a frame of data */
276                 if (is_speech) {
277                         /* If DTX enabled speex_encode returns 0 during silence */
278                         is_speech = speex_encode_int(tmp->speex, tmp->buf + samples, &tmp->bits) || !dtx;
279                 } else {
280                         /* 5 zeros interpreted by Speex as silence (submode 0) */
281                         speex_bits_pack(&tmp->bits, 0, 5);
282                 }
283 #else
284                 {
285                         float fbuf[1024];
286                         int x;
287                         /* Convert to floating point */
288                         for (x = 0; x < tmp->framesize; x++)
289                                 fbuf[x] = tmp->buf[samples + x];
290                         /* Encode a frame of data */
291                         is_speech = speex_encode(tmp->speex, fbuf, &tmp->bits) || !dtx;
292                 }
293 #endif
294                 samples += tmp->framesize;
295                 pvt->samples -= tmp->framesize;
296         }
297
298         /* Move the data at the end of the buffer to the front */
299         if (pvt->samples)
300                 memmove(tmp->buf, tmp->buf + samples, pvt->samples * 2);
301
302         /* Use AST_FRAME_CNG to signify the start of any silence period */
303         if (is_speech) {
304                 tmp->silent_state = 0;
305         } else {
306                 if (tmp->silent_state) {
307                         return NULL;
308                 } else {
309                         struct ast_frame frm = {
310                                 .frametype = AST_FRAME_CNG,
311                                 .src = pvt->t->name,
312                         };
313
314                         /*
315                          * XXX I don't think the AST_FRAME_CNG code has ever
316                          * really worked for speex.  There doesn't seem to be
317                          * any consumers of the frame type.  Everyone that
318                          * references the type seems to pass the frame on.
319                          */
320                         tmp->silent_state = 1;
321
322                         /* XXX what now ? format etc... */
323                         return ast_frisolate(&frm);
324                 }
325         }
326
327         /* Terminate bit stream */
328         speex_bits_pack(&tmp->bits, 15, 5);
329         datalen = speex_bits_write(&tmp->bits, pvt->outbuf.c, pvt->t->buf_size);
330         return ast_trans_frameout(pvt, datalen, samples);
331 }
332
333 static void speextolin_destroy(struct ast_trans_pvt *arg)
334 {
335         struct speex_coder_pvt *pvt = arg->pvt;
336
337         speex_decoder_destroy(pvt->speex);
338         speex_bits_destroy(&pvt->bits);
339 }
340
341 static void lintospeex_destroy(struct ast_trans_pvt *arg)
342 {
343         struct speex_coder_pvt *pvt = arg->pvt;
344 #ifdef _SPEEX_TYPES_H
345         if (preproc)
346                 speex_preprocess_state_destroy(pvt->pp);
347 #endif
348         speex_encoder_destroy(pvt->speex);
349         speex_bits_destroy(&pvt->bits);
350 }
351
352 static struct ast_translator speextolin = {
353         .name = "speextolin",
354         .src_codec = {
355                 .name = "speex",
356                 .type = AST_MEDIA_TYPE_AUDIO,
357                 .sample_rate = 8000,
358         },
359         .dst_codec = {
360                 .name = "slin",
361                 .type = AST_MEDIA_TYPE_AUDIO,
362                 .sample_rate = 8000,
363         },
364         .format = "slin",
365         .newpvt = speextolin_new,
366         .framein = speextolin_framein,
367         .destroy = speextolin_destroy,
368         .sample = speex_sample,
369         .desc_size = sizeof(struct speex_coder_pvt),
370         .buffer_samples = BUFFER_SAMPLES,
371         .buf_size = BUFFER_SAMPLES * 2,
372         .native_plc = 1,
373 };
374
375 static struct ast_translator lintospeex = {
376         .name = "lintospeex", 
377         .src_codec = {
378                 .name = "slin",
379                 .type = AST_MEDIA_TYPE_AUDIO,
380                 .sample_rate = 8000,
381         },
382         .dst_codec = {
383                 .name = "speex",
384                 .type = AST_MEDIA_TYPE_AUDIO,
385                 .sample_rate = 8000,
386         },
387         .format = "speex",
388         .newpvt = lintospeex_new,
389         .framein = lintospeex_framein,
390         .frameout = lintospeex_frameout,
391         .destroy = lintospeex_destroy,
392         .sample = slin8_sample,
393         .desc_size = sizeof(struct speex_coder_pvt),
394         .buffer_samples = BUFFER_SAMPLES,
395         .buf_size = BUFFER_SAMPLES * 2, /* XXX maybe a lot less ? */
396 };
397
398 static struct ast_translator speexwbtolin16 = {
399         .name = "speexwbtolin16",
400         .src_codec = {
401                 .name = "speex",
402                 .type = AST_MEDIA_TYPE_AUDIO,
403                 .sample_rate = 16000,
404         },
405         .dst_codec = {
406                 .name = "slin",
407                 .type = AST_MEDIA_TYPE_AUDIO,
408                 .sample_rate = 16000,
409         }, 
410         .format = "slin16",
411         .newpvt = speexwbtolin16_new,
412         .framein = speextolin_framein,
413         .destroy = speextolin_destroy,
414         .sample = speex16_sample,
415         .desc_size = sizeof(struct speex_coder_pvt),
416         .buffer_samples = BUFFER_SAMPLES,
417         .buf_size = BUFFER_SAMPLES * 2,
418         .native_plc = 1,
419 };
420
421 static struct ast_translator lin16tospeexwb = {
422         .name = "lin16tospeexwb",
423         .src_codec = {
424                 .name = "slin",
425                 .type = AST_MEDIA_TYPE_AUDIO,
426                 .sample_rate = 16000,
427         },
428         .dst_codec = {
429                 .name = "speex",
430                 .type = AST_MEDIA_TYPE_AUDIO,
431                 .sample_rate = 16000,
432         },
433         .format = "speex16",
434         .newpvt = lin16tospeexwb_new,
435         .framein = lintospeex_framein,
436         .frameout = lintospeex_frameout,
437         .destroy = lintospeex_destroy,
438         .sample = slin16_sample,
439         .desc_size = sizeof(struct speex_coder_pvt),
440         .buffer_samples = BUFFER_SAMPLES,
441         .buf_size = BUFFER_SAMPLES * 2, /* XXX maybe a lot less ? */
442 };
443
444 static struct ast_translator speexuwbtolin32 = {
445         .name = "speexuwbtolin32",
446         .src_codec = {
447                 .name = "speex",
448                 .type = AST_MEDIA_TYPE_AUDIO,
449                 .sample_rate = 32000,
450         },
451         .dst_codec = {
452                 .name = "slin",
453                 .type = AST_MEDIA_TYPE_AUDIO,
454                 .sample_rate = 32000,
455         },
456         .format = "slin32",
457         .newpvt = speexuwbtolin32_new,
458         .framein = speextolin_framein,
459         .destroy = speextolin_destroy,
460         .desc_size = sizeof(struct speex_coder_pvt),
461         .buffer_samples = BUFFER_SAMPLES,
462         .buf_size = BUFFER_SAMPLES * 2,
463         .native_plc = 1,
464 };
465
466 static struct ast_translator lin32tospeexuwb = {
467         .name = "lin32tospeexuwb",
468         .src_codec = {
469                 .name = "slin",
470                 .type = AST_MEDIA_TYPE_AUDIO,
471                 .sample_rate = 32000,
472         },
473         .dst_codec = {
474                 .name = "speex",
475                 .type = AST_MEDIA_TYPE_AUDIO,
476                 .sample_rate = 32000,
477         },
478         .format = "speex32",
479         .newpvt = lin32tospeexuwb_new,
480         .framein = lintospeex_framein,
481         .frameout = lintospeex_frameout,
482         .destroy = lintospeex_destroy,
483         .desc_size = sizeof(struct speex_coder_pvt),
484         .buffer_samples = BUFFER_SAMPLES,
485         .buf_size = BUFFER_SAMPLES * 2, /* XXX maybe a lot less ? */
486 };
487
488 static int parse_config(int reload) 
489 {
490         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
491         struct ast_config *cfg = ast_config_load("codecs.conf", config_flags);
492         struct ast_variable *var;
493         int res;
494         float res_f;
495
496         if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID)
497                 return 0;
498
499         for (var = ast_variable_browse(cfg, "speex"); var; var = var->next) {
500                 if (!strcasecmp(var->name, "quality")) {
501                         res = abs(atoi(var->value));
502                         if (res > -1 && res < 11) {
503                                 ast_verb(3, "CODEC SPEEX: Setting Quality to %d\n",res);
504                                 quality = res;
505                         } else 
506                                 ast_log(LOG_ERROR,"Error Quality must be 0-10\n");
507                 } else if (!strcasecmp(var->name, "complexity")) {
508                         res = abs(atoi(var->value));
509                         if (res > -1 && res < 11) {
510                                 ast_verb(3, "CODEC SPEEX: Setting Complexity to %d\n",res);
511                                 complexity = res;
512                         } else 
513                                 ast_log(LOG_ERROR,"Error! Complexity must be 0-10\n");
514                 } else if (!strcasecmp(var->name, "vbr_quality")) {
515                         if (sscanf(var->value, "%30f", &res_f) == 1 && res_f >= 0 && res_f <= 10) {
516                                 ast_verb(3, "CODEC SPEEX: Setting VBR Quality to %f\n",res_f);
517                                 vbr_quality = res_f;
518                         } else
519                                 ast_log(LOG_ERROR,"Error! VBR Quality must be 0-10\n");
520                 } else if (!strcasecmp(var->name, "abr_quality")) {
521                         ast_log(LOG_ERROR,"Error! ABR Quality setting obsolete, set ABR to desired bitrate\n");
522                 } else if (!strcasecmp(var->name, "enhancement")) {
523                         enhancement = ast_true(var->value) ? 1 : 0;
524                         ast_verb(3, "CODEC SPEEX: Perceptual Enhancement Mode. [%s]\n",enhancement ? "on" : "off");
525                 } else if (!strcasecmp(var->name, "vbr")) {
526                         vbr = ast_true(var->value) ? 1 : 0;
527                         ast_verb(3, "CODEC SPEEX: VBR Mode. [%s]\n",vbr ? "on" : "off");
528                 } else if (!strcasecmp(var->name, "abr")) {
529                         res = abs(atoi(var->value));
530                         if (res >= 0) {
531                                         if (res > 0)
532                                         ast_verb(3, "CODEC SPEEX: Setting ABR target bitrate to %d\n",res);
533                                         else
534                                         ast_verb(3, "CODEC SPEEX: Disabling ABR\n");
535                                 abr = res;
536                         } else 
537                                 ast_log(LOG_ERROR,"Error! ABR target bitrate must be >= 0\n");
538                 } else if (!strcasecmp(var->name, "vad")) {
539                         vad = ast_true(var->value) ? 1 : 0;
540                         ast_verb(3, "CODEC SPEEX: VAD Mode. [%s]\n",vad ? "on" : "off");
541                 } else if (!strcasecmp(var->name, "dtx")) {
542                         dtx = ast_true(var->value) ? 1 : 0;
543                         ast_verb(3, "CODEC SPEEX: DTX Mode. [%s]\n",dtx ? "on" : "off");
544                 } else if (!strcasecmp(var->name, "preprocess")) {
545                         preproc = ast_true(var->value) ? 1 : 0;
546                         ast_verb(3, "CODEC SPEEX: Preprocessing. [%s]\n",preproc ? "on" : "off");
547                 } else if (!strcasecmp(var->name, "pp_vad")) {
548                         pp_vad = ast_true(var->value) ? 1 : 0;
549                         ast_verb(3, "CODEC SPEEX: Preprocessor VAD. [%s]\n",pp_vad ? "on" : "off");
550                 } else if (!strcasecmp(var->name, "pp_agc")) {
551                         pp_agc = ast_true(var->value) ? 1 : 0;
552                         ast_verb(3, "CODEC SPEEX: Preprocessor AGC. [%s]\n",pp_agc ? "on" : "off");
553                 } else if (!strcasecmp(var->name, "pp_agc_level")) {
554                         if (sscanf(var->value, "%30f", &res_f) == 1 && res_f >= 0) {
555                                 ast_verb(3, "CODEC SPEEX: Setting preprocessor AGC Level to %f\n",res_f);
556                                 pp_agc_level = res_f;
557                         } else
558                                 ast_log(LOG_ERROR,"Error! Preprocessor AGC Level must be >= 0\n");
559                 } else if (!strcasecmp(var->name, "pp_denoise")) {
560                         pp_denoise = ast_true(var->value) ? 1 : 0;
561                         ast_verb(3, "CODEC SPEEX: Preprocessor Denoise. [%s]\n",pp_denoise ? "on" : "off");
562                 } else if (!strcasecmp(var->name, "pp_dereverb")) {
563                         pp_dereverb = ast_true(var->value) ? 1 : 0;
564                         ast_verb(3, "CODEC SPEEX: Preprocessor Dereverb. [%s]\n",pp_dereverb ? "on" : "off");
565                 } else if (!strcasecmp(var->name, "pp_dereverb_decay")) {
566                         if (sscanf(var->value, "%30f", &res_f) == 1 && res_f >= 0) {
567                                 ast_verb(3, "CODEC SPEEX: Setting preprocessor Dereverb Decay to %f\n",res_f);
568                                 pp_dereverb_decay = res_f;
569                         } else
570                                 ast_log(LOG_ERROR,"Error! Preprocessor Dereverb Decay must be >= 0\n");
571                 } else if (!strcasecmp(var->name, "pp_dereverb_level")) {
572                         if (sscanf(var->value, "%30f", &res_f) == 1 && res_f >= 0) {
573                                 ast_verb(3, "CODEC SPEEX: Setting preprocessor Dereverb Level to %f\n",res_f);
574                                 pp_dereverb_level = res_f;
575                         } else
576                                 ast_log(LOG_ERROR,"Error! Preprocessor Dereverb Level must be >= 0\n");
577                 }
578         }
579         ast_config_destroy(cfg);
580         return 0;
581 }
582
583 static int reload(void) 
584 {
585         if (parse_config(1))
586                 return AST_MODULE_LOAD_DECLINE;
587         return AST_MODULE_LOAD_SUCCESS;
588 }
589
590 static int unload_module(void)
591 {
592         int res = 0;
593
594         res |= ast_unregister_translator(&speextolin);
595         res |= ast_unregister_translator(&lintospeex);
596         res |= ast_unregister_translator(&speexwbtolin16);
597         res |= ast_unregister_translator(&lin16tospeexwb);
598         res |= ast_unregister_translator(&speexuwbtolin32);
599         res |= ast_unregister_translator(&lin32tospeexuwb);
600
601
602         return res;
603 }
604
605 static int load_module(void)
606 {
607         int res = 0;
608
609         if (parse_config(0))
610                 return AST_MODULE_LOAD_DECLINE;
611
612         res |= ast_register_translator(&speextolin);
613         res |= ast_register_translator(&lintospeex);
614         res |= ast_register_translator(&speexwbtolin16);
615         res |= ast_register_translator(&lin16tospeexwb);
616         res |= ast_register_translator(&speexuwbtolin32);
617         res |= ast_register_translator(&lin32tospeexuwb);
618
619         if (res) {
620                 unload_module();
621                 return res;
622         }
623
624         return res;
625 }
626
627 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Speex Coder/Decoder",
628                 .support_level = AST_MODULE_SUPPORT_CORE,
629                 .load = load_module,
630                 .unload = unload_module,
631                 .reload = reload,
632                );