issue #5605
[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  * http://www.speex.org
25  * \note This work was motivated by Jeremy McNamara 
26  * hacked to be configurable by anthm and bkw 9/28/2004
27  * \ingroup codecs
28  */
29
30 #include <fcntl.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <netinet/in.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include <speex.h>
37
38 /* We require a post 1.1.8 version of Speex to enable preprocessing
39    and better type handling */   
40 #ifdef _SPEEX_TYPES_H
41 #include <speex/speex_preprocess.h>
42 #endif
43
44 static int quality = 3;
45 static int complexity = 2;
46 static int enhancement = 0;
47 static int vad = 0;
48 static int vbr = 0;
49 static float vbr_quality = 4;
50 static int abr = 0;
51 static int dtx = 0;
52
53 static int preproc = 0;
54 static int pp_vad = 0;
55 static int pp_agc = 0;
56 static float pp_agc_level = 8000;
57 static int pp_denoise = 0;
58 static int pp_dereverb = 0;
59 static float pp_dereverb_decay = 0.4;
60 static float pp_dereverb_level = 0.3;
61
62 #define TYPE_SILENCE     0x2
63 #define TYPE_HIGH        0x0
64 #define TYPE_LOW         0x1
65 #define TYPE_MASK        0x3
66
67 #include "asterisk.h"
68
69 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
70
71 #include "asterisk/lock.h"
72 #include "asterisk/translate.h"
73 #include "asterisk/module.h"
74 #include "asterisk/config.h"
75 #include "asterisk/options.h"
76 #include "asterisk/logger.h"
77 #include "asterisk/channel.h"
78
79 /* Sample frame data */
80 #include "slin_speex_ex.h"
81 #include "speex_slin_ex.h"
82
83 AST_MUTEX_DEFINE_STATIC(localuser_lock);
84 static int localusecnt=0;
85
86 static char *tdesc = "Speex/PCM16 (signed linear) Codec Translator";
87
88 struct ast_translator_pvt {
89         void *speex;
90         struct ast_frame f;
91         SpeexBits bits;
92         int framesize;
93         /* Space to build offset */
94         char offset[AST_FRIENDLY_OFFSET];
95 #ifdef _SPEEX_TYPES_H
96         SpeexPreprocessState *pp;
97         /* Buffer for our outgoing frame */
98         spx_int16_t outbuf[8000];
99         /* Enough to store a full second */
100         spx_int16_t buf[8000];
101 #else
102         short outbuf[8000];
103         short buf[8000];
104 #endif
105
106         int tail;
107         int silent_state;
108 };
109
110 #define speex_coder_pvt ast_translator_pvt
111
112 static struct ast_translator_pvt *lintospeex_new(void)
113 {
114         struct speex_coder_pvt *tmp;
115         tmp = malloc(sizeof(struct speex_coder_pvt));
116         if (tmp) {
117                 if (!(tmp->speex = speex_encoder_init(&speex_nb_mode))) {
118                         free(tmp);
119                         tmp = NULL;
120                 } else {
121                         speex_bits_init(&tmp->bits);
122                         speex_bits_reset(&tmp->bits);
123                         speex_encoder_ctl(tmp->speex, SPEEX_GET_FRAME_SIZE, &tmp->framesize);
124                         speex_encoder_ctl(tmp->speex, SPEEX_SET_COMPLEXITY, &complexity);
125 #ifdef _SPEEX_TYPES_H
126                         if (preproc) {
127                                 tmp->pp = speex_preprocess_state_init(tmp->framesize, 8000);
128                                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_VAD, &pp_vad);
129                                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_AGC, &pp_agc);
130                                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_AGC_LEVEL, &pp_agc_level);
131                                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_DENOISE, &pp_denoise);
132                                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_DEREVERB, &pp_dereverb);
133                                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &pp_dereverb_decay);
134                                 speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &pp_dereverb_level);
135                         }
136 #endif
137                         if (!abr && !vbr) {
138                                 speex_encoder_ctl(tmp->speex, SPEEX_SET_QUALITY, &quality);
139                                 if (vad)
140                                         speex_encoder_ctl(tmp->speex, SPEEX_SET_VAD, &vad);
141                         }
142                         if (vbr) {
143                                 speex_encoder_ctl(tmp->speex, SPEEX_SET_VBR, &vbr);
144                                 speex_encoder_ctl(tmp->speex, SPEEX_SET_VBR_QUALITY, &vbr_quality);
145                         }
146                         if (abr) {
147                                 speex_encoder_ctl(tmp->speex, SPEEX_SET_ABR, &abr);
148                         }
149                         if (dtx)
150                                 speex_encoder_ctl(tmp->speex, SPEEX_SET_DTX, &dtx); 
151                         tmp->tail = 0;
152                         tmp->silent_state = 0;
153                 }
154                 localusecnt++;
155         }
156         return tmp;
157 }
158
159 static struct ast_translator_pvt *speextolin_new(void)
160 {
161         struct speex_coder_pvt *tmp;
162         tmp = malloc(sizeof(struct speex_coder_pvt));
163         if (tmp) {
164                 if (!(tmp->speex = speex_decoder_init(&speex_nb_mode))) {
165                         free(tmp);
166                         tmp = NULL;
167                 } else {
168                         speex_bits_init(&tmp->bits);
169                         speex_decoder_ctl(tmp->speex, SPEEX_GET_FRAME_SIZE, &tmp->framesize);
170                         if (enhancement)
171                                 speex_decoder_ctl(tmp->speex, SPEEX_SET_ENH, &enhancement);
172                         tmp->tail = 0;
173                 }
174                 localusecnt++;
175         }
176         return tmp;
177 }
178
179 static struct ast_frame *lintospeex_sample(void)
180 {
181         static struct ast_frame f;
182         f.frametype = AST_FRAME_VOICE;
183         f.subclass = AST_FORMAT_SLINEAR;
184         f.datalen = sizeof(slin_speex_ex);
185         /* Assume 8000 Hz */
186         f.samples = sizeof(slin_speex_ex)/2;
187         f.mallocd = 0;
188         f.offset = 0;
189         f.src = __PRETTY_FUNCTION__;
190         f.data = slin_speex_ex;
191         return &f;
192 }
193
194 static struct ast_frame *speextolin_sample(void)
195 {
196         static struct ast_frame f;
197         f.frametype = AST_FRAME_VOICE;
198         f.subclass = AST_FORMAT_SPEEX;
199         f.datalen = sizeof(speex_slin_ex);
200         /* All frames are 20 ms long */
201         f.samples = 160;
202         f.mallocd = 0;
203         f.offset = 0;
204         f.src = __PRETTY_FUNCTION__;
205         f.data = speex_slin_ex;
206         return &f;
207 }
208
209 static struct ast_frame *speextolin_frameout(struct ast_translator_pvt *tmp)
210 {
211         if (!tmp->tail)
212                 return NULL;
213         /* Signed linear is no particular frame size, so just send whatever
214            we have in the buffer in one lump sum */
215         tmp->f.frametype = AST_FRAME_VOICE;
216         tmp->f.subclass = AST_FORMAT_SLINEAR;
217         tmp->f.datalen = tmp->tail * 2;
218         /* Assume 8000 Hz */
219         tmp->f.samples = tmp->tail;
220         tmp->f.mallocd = 0;
221         tmp->f.offset = AST_FRIENDLY_OFFSET;
222         tmp->f.src = __PRETTY_FUNCTION__;
223         tmp->f.data = tmp->buf;
224         /* Reset tail pointer */
225         tmp->tail = 0;
226         return &tmp->f; 
227 }
228
229 static int speextolin_framein(struct ast_translator_pvt *tmp, struct ast_frame *f)
230 {
231         /* Assuming there's space left, decode into the current buffer at
232            the tail location.  Read in as many frames as there are */
233         int x;
234         int res;
235 #ifdef _SPEEX_TYPES_H
236         spx_int16_t out[1024];
237 #else
238         float fout[1024];
239 #endif
240
241         if (f->datalen == 0) {  /* Native PLC interpolation */
242                 if (tmp->tail + tmp->framesize > sizeof(tmp->buf) / 2) {
243                         ast_log(LOG_WARNING, "Out of buffer space\n");
244                         return -1;
245                 }
246 #ifdef _SPEEX_TYPES_H
247                 speex_decode_int(tmp->speex, NULL, tmp->buf + tmp->tail);
248 #else
249                 speex_decode(tmp->speex, NULL, fout);
250                 for (x=0;x<tmp->framesize;x++) {
251                         tmp->buf[tmp->tail + x] = fout[x];
252                 }
253 #endif
254                 tmp->tail += tmp->framesize;
255                 return 0;
256         }
257
258         /* Read in bits */
259         speex_bits_read_from(&tmp->bits, f->data, f->datalen);
260         for(;;) {
261 #ifdef _SPEEX_TYPES_H
262                 res = speex_decode_int(tmp->speex, &tmp->bits, out);
263 #else
264                 res = speex_decode(tmp->speex, &tmp->bits, fout);
265 #endif
266                 if (res < 0)
267                         break;
268                 if (tmp->tail + tmp->framesize < sizeof(tmp->buf) / 2) {
269                         for (x=0;x<tmp->framesize;x++) {
270 #ifdef _SPEEX_TYPES_H
271                                 tmp->buf[tmp->tail + x] = out[x];
272 #else
273                                 tmp->buf[tmp->tail + x] = fout[x];
274 #endif
275                         }
276                         tmp->tail += tmp->framesize;
277                 } else {
278                         ast_log(LOG_WARNING, "Out of buffer space\n");
279                         return -1;
280                 }
281                 
282         }
283         return 0;
284 }
285
286 static int lintospeex_framein(struct ast_translator_pvt *tmp, struct ast_frame *f)
287 {
288         /* Just add the frames to our stream */
289         /* XXX We should look at how old the rest of our stream is, and if it
290            is too old, then we should overwrite it entirely, otherwise we can
291            get artifacts of earlier talk that do not belong */
292         if (tmp->tail + f->datalen/2 < sizeof(tmp->buf) / 2) {
293                 memcpy((tmp->buf + tmp->tail), f->data, f->datalen);
294                 tmp->tail += f->datalen/2;
295         } else {
296                 ast_log(LOG_WARNING, "Out of buffer space\n");
297                 return -1;
298         }
299         return 0;
300 }
301
302 static struct ast_frame *lintospeex_frameout(struct ast_translator_pvt *tmp)
303 {
304 #ifndef _SPEEX_TYPES_H
305         float fbuf[1024];
306         int x;
307 #endif
308         int len;
309         int y=0;
310         int is_speech=1;
311         /* We can't work on anything less than a frame in size */
312         if (tmp->tail < tmp->framesize)
313                 return NULL;
314         tmp->f.frametype = AST_FRAME_VOICE;
315         tmp->f.subclass = AST_FORMAT_SPEEX;
316         tmp->f.mallocd = 0;
317         tmp->f.offset = AST_FRIENDLY_OFFSET;
318         tmp->f.src = __PRETTY_FUNCTION__;
319         tmp->f.data = tmp->outbuf;
320         speex_bits_reset(&tmp->bits);
321         while(tmp->tail >= tmp->framesize) {
322 #ifdef _SPEEX_TYPES_H
323                 /* Preprocess audio */
324                 if(preproc)
325                         is_speech = speex_preprocess(tmp->pp, tmp->buf, NULL);
326                 /* Encode a frame of data */
327                 if (is_speech) {
328                         /* If DTX enabled speex_encode returns 0 during silence */
329                         is_speech = speex_encode_int(tmp->speex, tmp->buf, &tmp->bits) || !dtx;
330                 } else {
331                         /* 5 zeros interpreted by Speex as silence (submode 0) */
332                         speex_bits_pack(&tmp->bits, 0, 5);
333                 }
334 #else
335                 /* Convert to floating point */
336                 for (x=0;x<tmp->framesize;x++)
337                         fbuf[x] = tmp->buf[x];
338                 /* Encode a frame of data */
339                 is_speech = speex_encode(tmp->speex, fbuf, &tmp->bits) || !dtx;
340 #endif
341                 /* Assume 8000 Hz -- 20 ms */
342                 tmp->tail -= tmp->framesize;
343                 /* Move the data at the end of the buffer to the front */
344                 if (tmp->tail)
345                         memmove(tmp->buf, tmp->buf + tmp->framesize, tmp->tail * 2);
346                 y++;
347         }
348
349         /* Use AST_FRAME_CNG to signify the start of any silence period */
350         if (!is_speech) {
351                 if (tmp->silent_state) {
352                         return NULL;
353                 } else {
354                         tmp->silent_state = 1;
355                         speex_bits_reset(&tmp->bits);
356                         tmp->f.frametype = AST_FRAME_CNG;
357                 }
358         } else {
359                 tmp->silent_state = 0;
360         }
361
362         /* Terminate bit stream */
363         speex_bits_pack(&tmp->bits, 15, 5);
364         len = speex_bits_write(&tmp->bits, (char *)tmp->outbuf, sizeof(tmp->outbuf));
365         tmp->f.datalen = len;
366         tmp->f.samples = y * 160;
367 #if 0
368         {
369                 static int fd = -1;
370                 if (fd < 0) {
371                         fd = open("speex.raw", O_WRONLY|O_TRUNC|O_CREAT);
372                         if (fd > -1) {
373                                 write(fd, tmp->f.data, tmp->f.datalen);
374                                 close(fd);
375                         }
376                 }
377         }
378 #endif
379         return &tmp->f; 
380 }
381
382 static void speextolin_destroy(struct ast_translator_pvt *pvt)
383 {
384         speex_decoder_destroy(pvt->speex);
385         speex_bits_destroy(&pvt->bits);
386         free(pvt);
387         localusecnt--;
388 }
389
390 static void lintospeex_destroy(struct ast_translator_pvt *pvt)
391 {
392 #ifdef _SPEEX_TYPES_H
393         if (preproc)
394                 speex_preprocess_state_destroy(pvt->pp);
395 #endif
396         speex_encoder_destroy(pvt->speex);
397         speex_bits_destroy(&pvt->bits);
398         free(pvt);
399         localusecnt--;
400 }
401
402 static struct ast_translator speextolin =
403         { "speextolin", 
404            AST_FORMAT_SPEEX, AST_FORMAT_SLINEAR,
405            speextolin_new,
406            speextolin_framein,
407            speextolin_frameout,
408            speextolin_destroy,
409            speextolin_sample
410            };
411
412 static struct ast_translator lintospeex =
413         { "lintospeex", 
414            AST_FORMAT_SLINEAR, AST_FORMAT_SPEEX,
415            lintospeex_new,
416            lintospeex_framein,
417            lintospeex_frameout,
418            lintospeex_destroy,
419            lintospeex_sample
420         };
421
422
423 static void parse_config(void) 
424 {
425         struct ast_config *cfg;
426         struct ast_variable *var;
427         int res;
428         float res_f;
429
430         if ((cfg = ast_config_load("codecs.conf"))) {
431                 if ((var = ast_variable_browse(cfg, "speex"))) {
432                         while (var) {
433                                 if (!strcasecmp(var->name, "quality")) {
434                                         res = abs(atoi(var->value));
435                                         if (res > -1 && res < 11) {
436                                                 if (option_verbose > 2)
437                                                         ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: Setting Quality to %d\n",res);
438                                                 ast_mutex_lock(&localuser_lock);
439                                                 quality = res;
440                                                 ast_mutex_unlock(&localuser_lock);
441                                         } else 
442                                                 ast_log(LOG_ERROR,"Error Quality must be 0-10\n");
443                                 } else if (!strcasecmp(var->name, "complexity")) {
444                                         res = abs(atoi(var->value));
445                                         if (res > -1 && res < 11) {
446                                                 if (option_verbose > 2)
447                                                         ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: Setting Complexity to %d\n",res);
448                                                 ast_mutex_lock(&localuser_lock);
449                                                 complexity = res;
450                                                 ast_mutex_unlock(&localuser_lock);
451                                         } else 
452                                                 ast_log(LOG_ERROR,"Error! Complexity must be 0-10\n");
453                                 } else if (!strcasecmp(var->name, "vbr_quality")) {
454                                         if (sscanf(var->value, "%f", &res_f) == 1 && res_f >= 0 && res_f <= 10) {
455                                                 if (option_verbose > 2)
456                                                         ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: Setting VBR Quality to %f\n",res_f);
457                                                 ast_mutex_lock(&localuser_lock);
458                                                 vbr_quality = res_f;
459                                                 ast_mutex_unlock(&localuser_lock);
460                                         } else
461                                                 ast_log(LOG_ERROR,"Error! VBR Quality must be 0-10\n");
462                                 } else if (!strcasecmp(var->name, "abr_quality")) {
463                                         ast_log(LOG_ERROR,"Error! ABR Quality setting obsolete, set ABR to desired bitrate\n");
464                                 } else if (!strcasecmp(var->name, "enhancement")) {
465                                         ast_mutex_lock(&localuser_lock);
466                                         enhancement = ast_true(var->value) ? 1 : 0;
467                                         if (option_verbose > 2)
468                                                 ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: Perceptual Enhancement Mode. [%s]\n",enhancement ? "on" : "off");
469                                         ast_mutex_unlock(&localuser_lock);
470                                 } else if (!strcasecmp(var->name, "vbr")) {
471                                         ast_mutex_lock(&localuser_lock);
472                                         vbr = ast_true(var->value) ? 1 : 0;
473                                         if (option_verbose > 2)
474                                                 ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: VBR Mode. [%s]\n",vbr ? "on" : "off");
475                                         ast_mutex_unlock(&localuser_lock);
476                                 } else if (!strcasecmp(var->name, "abr")) {
477                                         res = abs(atoi(var->value));
478                                         if (res >= 0) {
479                                                 if (option_verbose > 2) {
480                                                         if (res > 0)
481                                                                 ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: Setting ABR target bitrate to %d\n",res);
482                                                         else
483                                                                 ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: Disabling ABR\n");
484                                                 }
485                                                 ast_mutex_lock(&localuser_lock);
486                                                 abr = res;
487                                                 ast_mutex_unlock(&localuser_lock);
488                                         } else 
489                                                 ast_log(LOG_ERROR,"Error! ABR target bitrate must be >= 0\n");
490                                 } else if (!strcasecmp(var->name, "vad")) {
491                                         ast_mutex_lock(&localuser_lock);
492                                         vad = ast_true(var->value) ? 1 : 0;
493                                         if (option_verbose > 2)
494                                                 ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: VAD Mode. [%s]\n",vad ? "on" : "off");
495                                         ast_mutex_unlock(&localuser_lock);
496                                 } else if (!strcasecmp(var->name, "dtx")) {
497                                         ast_mutex_lock(&localuser_lock);
498                                         dtx = ast_true(var->value) ? 1 : 0;
499                                         if (option_verbose > 2)
500                                                 ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: DTX Mode. [%s]\n",dtx ? "on" : "off");
501                                         ast_mutex_unlock(&localuser_lock);
502                                 } else if (!strcasecmp(var->name, "preprocess")) {
503                                         ast_mutex_lock(&localuser_lock);
504                                         preproc = ast_true(var->value) ? 1 : 0;
505                                         if (option_verbose > 2)
506                                                 ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: Preprocessing. [%s]\n",preproc ? "on" : "off");
507                                         ast_mutex_unlock(&localuser_lock);
508                                 } else if (!strcasecmp(var->name, "pp_vad")) {
509                                         ast_mutex_lock(&localuser_lock);
510                                         pp_vad = ast_true(var->value) ? 1 : 0;
511                                         if (option_verbose > 2)
512                                                 ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: Preprocessor VAD. [%s]\n",pp_vad ? "on" : "off");
513                                         ast_mutex_unlock(&localuser_lock);
514                                 } else if (!strcasecmp(var->name, "pp_agc")) {
515                                         ast_mutex_lock(&localuser_lock);
516                                         pp_agc = ast_true(var->value) ? 1 : 0;
517                                         if (option_verbose > 2)
518                                                 ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: Preprocessor AGC. [%s]\n",pp_agc ? "on" : "off");
519                                         ast_mutex_unlock(&localuser_lock);
520                                 } else if (!strcasecmp(var->name, "pp_agc_level")) {
521                                         if (sscanf(var->value, "%f", &res_f) == 1 && res_f >= 0) {
522                                                 if (option_verbose > 2)
523                                                         ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: Setting preprocessor AGC Level to %f\n",res_f);
524                                                 ast_mutex_lock(&localuser_lock);
525                                                 pp_agc_level = res_f;
526                                                 ast_mutex_unlock(&localuser_lock);
527                                         } else
528                                                 ast_log(LOG_ERROR,"Error! Preprocessor AGC Level must be >= 0\n");
529                                 } else if (!strcasecmp(var->name, "pp_denoise")) {
530                                         ast_mutex_lock(&localuser_lock);
531                                         pp_denoise = ast_true(var->value) ? 1 : 0;
532                                         if (option_verbose > 2)
533                                                 ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: Preprocessor Denoise. [%s]\n",pp_denoise ? "on" : "off");
534                                         ast_mutex_unlock(&localuser_lock);
535                                 } else if (!strcasecmp(var->name, "pp_dereverb")) {
536                                         ast_mutex_lock(&localuser_lock);
537                                         pp_dereverb = ast_true(var->value) ? 1 : 0;
538                                         if (option_verbose > 2)
539                                                 ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: Preprocessor Dereverb. [%s]\n",pp_dereverb ? "on" : "off");
540                                         ast_mutex_unlock(&localuser_lock);
541                                 } else if (!strcasecmp(var->name, "pp_dereverb_decay")) {
542                                         if (sscanf(var->value, "%f", &res_f) == 1 && res_f >= 0) {
543                                                 if (option_verbose > 2)
544                                                         ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: Setting preprocessor Dereverb Decay to %f\n",res_f);
545                                                 ast_mutex_lock(&localuser_lock);
546                                                 pp_dereverb_decay = res_f;
547                                                 ast_mutex_unlock(&localuser_lock);
548                                         } else
549                                                 ast_log(LOG_ERROR,"Error! Preprocessor Dereverb Decay must be >= 0\n");
550                                 } else if (!strcasecmp(var->name, "pp_dereverb_level")) {
551                                         if (sscanf(var->value, "%f", &res_f) == 1 && res_f >= 0) {
552                                                 if (option_verbose > 2)
553                                                         ast_verbose(VERBOSE_PREFIX_3 "CODEC SPEEX: Setting preprocessor Dereverb Level to %f\n",res_f);
554                                                 ast_mutex_lock(&localuser_lock);
555                                                 pp_dereverb_level = res_f;
556                                                 ast_mutex_unlock(&localuser_lock);
557                                         } else
558                                                 ast_log(LOG_ERROR,"Error! Preprocessor Dereverb Level must be >= 0\n");
559                                 }
560                                 var = var->next;
561                         }
562                 }
563                 ast_config_destroy(cfg);
564         }
565 }
566
567 int reload(void) 
568 {
569         parse_config();
570         return 0;
571 }
572
573 int unload_module(void)
574 {
575         int res;
576         ast_mutex_lock(&localuser_lock);
577         res = ast_unregister_translator(&lintospeex);
578         if (!res)
579                 res = ast_unregister_translator(&speextolin);
580         if (localusecnt)
581                 res = -1;
582         ast_mutex_unlock(&localuser_lock);
583         return res;
584 }
585
586 int load_module(void)
587 {
588         int res;
589         parse_config();
590         res=ast_register_translator(&speextolin);
591         if (!res) 
592                 res=ast_register_translator(&lintospeex);
593         else
594                 ast_unregister_translator(&speextolin);
595         return res;
596 }
597
598 char *description(void)
599 {
600         return tdesc;
601 }
602
603 int usecount(void)
604 {
605         int res;
606         STANDARD_USECOUNT(res);
607         return res;
608 }
609
610 char *key()
611 {
612         return ASTERISK_GPL_KEY;
613 }