2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * Updated for Mac OSX CoreAudio
9 * by Josh Roberson <josh@asteriasgi.com>
11 * See http://www.asterisk.org for more information about
12 * the Asterisk project. Please do not directly contact
13 * any of the maintainers of this project for assistance;
14 * the project provides a web site, mailing lists and IRC
15 * channels for your use.
17 * This program is free software, distributed under the terms of
18 * the GNU General Public License Version 2. See the LICENSE file
19 * at the top of the source tree.
26 * \author Mark Spencer <markster@digium.com>
28 * Updated for Mac OSX CoreAudio
29 * \arg Josh Roberson <josh@asteriasgi.com>
31 * \note Specially written for Malcolm Davenport, but I think I'll use it too
32 * Connects to the Asterisk Manager Interface, AMI, and listens for events
33 * on certain devices. If a phone call is connected to one of the devices (phones)
34 * the local sound is muted to a lower volume during the call.
39 #include <CoreAudio/AudioHardware.h>
40 #elif defined(__linux__) || defined(__FreeBSD__)
41 #include <sys/soundcard.h>
50 #include <sys/socket.h>
51 #include <sys/ioctl.h>
52 #include <netinet/in.h>
53 #include <arpa/inet.h>
55 static char *config = "/etc/muted.conf";
57 static char host[256] = "";
58 static char user[256] = "";
59 static char pass[256] = "";
60 static int smoothfade = 0;
61 static int mutelevel = 20;
63 static int needfork = 1;
65 static int stepsize = 3;
67 static int mixchan = SOUND_MIXER_VOLUME;
72 struct subchannel *next;
75 static struct channel {
79 struct subchannel *subs;
82 static void add_channel(char *tech, char *location)
85 chan = malloc(sizeof(struct channel));
87 memset(chan, 0, sizeof(struct channel));
88 if (!(chan->tech = strdup(tech))) {
92 if (!(chan->location = strdup(location))) {
97 chan->next = channels;
103 static int load_config(void)
111 f = fopen(config, "r");
113 fprintf(stderr, "Unable to open config file '%s': %s\n", config, strerror(errno));
117 fgets(buf, sizeof(buf), f);
120 val = strchr(buf, '#');
121 if (val) *val = '\0';
122 while(strlen(buf) && (buf[strlen(buf) - 1] < 33))
123 buf[strlen(buf) - 1] = '\0';
135 while(*val && (*val < 33)) val++;
137 if (!strcasecmp(buf, "host")) {
138 if (val && strlen(val))
139 strncpy(host, val, sizeof(host) - 1);
141 fprintf(stderr, "host needs an argument (the host) at line %d\n", lineno);
142 } else if (!strcasecmp(buf, "user")) {
143 if (val && strlen(val))
144 strncpy(user, val, sizeof(user) - 1);
146 fprintf(stderr, "user needs an argument (the user) at line %d\n", lineno);
147 } else if (!strcasecmp(buf, "pass")) {
148 if (val && strlen(val))
149 strncpy(pass, val, sizeof(pass) - 1);
151 fprintf(stderr, "pass needs an argument (the password) at line %d\n", lineno);
152 } else if (!strcasecmp(buf, "smoothfade")) {
154 } else if (!strcasecmp(buf, "mutelevel")) {
155 if (val && (sscanf(val, "%d", &x) == 1) && (x > -1) && (x < 101)) {
158 fprintf(stderr, "mutelevel must be a number from 0 (most muted) to 100 (no mute) at line %d\n", lineno);
159 } else if (!strcasecmp(buf, "channel")) {
160 if (val && strlen(val)) {
161 val2 = strchr(val, '/');
165 add_channel(val, val2);
167 fprintf(stderr, "channel needs to be of the format Tech/Location at line %d\n", lineno);
169 fprintf(stderr, "channel needs an argument (the channel) at line %d\n", lineno);
171 fprintf(stderr, "ignoring unknown keyword '%s'\n", buf);
177 fprintf(stderr, "no 'host' specification in config file\n");
178 else if (!strlen(user))
179 fprintf(stderr, "no 'user' specification in config file\n");
181 fprintf(stderr, "no 'channel' specifications in config file\n");
191 static int open_mixer(void)
193 mixfd = open("/dev/mixer", O_RDWR);
195 fprintf(stderr, "Unable to open /dev/mixer: %s\n", strerror(errno));
200 #endif /* !__Darwin */
202 /*! Connect to the asterisk manager interface */
203 static int connect_asterisk(void)
209 struct sockaddr_in sin;
211 ports = strchr(host, ':');
215 if ((sscanf(ports, "%d", &port) != 1) || (port < 1) || (port > 65535)) {
216 fprintf(stderr, "'%s' is not a valid port number in the hostname\n", ports);
220 hp = gethostbyname(host);
222 fprintf(stderr, "Can't find host '%s'\n", host);
225 sock = socket(AF_INET, SOCK_STREAM, 0);
227 fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
230 sin.sin_family = AF_INET;
231 sin.sin_port = htons(port);
232 memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
233 if (connect(sock, (struct sockaddr *)&sin, sizeof(sin))) {
234 fprintf(stderr, "Failed to connect to '%s' port '%d': %s\n", host, port, strerror(errno));
238 astf = fdopen(sock, "r+");
240 fprintf(stderr, "fdopen failed: %s\n", strerror(errno));
247 static char *get_line(void)
249 static char buf[1024];
250 if (fgets(buf, sizeof(buf), astf)) {
251 while(strlen(buf) && (buf[strlen(buf) - 1] < 33))
252 buf[strlen(buf) - 1] = '\0';
258 /*! Login to the asterisk manager interface */
259 static int login_asterisk(void)
263 if (!(welcome = get_line())) {
264 fprintf(stderr, "disconnected (1)\n");
270 "Secret: %s\r\n\r\n", user, pass);
271 if (!(welcome = get_line())) {
272 fprintf(stderr, "disconnected (2)\n");
275 if (strcasecmp(welcome, "Response: Success")) {
276 fprintf(stderr, "login failed ('%s')\n", welcome);
279 /* Eat the rest of the event */
280 while((resp = get_line()) && strlen(resp));
282 fprintf(stderr, "disconnected (3)\n");
286 "Action: Status\r\n\r\n");
287 if (!(welcome = get_line())) {
288 fprintf(stderr, "disconnected (4)\n");
291 if (strcasecmp(welcome, "Response: Success")) {
292 fprintf(stderr, "status failed ('%s')\n", welcome);
295 /* Eat the rest of the event */
296 while((resp = get_line()) && strlen(resp));
298 fprintf(stderr, "disconnected (5)\n");
304 static struct channel *find_channel(char *channel)
308 struct channel *chan;
309 strncpy(tmp, channel, sizeof(tmp) - 1);
310 s = strchr(tmp, '/');
319 printf("Searching for '%s' tech, '%s' location\n", tmp, s);
322 if (!strcasecmp(chan->tech, tmp) && !strcasecmp(chan->location, s)) {
324 printf("Found '%s'/'%s'\n", chan->tech, chan->location);
335 static int getvol(void)
339 if (ioctl(mixfd, MIXER_READ(mixchan), &vol)) {
341 static float getvol(void)
343 float volumeL, volumeR, vol;
345 AudioDeviceID device;
349 size = sizeof(device);
350 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device);
351 size = sizeof(channels);
353 err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels);
356 err = AudioDeviceGetProperty(device, channels[0], false, kAudioDevicePropertyVolumeScalar, &size, &volumeL);
358 err = AudioDeviceGetProperty(device, channels[1], false, kAudioDevicePropertyVolumeScalar, &size, &volumeR);
360 vol = (volumeL < volumeR) ? volumeR : volumeL;
363 fprintf(stderr, "Unable to read mixer volume: %s\n", strerror(errno));
370 static int setvol(int vol)
372 static int setvol(float vol)
376 if (ioctl(mixfd, MIXER_WRITE(mixchan), &vol)) {
381 AudioDeviceID device;
385 size = sizeof(device);
386 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device);
387 size = sizeof(channels);
388 err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels);
391 err = AudioDeviceSetProperty(device, 0, channels[0], false, kAudioDevicePropertyVolumeScalar, size, &volumeL);
393 err = AudioDeviceSetProperty(device, 0, channels[1], false, kAudioDevicePropertyVolumeScalar, size, &volumeR);
397 fprintf(stderr, "Unable to write mixer volume: %s\n", strerror(errno));
405 static int oldvol = 0;
406 static int mutevol = 0;
408 static float oldvol = 0;
409 static float mutevol = 0;
413 static int mutedlevel(int orig, int mutelevel)
417 l = (float)(mutelevel) * (float)(l) / 100.0;
418 r = (float)(mutelevel) * (float)(r) / 100.0;
422 static float mutedlevel(float orig, float mutelevel)
425 master = mutelevel * master / 100.0;
431 static void mute(void)
452 for (x=start;x>=mutelevel;x-=stepsize) {
453 mutevol = mutedlevel(vol, x);
458 mutevol = mutedlevel(vol, mutelevel);
462 printf("Mute from '%f' to '%f'!\n", oldvol, mutevol);
464 printf("Mute from '%04x' to '%04x'!\n", oldvol, mutevol);
469 static void unmute(void)
483 printf("Unmute from '%f' (should be '%f') to '%f'!\n", vol, mutevol, oldvol);
485 if (vol == mutevol) {
487 printf("Unmute from '%04x' (should be '%04x') to '%04x'!\n", vol, mutevol, oldvol);
488 if ((int)vol == mutevol) {
498 for (x=start;x<100;x+=stepsize) {
499 mutevol = mutedlevel(oldvol, x);
506 printf("Whoops, it's already been changed!\n");
510 static void check_mute(void)
513 struct channel *chan;
522 if (offhook && !muted)
524 else if (!offhook && muted)
528 static void delete_sub(struct channel *chan, char *name)
530 struct subchannel *sub, *prev;
534 if (!strcasecmp(sub->name, name)) {
536 prev->next = sub->next;
538 chan->subs = sub->next;
548 static void append_sub(struct channel *chan, char *name)
550 struct subchannel *sub;
553 if (!strcasecmp(sub->name, name))
557 sub = malloc(sizeof(struct subchannel));
559 memset(sub, 0, sizeof(struct subchannel));
560 if (!(sub->name = strdup(name))) {
564 sub->next = chan->subs;
569 static void hangup_chan(char *channel)
571 struct channel *chan;
573 printf("Hangup '%s'\n", channel);
574 chan = find_channel(channel);
576 delete_sub(chan, channel);
580 static void offhook_chan(char *channel)
582 struct channel *chan;
584 printf("Offhook '%s'\n", channel);
585 chan = find_channel(channel);
587 append_sub(chan, channel);
591 static int wait_event(void)
595 char channel[120]="";
596 char oldname[120]="";
597 char newname[120]="";
601 fprintf(stderr, "disconnected (6)\n");
604 if (!strncasecmp(resp, "Event: ", strlen("Event: "))) {
605 strncpy(event, resp + strlen("Event: "), sizeof(event) - 1);
606 /* Consume the rest of the non-event */
607 while((resp = get_line()) && strlen(resp)) {
608 if (!strncasecmp(resp, "Channel: ", strlen("Channel: ")))
609 strncpy(channel, resp + strlen("Channel: "), sizeof(channel) - 1);
610 if (!strncasecmp(resp, "Newname: ", strlen("Newname: ")))
611 strncpy(newname, resp + strlen("Newname: "), sizeof(newname) - 1);
612 if (!strncasecmp(resp, "Oldname: ", strlen("Oldname: ")))
613 strncpy(oldname, resp + strlen("Oldname: "), sizeof(oldname) - 1);
615 if (strlen(channel)) {
616 if (!strcasecmp(event, "Hangup"))
617 hangup_chan(channel);
619 offhook_chan(channel);
621 if (strlen(newname) && strlen(oldname)) {
622 if (!strcasecmp(event, "Rename")) {
623 hangup_chan(oldname);
624 offhook_chan(newname);
628 /* Consume the rest of the non-event */
629 while((resp = get_line()) && strlen(resp));
632 fprintf(stderr, "disconnected (7)\n");
638 static void usage(void)
640 printf("Usage: muted [-f] [-d]\n"
641 " -f : Do not fork\n"
642 " -d : Debug (implies -f)\n");
645 int main(int argc, char *argv[])
648 while((x = getopt(argc, argv, "fhd")) > 0) {
670 if (connect_asterisk()) {
676 if (login_asterisk()) {
688 while(connect_asterisk()) {
691 if (login_asterisk()) {