fix compiler warnings
[asterisk/asterisk.git] / codecs / codec_gsm.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * The GSM code is from TOAST.  Copyright information for that package is available
5  * in the GSM directory.
6  *
7  * Copyright (C) 1999 - 2005, Digium, Inc.
8  *
9  * Mark Spencer <markster@digium.com>
10  *
11  * See http://www.asterisk.org for more information about
12  * the Asterisk project. Please do not directly contact
13  * any of the maintainers of this project for assistance;
14  * the project provides a web site, mailing lists and IRC
15  * channels for your use.
16  *
17  * This program is free software, distributed under the terms of
18  * the GNU General Public License Version 2. See the LICENSE file
19  * at the top of the source tree.
20  */
21
22 /*! \file
23  *
24  * \brief Translate between signed linear and Global System for Mobile Communications (GSM)
25  *
26  */
27
28 #include <fcntl.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <netinet/in.h>
32 #include <string.h>
33 #include <stdio.h>
34
35 #include "asterisk.h"
36
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38
39 #include "asterisk/lock.h"
40 #include "asterisk/translate.h"
41 #include "asterisk/config.h"
42 #include "asterisk/options.h"
43 #include "asterisk/module.h"
44 #include "asterisk/logger.h"
45 #include "asterisk/channel.h"
46
47 #include "gsm/inc/gsm.h"
48 #include "../formats/msgsm.h"
49
50 /* Sample frame data */
51 #include "slin_gsm_ex.h"
52 #include "gsm_slin_ex.h"
53
54 AST_MUTEX_DEFINE_STATIC(localuser_lock);
55 static int localusecnt=0;
56
57 static char *tdesc = "GSM/PCM16 (signed linear) Codec Translator";
58
59 static int useplc = 0;
60
61 struct ast_translator_pvt {
62         gsm gsm;
63         struct ast_frame f;
64         /* Space to build offset */
65         char offset[AST_FRIENDLY_OFFSET];
66         /* Buffer for our outgoing frame */
67         short outbuf[8000];
68         /* Enough to store a full second */
69         short buf[8000];
70         int tail;
71         plc_state_t plc;
72 };
73
74 #define gsm_coder_pvt ast_translator_pvt
75
76 static struct ast_translator_pvt *gsm_new(void)
77 {
78         struct gsm_coder_pvt *tmp;
79         tmp = malloc(sizeof(struct gsm_coder_pvt));
80         if (tmp) {
81                 if (!(tmp->gsm = gsm_create())) {
82                         free(tmp);
83                         tmp = NULL;
84                 }
85                 tmp->tail = 0;
86                 plc_init(&tmp->plc);
87                 localusecnt++;
88         }
89         return tmp;
90 }
91
92 static struct ast_frame *lintogsm_sample(void)
93 {
94         static struct ast_frame f;
95         f.frametype = AST_FRAME_VOICE;
96         f.subclass = AST_FORMAT_SLINEAR;
97         f.datalen = sizeof(slin_gsm_ex);
98         /* Assume 8000 Hz */
99         f.samples = sizeof(slin_gsm_ex)/2;
100         f.mallocd = 0;
101         f.offset = 0;
102         f.src = __PRETTY_FUNCTION__;
103         f.data = slin_gsm_ex;
104         return &f;
105 }
106
107 static struct ast_frame *gsmtolin_sample(void)
108 {
109         static struct ast_frame f;
110         f.frametype = AST_FRAME_VOICE;
111         f.subclass = AST_FORMAT_GSM;
112         f.datalen = sizeof(gsm_slin_ex);
113         /* All frames are 20 ms long */
114         f.samples = 160;
115         f.mallocd = 0;
116         f.offset = 0;
117         f.src = __PRETTY_FUNCTION__;
118         f.data = gsm_slin_ex;
119         return &f;
120 }
121
122 static struct ast_frame *gsmtolin_frameout(struct ast_translator_pvt *tmp)
123 {
124         if (!tmp->tail)
125                 return NULL;
126         /* Signed linear is no particular frame size, so just send whatever
127            we have in the buffer in one lump sum */
128         tmp->f.frametype = AST_FRAME_VOICE;
129         tmp->f.subclass = AST_FORMAT_SLINEAR;
130         tmp->f.datalen = tmp->tail * 2;
131         /* Assume 8000 Hz */
132         tmp->f.samples = tmp->tail;
133         tmp->f.mallocd = 0;
134         tmp->f.offset = AST_FRIENDLY_OFFSET;
135         tmp->f.src = __PRETTY_FUNCTION__;
136         tmp->f.data = tmp->buf;
137         /* Reset tail pointer */
138         tmp->tail = 0;
139
140         return &tmp->f; 
141 }
142
143 static int gsmtolin_framein(struct ast_translator_pvt *tmp, struct ast_frame *f)
144 {
145         /* Assuming there's space left, decode into the current buffer at
146            the tail location.  Read in as many frames as there are */
147         int x;
148         unsigned char data[66];
149         int msgsm=0;
150         
151         if(f->datalen == 0) { /* perform PLC with nominal framesize of 20ms/160 samples */
152               if((tmp->tail + 160) > sizeof(tmp->buf) / 2) {
153                   ast_log(LOG_WARNING, "Out of buffer space\n");
154                   return -1;
155               }
156               if(useplc) {
157                   plc_fillin(&tmp->plc, tmp->buf+tmp->tail, 160);
158                   tmp->tail += 160;
159               }
160               return 0;
161         }
162
163         if ((f->datalen % 33) && (f->datalen % 65)) {
164                 ast_log(LOG_WARNING, "Huh?  A GSM frame that isn't a multiple of 33 or 65 bytes long from %s (%d)?\n", f->src, f->datalen);
165                 return -1;
166         }
167         
168         if (f->datalen % 65 == 0) 
169                 msgsm = 1;
170                 
171         for (x=0;x<f->datalen;x+=(msgsm ? 65 : 33)) {
172                 if (msgsm) {
173                         /* Translate MSGSM format to Real GSM format before feeding in */
174                         conv65(f->data + x, data);
175                         if (tmp->tail + 320 < sizeof(tmp->buf)/2) {     
176                                 if (gsm_decode(tmp->gsm, data, tmp->buf + tmp->tail)) {
177                                         ast_log(LOG_WARNING, "Invalid GSM data (1)\n");
178                                         return -1;
179                                 }
180                                 tmp->tail+=160;
181                                 if (gsm_decode(tmp->gsm, data + 33, tmp->buf + tmp->tail)) {
182                                         ast_log(LOG_WARNING, "Invalid GSM data (2)\n");
183                                         return -1;
184                                 }
185                                 tmp->tail+=160;
186                         } else {
187                                 ast_log(LOG_WARNING, "Out of (MS) buffer space\n");
188                                 return -1;
189                         }
190                 } else {
191                         if (tmp->tail + 160 < sizeof(tmp->buf)/2) {     
192                                 if (gsm_decode(tmp->gsm, f->data + x, tmp->buf + tmp->tail)) {
193                                         ast_log(LOG_WARNING, "Invalid GSM data\n");
194                                         return -1;
195                                 }
196                                 tmp->tail+=160;
197                         } else {
198                                 ast_log(LOG_WARNING, "Out of buffer space\n");
199                                 return -1;
200                         }
201                 }
202         }
203
204         /* just add the last 20ms frame; there must have been at least one */
205         if(useplc) plc_rx(&tmp->plc, tmp->buf+tmp->tail-160, 160);
206
207         return 0;
208 }
209
210 static int lintogsm_framein(struct ast_translator_pvt *tmp, struct ast_frame *f)
211 {
212         /* Just add the frames to our stream */
213         /* XXX We should look at how old the rest of our stream is, and if it
214            is too old, then we should overwrite it entirely, otherwise we can
215            get artifacts of earlier talk that do not belong */
216         if (tmp->tail + f->datalen/2 < sizeof(tmp->buf) / 2) {
217                 memcpy((tmp->buf + tmp->tail), f->data, f->datalen);
218                 tmp->tail += f->datalen/2;
219         } else {
220                 ast_log(LOG_WARNING, "Out of buffer space\n");
221                 return -1;
222         }
223         return 0;
224 }
225
226 static struct ast_frame *lintogsm_frameout(struct ast_translator_pvt *tmp)
227 {
228         int x=0;
229         /* We can't work on anything less than a frame in size */
230         if (tmp->tail < 160)
231                 return NULL;
232         tmp->f.frametype = AST_FRAME_VOICE;
233         tmp->f.subclass = AST_FORMAT_GSM;
234         tmp->f.mallocd = 0;
235         tmp->f.offset = AST_FRIENDLY_OFFSET;
236         tmp->f.src = __PRETTY_FUNCTION__;
237         tmp->f.data = tmp->outbuf;
238         while(tmp->tail >= 160) {
239                 if ((x+1) * 33 >= sizeof(tmp->outbuf)) {
240                         ast_log(LOG_WARNING, "Out of buffer space\n");
241                         break;
242                 }
243                 /* Encode a frame of data */
244                 gsm_encode(tmp->gsm, tmp->buf, ((gsm_byte *) tmp->outbuf) + (x * 33));
245                 /* Assume 8000 Hz -- 20 ms */
246                 tmp->tail -= 160;
247                 /* Move the data at the end of the buffer to the front */
248                 if (tmp->tail)
249                         memmove(tmp->buf, tmp->buf + 160, tmp->tail * 2);
250                 x++;
251         }
252         tmp->f.datalen = x * 33;
253         tmp->f.samples = x * 160;
254         return &tmp->f; 
255 }
256
257 static void gsm_destroy_stuff(struct ast_translator_pvt *pvt)
258 {
259         if (pvt->gsm)
260                 gsm_destroy(pvt->gsm);
261         free(pvt);
262         localusecnt--;
263 }
264
265 static struct ast_translator gsmtolin =
266         { "gsmtolin", 
267            AST_FORMAT_GSM, AST_FORMAT_SLINEAR,
268            gsm_new,
269            gsmtolin_framein,
270            gsmtolin_frameout,
271            gsm_destroy_stuff,
272            gsmtolin_sample
273            };
274
275 static struct ast_translator lintogsm =
276         { "lintogsm", 
277            AST_FORMAT_SLINEAR, AST_FORMAT_GSM,
278            gsm_new,
279            lintogsm_framein,
280            lintogsm_frameout,
281            gsm_destroy_stuff,
282            lintogsm_sample
283            };
284
285
286 static void parse_config(void)
287 {
288         struct ast_config *cfg;
289         struct ast_variable *var;
290         if ((cfg = ast_config_load("codecs.conf"))) {
291                 if ((var = ast_variable_browse(cfg, "plc"))) {
292                         while (var) {
293                                if (!strcasecmp(var->name, "genericplc")) {
294                                        useplc = ast_true(var->value) ? 1 : 0;
295                                        if (option_verbose > 2)
296                                                ast_verbose(VERBOSE_PREFIX_3 "codec_gsm: %susing generic PLC\n", useplc ? "" : "not ");
297                                }
298                                var = var->next;
299                         }
300                 }
301                 ast_config_destroy(cfg);
302         }
303 }
304
305 int reload(void)
306 {
307         parse_config();
308         return 0;
309 }
310
311 int unload_module(void)
312 {
313         int res;
314         ast_mutex_lock(&localuser_lock);
315         res = ast_unregister_translator(&lintogsm);
316         if (!res)
317                 res = ast_unregister_translator(&gsmtolin);
318         if (localusecnt)
319                 res = -1;
320         ast_mutex_unlock(&localuser_lock);
321         return res;
322 }
323
324 int load_module(void)
325 {
326         int res;
327         parse_config();
328         res=ast_register_translator(&gsmtolin);
329         if (!res) 
330                 res=ast_register_translator(&lintogsm);
331         else
332                 ast_unregister_translator(&gsmtolin);
333         return res;
334 }
335
336 char *description(void)
337 {
338         return tdesc;
339 }
340
341 int usecount(void)
342 {
343         int res;
344         STANDARD_USECOUNT(res);
345         return res;
346 }
347
348 char *key()
349 {
350         return ASTERISK_GPL_KEY;
351 }