Fri Mar 7 07:00:00 CET 2003
[asterisk/asterisk.git] / codecs / codec_gsm.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Translate between signed linear and Global System for Mobile Communications (GSM)
5  *
6  * The GSM code is from TOAST.  Copyright information for that package is available
7  * in  the GSM directory.
8  * 
9  * Copyright (C) 1999, Mark Spencer
10  *
11  * Mark Spencer <markster@linux-support.net>
12  *
13  * This program is free software, distributed under the terms of
14  * the GNU General Public License
15  */
16
17 #define TYPE_SILENCE     0x2
18 #define TYPE_HIGH        0x0
19 #define TYPE_LOW         0x1
20 #define TYPE_MASK        0x3
21
22 #include <asterisk/lock.h>
23 #include <asterisk/translate.h>
24 #include <asterisk/module.h>
25 #include <asterisk/logger.h>
26 #include <asterisk/channel.h>
27 #include <pthread.h>
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 "gsm/inc/gsm.h"
36 #include "../formats/msgsm.h"
37
38 /* Sample frame data */
39 #include "slin_gsm_ex.h"
40 #include "gsm_slin_ex.h"
41
42 static pthread_mutex_t localuser_lock = AST_MUTEX_INITIALIZER;
43 static int localusecnt=0;
44
45 static char *tdesc = "GSM/PCM16 (signed linear) Codec Translator";
46
47 struct ast_translator_pvt {
48         gsm gsm;
49         struct ast_frame f;
50         /* Space to build offset */
51         char offset[AST_FRIENDLY_OFFSET];
52         /* Buffer for our outgoing frame */
53         short outbuf[8000];
54         /* Enough to store a full second */
55         short buf[8000];
56         int tail;
57 };
58
59 #define gsm_coder_pvt ast_translator_pvt
60
61 static struct ast_translator_pvt *gsm_new(void)
62 {
63         struct gsm_coder_pvt *tmp;
64         tmp = malloc(sizeof(struct gsm_coder_pvt));
65         if (tmp) {
66                 if (!(tmp->gsm = gsm_create())) {
67                         free(tmp);
68                         tmp = NULL;
69                 }
70                 tmp->tail = 0;
71                 localusecnt++;
72         }
73         return tmp;
74 }
75
76 static struct ast_frame *lintogsm_sample(void)
77 {
78         static struct ast_frame f;
79         f.frametype = AST_FRAME_VOICE;
80         f.subclass = AST_FORMAT_SLINEAR;
81         f.datalen = sizeof(slin_gsm_ex);
82         /* Assume 8000 Hz */
83         f.samples = sizeof(slin_gsm_ex)/2;
84         f.mallocd = 0;
85         f.offset = 0;
86         f.src = __PRETTY_FUNCTION__;
87         f.data = slin_gsm_ex;
88         return &f;
89 }
90
91 static struct ast_frame *gsmtolin_sample(void)
92 {
93         static struct ast_frame f;
94         f.frametype = AST_FRAME_VOICE;
95         f.subclass = AST_FORMAT_GSM;
96         f.datalen = sizeof(gsm_slin_ex);
97         /* All frames are 20 ms long */
98         f.samples = 160;
99         f.mallocd = 0;
100         f.offset = 0;
101         f.src = __PRETTY_FUNCTION__;
102         f.data = gsm_slin_ex;
103         return &f;
104 }
105
106 static struct ast_frame *gsmtolin_frameout(struct ast_translator_pvt *tmp)
107 {
108         if (!tmp->tail)
109                 return NULL;
110         /* Signed linear is no particular frame size, so just send whatever
111            we have in the buffer in one lump sum */
112         tmp->f.frametype = AST_FRAME_VOICE;
113         tmp->f.subclass = AST_FORMAT_SLINEAR;
114         tmp->f.datalen = tmp->tail * 2;
115         /* Assume 8000 Hz */
116         tmp->f.samples = tmp->tail;
117         tmp->f.mallocd = 0;
118         tmp->f.offset = AST_FRIENDLY_OFFSET;
119         tmp->f.src = __PRETTY_FUNCTION__;
120         tmp->f.data = tmp->buf;
121         /* Reset tail pointer */
122         tmp->tail = 0;
123
124         return &tmp->f; 
125 }
126
127 static int gsmtolin_framein(struct ast_translator_pvt *tmp, struct ast_frame *f)
128 {
129         /* Assuming there's space left, decode into the current buffer at
130            the tail location.  Read in as many frames as there are */
131         int x;
132         unsigned char data[66];
133         int msgsm=0;
134         
135         if ((f->datalen % 33) && (f->datalen % 65)) {
136                 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);
137                 return -1;
138         }
139         
140         if (f->datalen % 65 == 0) 
141                 msgsm = 1;
142                 
143         for (x=0;x<f->datalen;x+=(msgsm ? 65 : 33)) {
144                 if (msgsm) {
145                         /* Translate MSGSM format to Real GSM format before feeding in */
146                         conv65(f->data + x, data);
147                         if (tmp->tail + 320 < sizeof(tmp->buf)/2) {     
148                                 if (gsm_decode(tmp->gsm, data, tmp->buf + tmp->tail)) {
149                                         ast_log(LOG_WARNING, "Invalid GSM data (1)\n");
150                                         return -1;
151                                 }
152                                 tmp->tail+=160;
153                                 if (gsm_decode(tmp->gsm, data + 33, tmp->buf + tmp->tail)) {
154                                         ast_log(LOG_WARNING, "Invalid GSM data (2)\n");
155                                         return -1;
156                                 }
157                                 tmp->tail+=160;
158                         } else {
159                                 ast_log(LOG_WARNING, "Out of (MS) buffer space\n");
160                                 return -1;
161                         }
162                 } else {
163                         if (tmp->tail + 160 < sizeof(tmp->buf)/2) {     
164                                 if (gsm_decode(tmp->gsm, f->data + x, tmp->buf + tmp->tail)) {
165                                         ast_log(LOG_WARNING, "Invalid GSM data\n");
166                                         return -1;
167                                 }
168                                 tmp->tail+=160;
169                         } else {
170                                 ast_log(LOG_WARNING, "Out of buffer space\n");
171                                 return -1;
172                         }
173                 }
174         }
175         return 0;
176 }
177
178 static int lintogsm_framein(struct ast_translator_pvt *tmp, struct ast_frame *f)
179 {
180         /* Just add the frames to our stream */
181         /* XXX We should look at how old the rest of our stream is, and if it
182            is too old, then we should overwrite it entirely, otherwise we can
183            get artifacts of earlier talk that do not belong */
184         if (tmp->tail + f->datalen/2 < sizeof(tmp->buf) / 2) {
185                 memcpy((tmp->buf + tmp->tail), f->data, f->datalen);
186                 tmp->tail += f->datalen/2;
187         } else {
188                 ast_log(LOG_WARNING, "Out of buffer space\n");
189                 return -1;
190         }
191         return 0;
192 }
193
194 static struct ast_frame *lintogsm_frameout(struct ast_translator_pvt *tmp)
195 {
196         int x=0;
197         /* We can't work on anything less than a frame in size */
198         if (tmp->tail < 160)
199                 return NULL;
200         tmp->f.frametype = AST_FRAME_VOICE;
201         tmp->f.subclass = AST_FORMAT_GSM;
202         tmp->f.mallocd = 0;
203         tmp->f.offset = AST_FRIENDLY_OFFSET;
204         tmp->f.src = __PRETTY_FUNCTION__;
205         tmp->f.data = tmp->outbuf;
206         while(tmp->tail >= 160) {
207                 if ((x+1) * 33 >= sizeof(tmp->outbuf)) {
208                         ast_log(LOG_WARNING, "Out of buffer space\n");
209                         break;
210                 }
211                 /* Encode a frame of data */
212                 gsm_encode(tmp->gsm, tmp->buf, ((gsm_byte *) tmp->outbuf) + (x * 33));
213                 /* Assume 8000 Hz -- 20 ms */
214                 tmp->tail -= 160;
215                 /* Move the data at the end of the buffer to the front */
216                 if (tmp->tail)
217                         memmove(tmp->buf, tmp->buf + 160, tmp->tail * 2);
218                 x++;
219         }
220         tmp->f.datalen = x * 33;
221         tmp->f.samples = x * 160;
222         return &tmp->f; 
223 }
224
225 static void gsm_destroy_stuff(struct ast_translator_pvt *pvt)
226 {
227         if (pvt->gsm)
228                 gsm_destroy(pvt->gsm);
229         free(pvt);
230         localusecnt--;
231 }
232
233 static struct ast_translator gsmtolin =
234         { "gsmtolin", 
235            AST_FORMAT_GSM, AST_FORMAT_SLINEAR,
236            gsm_new,
237            gsmtolin_framein,
238            gsmtolin_frameout,
239            gsm_destroy_stuff,
240            gsmtolin_sample
241            };
242
243 static struct ast_translator lintogsm =
244         { "lintogsm", 
245            AST_FORMAT_SLINEAR, AST_FORMAT_GSM,
246            gsm_new,
247            lintogsm_framein,
248            lintogsm_frameout,
249            gsm_destroy_stuff,
250            lintogsm_sample
251            };
252
253 int unload_module(void)
254 {
255         int res;
256         ast_pthread_mutex_lock(&localuser_lock);
257         res = ast_unregister_translator(&lintogsm);
258         if (!res)
259                 res = ast_unregister_translator(&gsmtolin);
260         if (localusecnt)
261                 res = -1;
262         ast_pthread_mutex_unlock(&localuser_lock);
263         return res;
264 }
265
266 int load_module(void)
267 {
268         int res;
269         res=ast_register_translator(&gsmtolin);
270         if (!res) 
271                 res=ast_register_translator(&lintogsm);
272         else
273                 ast_unregister_translator(&gsmtolin);
274         return res;
275 }
276
277 char *description(void)
278 {
279         return tdesc;
280 }
281
282 int usecount(void)
283 {
284         int res;
285         STANDARD_USECOUNT(res);
286         return res;
287 }
288
289 char *key()
290 {
291         return ASTERISK_GPL_KEY;
292 }