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