Version 0.1.7 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/2 < 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 the frames */
191         { 
192                 static int fd2 = -1;
193                 if (fd2 == -1) {
194                         fd2 = open("gsm.example", O_WRONLY | O_CREAT | O_TRUNC, 0644);
195                 }
196                 write(fd2, tmp->f.data, tmp->f.datalen);
197         }               
198 #endif
199         return &tmp->f; 
200 }
201
202 static void gsm_destroy_stuff(struct ast_translator_pvt *pvt)
203 {
204         free(pvt);
205         localusecnt--;
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 }