Version 0.1.1 from FTP
[asterisk/asterisk.git] / apps / app_intercom.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Use /dev/dsp as an intercom.
5  * 
6  * Copyright (C) 1999, Adtran Inc. and Linux Support Services, LLC
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13  
14 #include <asterisk/file.h>
15 #include <asterisk/frame.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/pbx.h>
19 #include <asterisk/module.h>
20 #include <asterisk/translate.h>
21 #include <unistd.h>
22 #include <errno.h>
23 #include <sys/ioctl.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <pthread.h>
27 #include <sys/time.h>
28 #include <linux/soundcard.h>
29 #include <netinet/in.h>
30
31 #define DEV_DSP "/dev/dsp"
32
33 /* Number of 32 byte buffers -- each buffer is 2 ms */
34 #define BUFFER_SIZE 32
35
36 static char *tdesc = "Intercom using /dev/dsp for output";
37
38 static char *app = "Intercom";
39
40 STANDARD_LOCAL_USER;
41
42 LOCAL_USER_DECL;
43
44 static pthread_mutex_t sound_lock = PTHREAD_MUTEX_INITIALIZER;
45 static int sound = -1;
46
47 static int write_audio(short *data, int len)
48 {
49         int res;
50         struct audio_buf_info info;
51         pthread_mutex_lock(&sound_lock);
52         if (sound < 0) {
53                 ast_log(LOG_WARNING, "Sound device closed?\n");
54                 pthread_mutex_unlock(&sound_lock);
55                 return -1;
56         }
57     if (ioctl(sound, SNDCTL_DSP_GETOSPACE, &info)) {
58                 ast_log(LOG_WARNING, "Unable to read output space\n");
59                 pthread_mutex_unlock(&sound_lock);
60         return -1;
61     }
62                 res = write(sound, data, len);
63         pthread_mutex_unlock(&sound_lock);
64         return res;
65 }
66
67 static int create_audio()
68 {
69         int fmt, desired, res, fd;
70         fd = open(DEV_DSP, O_WRONLY);
71         if (fd < 0) {
72                 ast_log(LOG_WARNING, "Unable to open %s: %s\n", DEV_DSP, strerror(errno));
73                 close(fd);
74                 return -1;
75         }
76         fmt = AFMT_S16_LE;
77         res = ioctl(fd, SNDCTL_DSP_SETFMT, &fmt);
78         if (res < 0) {
79                 ast_log(LOG_WARNING, "Unable to set format to 16-bit signed\n");
80                 close(fd);
81                 return -1;
82         }
83         fmt = 0;
84         res = ioctl(fd, SNDCTL_DSP_STEREO, &fmt);
85         if (res < 0) {
86                 ast_log(LOG_WARNING, "Failed to set audio device to mono\n");
87                 close(fd);
88                 return -1;
89         }
90         /* 8000 Hz desired */
91         desired = 8000;
92         fmt = desired;
93         res = ioctl(fd, SNDCTL_DSP_SPEED, &fmt);
94         if (res < 0) {
95                 ast_log(LOG_WARNING, "Failed to set audio device to mono\n");
96                 close(fd);
97                 return -1;
98         }
99         if (fmt != desired) {
100                 ast_log(LOG_WARNING, "Requested %d Hz, got %d Hz -- sound may be choppy\n");
101         }
102 #if 1
103         /* 2 bytes * 15 units of 2^5 = 32 bytes per buffer */
104         fmt = ((BUFFER_SIZE) << 16) | (0x0005);
105         res = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &fmt);
106         if (res < 0) {
107                 ast_log(LOG_WARNING, "Unable to set fragment size -- sound may be choppy\n");
108         }
109 #endif
110         sound = fd;
111         return 0;
112 }
113
114 static int intercom_exec(struct ast_channel *chan, void *data)
115 {
116         int res = 0;
117         struct localuser *u;
118         struct ast_frame *f;
119         struct ast_channel *trans;
120         if (!data) {
121                 ast_log(LOG_WARNING, "Playback requires an argument (filename)\n");
122                 return -1;
123         }
124         LOCAL_USER_ADD(u);
125         /* See if we need a translator */
126         if (!(chan->format & AST_FORMAT_SLINEAR)) 
127                 trans = ast_translator_create(chan, AST_FORMAT_SLINEAR, AST_DIRECTION_IN);
128         else
129                 trans = chan;
130         if (trans) {
131                 /* Read packets from the channel */
132                 while(!res) {
133                         res = ast_waitfor(trans, -1);
134                         if (res > 0) {
135                                 res = 0;
136                                 f = ast_read(trans);
137                                 if (f) {
138                                         if (f->frametype == AST_FRAME_DTMF) {
139                                                 ast_frfree(f);
140                                                 break;
141                                         } else {
142                                                 if (f->frametype == AST_FRAME_VOICE) {
143                                                         if (f->subclass == AST_FORMAT_SLINEAR) {
144                                                                 res = write_audio(f->data, f->datalen);
145                                                                 if (res > 0)
146                                                                         res = 0;
147                                                         } else
148                                                                 ast_log(LOG_DEBUG, "Unable to handle non-signed linear frame (%d)\n", f->subclass);
149                                                 }
150                                         }
151                                         ast_frfree(f);
152                                 } else
153                                         res = -1;
154                         }
155                 }
156                 if (trans != chan)
157                         ast_translator_destroy(trans);
158         } else
159                 ast_log(LOG_WARNING, "Unable to build translator to signed linear format on '%s'\n", chan->name);
160         LOCAL_USER_REMOVE(u);
161         return res;
162 }
163
164 int unload_module(void)
165 {
166         STANDARD_HANGUP_LOCALUSERS;
167         if (sound > -1)
168                 close(sound);
169         return ast_unregister_application(app);
170 }
171
172 int load_module(void)
173 {
174         if (create_audio())
175                 return -1;
176         return ast_register_application(app, intercom_exec);
177 }
178
179 char *description(void)
180 {
181         return tdesc;
182 }
183
184 int usecount(void)
185 {
186         int res;
187         STANDARD_USECOUNT(res);
188         return res;
189 }