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