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