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