Version 0.1.9 from FTP
[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/translate.h>
23 #include <asterisk/module.h>
24 #include <asterisk/logger.h>
25 #include <asterisk/channel.h>
26 #include <pthread.h>
27 #include <fcntl.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <netinet/in.h>
31 #include <string.h>
32 #include <stdio.h>
33
34 #include "gsm/inc/gsm.h"
35
36 /* Sample frame data */
37 #include "slin_gsm_ex.h"
38 #include "gsm_slin_ex.h"
39
40 static pthread_mutex_t localuser_lock = PTHREAD_MUTEX_INITIALIZER;
41 static int localusecnt=0;
42
43 static char *tdesc = "GSM/PCM16 (signed linear) Codec Translator";
44
45 struct ast_translator_pvt {
46         gsm gsm;
47         struct ast_frame f;
48         /* Space to build offset */
49         char offset[AST_FRIENDLY_OFFSET];
50         /* Buffer for our outgoing frame */
51         short outbuf[8000];
52         /* Enough to store a full second */
53         short buf[8000];
54         int tail;
55 };
56
57 #define gsm_coder_pvt ast_translator_pvt
58
59 static struct ast_translator_pvt *gsm_new()
60 {
61         struct gsm_coder_pvt *tmp;
62         tmp = malloc(sizeof(struct gsm_coder_pvt));
63         if (tmp) {
64                 if (!(tmp->gsm = gsm_create())) {
65                         free(tmp);
66                         tmp = NULL;
67                 }
68                 tmp->tail = 0;
69                 localusecnt++;
70         }
71         return tmp;
72 }
73
74 static struct ast_frame *lintogsm_sample()
75 {
76         static struct ast_frame f;
77         f.frametype = AST_FRAME_VOICE;
78         f.subclass = AST_FORMAT_SLINEAR;
79         f.datalen = sizeof(slin_gsm_ex);
80         /* Assume 8000 Hz */
81         f.timelen = sizeof(slin_gsm_ex)/16;
82         f.mallocd = 0;
83         f.offset = 0;
84         f.src = __PRETTY_FUNCTION__;
85         f.data = slin_gsm_ex;
86         return &f;
87 }
88
89 static struct ast_frame *gsmtolin_sample()
90 {
91         static struct ast_frame f;
92         f.frametype = AST_FRAME_VOICE;
93         f.subclass = AST_FORMAT_GSM;
94         f.datalen = sizeof(gsm_slin_ex);
95         /* All frames are 20 ms long */
96         f.timelen = 20;
97         f.mallocd = 0;
98         f.offset = 0;
99         f.src = __PRETTY_FUNCTION__;
100         f.data = gsm_slin_ex;
101         return &f;
102 }
103
104 static struct ast_frame *gsmtolin_frameout(struct ast_translator_pvt *tmp)
105 {
106         if (!tmp->tail)
107                 return NULL;
108         /* Signed linear is no particular frame size, so just send whatever
109            we have in the buffer in one lump sum */
110         tmp->f.frametype = AST_FRAME_VOICE;
111         tmp->f.subclass = AST_FORMAT_SLINEAR;
112         tmp->f.datalen = tmp->tail * 2;
113         /* Assume 8000 Hz */
114         tmp->f.timelen = tmp->tail / 8;
115         tmp->f.mallocd = 0;
116         tmp->f.offset = AST_FRIENDLY_OFFSET;
117         tmp->f.src = __PRETTY_FUNCTION__;
118         tmp->f.data = tmp->buf;
119         /* Reset tail pointer */
120         tmp->tail = 0;
121
122         return &tmp->f; 
123 }
124
125 static int gsmtolin_framein(struct ast_translator_pvt *tmp, struct ast_frame *f)
126 {
127         /* Assuming there's space left, decode into the current buffer at
128            the tail location.  Read in as many frames as there are */
129         int x;
130         if (f->datalen % 33) {
131                 ast_log(LOG_WARNING, "Huh?  A GSM frame that isn't a multiple of 33 bytes long from %s (%d)?\n", f->src, f->datalen);
132                 return -1;
133         }
134         for (x=0;x<f->datalen;x+=33) {
135                 if (tmp->tail + 160 < sizeof(tmp->buf)/2) {     
136                         if (gsm_decode(tmp->gsm, f->data + x, tmp->buf + tmp->tail)) {
137                                 ast_log(LOG_WARNING, "Invalid GSM data\n");
138                                 return -1;
139                         }
140                         tmp->tail+=160;
141                 } else {
142                         ast_log(LOG_WARNING, "Out of buffer space\n");
143                         return -1;
144                 }
145         }
146         return 0;
147 }
148
149 static int lintogsm_framein(struct ast_translator_pvt *tmp, struct ast_frame *f)
150 {
151         /* Just add the frames to our stream */
152         /* XXX We should look at how old the rest of our stream is, and if it
153            is too old, then we should overwrite it entirely, otherwise we can
154            get artifacts of earlier talk that do not belong */
155         if (tmp->tail + f->datalen/2 < sizeof(tmp->buf) / 2) {
156                 memcpy((tmp->buf + tmp->tail), f->data, f->datalen);
157                 tmp->tail += f->datalen/2;
158         } else {
159                 ast_log(LOG_WARNING, "Out of buffer space\n");
160                 return -1;
161         }
162         return 0;
163 }
164
165 static struct ast_frame *lintogsm_frameout(struct ast_translator_pvt *tmp)
166 {
167         int x=0;
168         /* We can't work on anything less than a frame in size */
169         if (tmp->tail < 160)
170                 return NULL;
171         tmp->f.frametype = AST_FRAME_VOICE;
172         tmp->f.subclass = AST_FORMAT_GSM;
173         tmp->f.mallocd = 0;
174         tmp->f.offset = AST_FRIENDLY_OFFSET;
175         tmp->f.src = __PRETTY_FUNCTION__;
176         tmp->f.data = tmp->outbuf;
177         while(tmp->tail >= 160) {
178                 if ((x+1) * 33 >= sizeof(tmp->outbuf)) {
179                         ast_log(LOG_WARNING, "Out of buffer space\n");
180                         break;
181                 }
182                 /* Encode a frame of data */
183                 gsm_encode(tmp->gsm, tmp->buf, ((gsm_byte *) tmp->outbuf) + (x * 33));
184                 /* Assume 8000 Hz -- 20 ms */
185                 tmp->tail -= 160;
186                 /* Move the data at the end of the buffer to the front */
187                 if (tmp->tail)
188                         memmove(tmp->buf, tmp->buf + 160, tmp->tail * 2);
189                 x++;
190         }
191         tmp->f.datalen = x * 33;
192         tmp->f.timelen = x * 20;
193         return &tmp->f; 
194 }
195
196 static void gsm_destroy_stuff(struct ast_translator_pvt *pvt)
197 {
198         free(pvt);
199         localusecnt--;
200 }
201
202 static struct ast_translator gsmtolin =
203         { "gsmtolin", 
204            AST_FORMAT_GSM, AST_FORMAT_SLINEAR,
205            gsm_new,
206            gsmtolin_framein,
207            gsmtolin_frameout,
208            gsm_destroy_stuff,
209            gsmtolin_sample
210            };
211
212 static struct ast_translator lintogsm =
213         { "lintogsm", 
214            AST_FORMAT_SLINEAR, AST_FORMAT_GSM,
215            gsm_new,
216            lintogsm_framein,
217            lintogsm_frameout,
218            gsm_destroy_stuff,
219            lintogsm_sample
220            };
221
222 int unload_module(void)
223 {
224         int res;
225         ast_pthread_mutex_lock(&localuser_lock);
226         res = ast_unregister_translator(&lintogsm);
227         if (!res)
228                 res = ast_unregister_translator(&gsmtolin);
229         if (localusecnt)
230                 res = -1;
231         ast_pthread_mutex_unlock(&localuser_lock);
232         return res;
233 }
234
235 int load_module(void)
236 {
237         int res;
238         res=ast_register_translator(&gsmtolin);
239         if (!res) 
240                 res=ast_register_translator(&lintogsm);
241         else
242                 ast_unregister_translator(&gsmtolin);
243         return res;
244 }
245
246 char *description(void)
247 {
248         return tdesc;
249 }
250
251 int usecount(void)
252 {
253         int res;
254         STANDARD_USECOUNT(res);
255         return res;
256 }
257
258 char *key()
259 {
260         return ASTERISK_GPL_KEY;
261 }