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