4 * Specially written for Malcolm Davenport, but I think I'll use it too
6 * Copyright (C) 2004 - 2005, Digium Inc.
8 * Mark Spencer <markster@digium.com>
10 * Updated for Mac OSX CoreAudio
11 * by Josh Roberson <josh@asteraisgi.com>
13 * Distributed under the terms of the GNU General Public License version 2.0
18 #include <linux/soundcard.h>
20 #include <CoreAudio/AudioHardware.h>
29 #include <sys/socket.h>
30 #include <sys/ioctl.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
34 static char *config = "/etc/muted.conf";
36 static char host[256] = "";
37 static char user[256] = "";
38 static char pass[256] = "";
39 static int smoothfade = 0;
40 static int mutelevel = 20;
42 static int needfork = 1;
44 static int stepsize = 3;
46 static int mixchan = SOUND_MIXER_VOLUME;
51 struct subchannel *next;
54 static struct channel {
58 struct subchannel *subs;
61 static void add_channel(char *tech, char *location)
64 chan = malloc(sizeof(struct channel));
66 memset(chan, 0, sizeof(struct channel));
67 chan->tech = strdup(tech);
68 chan->location = strdup(location);
69 chan->next = channels;
75 static int load_config(void)
83 f = fopen(config, "r");
85 fprintf(stderr, "Unable to open config file '%s': %s\n", config, strerror(errno));
89 fgets(buf, sizeof(buf), f);
92 val = strchr(buf, '#');
94 while(strlen(buf) && (buf[strlen(buf) - 1] < 33))
95 buf[strlen(buf) - 1] = '\0';
107 while(*val && (*val < 33)) val++;
109 if (!strcasecmp(buf, "host")) {
110 if (val && strlen(val))
111 strncpy(host, val, sizeof(host) - 1);
113 fprintf(stderr, "host needs an argument (the host) at line %d\n", lineno);
114 } else if (!strcasecmp(buf, "user")) {
115 if (val && strlen(val))
116 strncpy(user, val, sizeof(user) - 1);
118 fprintf(stderr, "user needs an argument (the user) at line %d\n", lineno);
119 } else if (!strcasecmp(buf, "pass")) {
120 if (val && strlen(val))
121 strncpy(pass, val, sizeof(pass) - 1);
123 fprintf(stderr, "pass needs an argument (the password) at line %d\n", lineno);
124 } else if (!strcasecmp(buf, "smoothfade")) {
126 } else if (!strcasecmp(buf, "mutelevel")) {
127 if (val && (sscanf(val, "%d", &x) == 1) && (x > -1) && (x < 101)) {
130 fprintf(stderr, "mutelevel must be a number from 0 (most muted) to 100 (no mute) at line %d\n", lineno);
131 } else if (!strcasecmp(buf, "channel")) {
132 if (val && strlen(val)) {
133 val2 = strchr(val, '/');
137 add_channel(val, val2);
139 fprintf(stderr, "channel needs to be of the format Tech/Location at line %d\n", lineno);
141 fprintf(stderr, "channel needs an argument (the channel) at line %d\n", lineno);
143 fprintf(stderr, "ignoring unknown keyword '%s'\n", buf);
149 fprintf(stderr, "no 'host' specification in config file\n");
150 else if (!strlen(user))
151 fprintf(stderr, "no 'user' specification in config file\n");
153 fprintf(stderr, "no 'channel' specifications in config file\n");
163 static int open_mixer(void)
165 mixfd = open("/dev/mixer", O_RDWR);
167 fprintf(stderr, "Unable to open /dev/mixer: %s\n", strerror(errno));
172 #endif /* !__Darwin */
174 static int connect_asterisk(void)
180 struct sockaddr_in sin;
181 ports = strchr(host, ':');
185 if ((sscanf(ports, "%d", &port) != 1) || (port < 1) || (port > 65535)) {
186 fprintf(stderr, "'%s' is not a valid port number in the hostname\n", ports);
190 hp = gethostbyname(host);
192 fprintf(stderr, "Can't find host '%s'\n", host);
195 sock = socket(AF_INET, SOCK_STREAM, 0);
197 fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
200 sin.sin_family = AF_INET;
201 sin.sin_port = htons(port);
202 memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
203 if (connect(sock, &sin, sizeof(sin))) {
204 fprintf(stderr, "Failed to connect to '%s' port '%d': %s\n", host, port, strerror(errno));
208 astf = fdopen(sock, "r+");
210 fprintf(stderr, "fdopen failed: %s\n", strerror(errno));
217 static char *get_line(void)
219 static char buf[1024];
220 if (fgets(buf, sizeof(buf), astf)) {
221 while(strlen(buf) && (buf[strlen(buf) - 1] < 33))
222 buf[strlen(buf) - 1] = '\0';
228 static int login_asterisk(void)
232 if (!(welcome = get_line())) {
233 fprintf(stderr, "disconnected (1)\n");
239 "Secret: %s\r\n\r\n", user, pass);
240 if (!(welcome = get_line())) {
241 fprintf(stderr, "disconnected (2)\n");
244 if (strcasecmp(welcome, "Response: Success")) {
245 fprintf(stderr, "login failed ('%s')\n", welcome);
248 /* Eat the rest of the event */
249 while((resp = get_line()) && strlen(resp));
251 fprintf(stderr, "disconnected (3)\n");
255 "Action: Status\r\n\r\n");
256 if (!(welcome = get_line())) {
257 fprintf(stderr, "disconnected (4)\n");
260 if (strcasecmp(welcome, "Response: Success")) {
261 fprintf(stderr, "status failed ('%s')\n", welcome);
264 /* Eat the rest of the event */
265 while((resp = get_line()) && strlen(resp));
267 fprintf(stderr, "disconnected (5)\n");
273 static struct channel *find_channel(char *channel)
277 struct channel *chan;
278 strncpy(tmp, channel, sizeof(tmp) - 1);
279 s = strchr(tmp, '/');
288 printf("Searching for '%s' tech, '%s' location\n", tmp, s);
291 if (!strcasecmp(chan->tech, tmp) && !strcasecmp(chan->location, s)) {
293 printf("Found '%s'/'%s'\n", chan->tech, chan->location);
304 static int getvol(void)
308 if (ioctl(mixfd, MIXER_READ(mixchan), &vol)) {
310 static float getvol(void)
312 float volumeL, volumeR, vol;
314 AudioDeviceID device;
318 size = sizeof(device);
319 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device);
320 size = sizeof(channels);
322 err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels);
325 err = AudioDeviceGetProperty(device, channels[0], false, kAudioDevicePropertyVolumeScalar, &size, &volumeL);
327 err = AudioDeviceGetProperty(device, channels[1], false, kAudioDevicePropertyVolumeScalar, &size, &volumeR);
328 printf("volumeL = %f - volumeR = %f\n", volumeL, volumeR);
330 vol = (volumeL < volumeR) ? volumeR : volumeL;
333 fprintf(stderr, "Unable to read mixer volume: %s\n", strerror(errno));
340 static int setvol(int vol)
342 static int setvol(float vol)
346 if (ioctl(mixfd, MIXER_WRITE(mixchan), &vol)) {
351 AudioDeviceID device;
355 size = sizeof(device);
356 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device);
357 size = sizeof(channels);
358 err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels);
361 err = AudioDeviceSetProperty(device, 0, channels[0], false, kAudioDevicePropertyVolumeScalar, size, &volumeL);
363 err = AudioDeviceSetProperty(device, 0, channels[1], false, kAudioDevicePropertyVolumeScalar, size, &volumeR);
367 fprintf(stderr, "Unable to write mixer volume: %s\n", strerror(errno));
375 static int oldvol = 0;
376 static int mutevol = 0;
378 static float oldvol = 0;
379 static float mutevol = 0;
382 static int mutedlevel(int orig, int mutelevel)
386 l = (float)(mutelevel) * (float)(l) / 100.0;
387 r = (float)(mutelevel) * (float)(r) / 100.0;
391 static void mute(void)
406 for (x=start;x>=mutelevel;x-=stepsize) {
407 mutevol = mutedlevel(vol, x);
412 mutevol = mutedlevel(vol, mutelevel);
416 printf("Mute from '%f' to '%f'!\n", oldvol, mutevol);
418 printf("Mute from '%04x' to '%04x'!\n", oldvol, mutevol);
423 static void unmute(void)
435 printf("Unmute from '%f' (should be '%f') to '%f'!\n", vol, mutevol, oldvol);
437 printf("Unmute from '%04x' (should be '%04x') to '%04x'!\n", vol, mutevol, oldvol);
439 if ((int)vol == mutevol) {
444 for (x=start;x<100;x+=stepsize) {
445 mutevol = mutedlevel(oldvol, x);
452 printf("Whoops, it's already been changed!\n");
456 static void check_mute(void)
459 struct channel *chan;
468 if (offhook && !muted)
470 else if (!offhook && muted)
474 static void delete_sub(struct channel *chan, char *name)
476 struct subchannel *sub, *prev;
480 if (!strcasecmp(sub->name, name)) {
482 prev->next = sub->next;
484 chan->subs = sub->next;
494 static void append_sub(struct channel *chan, char *name)
496 struct subchannel *sub;
499 if (!strcasecmp(sub->name, name))
503 sub = malloc(sizeof(struct subchannel));
505 memset(sub, 0, sizeof(struct subchannel));
506 sub->name = strdup(name);
507 sub->next = chan->subs;
512 static void hangup_chan(char *channel)
514 struct channel *chan;
516 printf("Hangup '%s'\n", channel);
517 chan = find_channel(channel);
519 delete_sub(chan, channel);
523 static void offhook_chan(char *channel)
525 struct channel *chan;
527 printf("Offhook '%s'\n", channel);
528 chan = find_channel(channel);
530 append_sub(chan, channel);
534 static int wait_event(void)
538 char channel[120]="";
539 char oldname[120]="";
540 char newname[120]="";
543 fprintf(stderr, "disconnected (6)\n");
546 if (!strncasecmp(resp, "Event: ", strlen("Event: "))) {
547 strncpy(event, resp + strlen("Event: "), sizeof(event) - 1);
548 /* Consume the rest of the non-event */
549 while((resp = get_line()) && strlen(resp)) {
550 if (!strncasecmp(resp, "Channel: ", strlen("Channel: ")))
551 strncpy(channel, resp + strlen("Channel: "), sizeof(channel) - 1);
552 if (!strncasecmp(resp, "Newname: ", strlen("Newname: ")))
553 strncpy(newname, resp + strlen("Newname: "), sizeof(newname) - 1);
554 if (!strncasecmp(resp, "Oldname: ", strlen("Oldname: ")))
555 strncpy(oldname, resp + strlen("Oldname: "), sizeof(oldname) - 1);
557 if (strlen(channel)) {
558 if (!strcasecmp(event, "Hangup"))
559 hangup_chan(channel);
561 offhook_chan(channel);
563 if (strlen(newname) && strlen(oldname)) {
564 if (!strcasecmp(event, "Rename")) {
565 hangup_chan(oldname);
566 offhook_chan(newname);
570 /* Consume the rest of the non-event */
571 while((resp = get_line()) && strlen(resp));
574 fprintf(stderr, "disconnected (7)\n");
580 static void usage(void)
582 printf("Usage: muted [-f] [-d]\n"
583 " -f : Do not fork\n"
584 " -d : Debug (implies -f)\n");
587 int main(int argc, char *argv[])
590 while((x = getopt(argc, argv, "fhd")) > 0) {
612 if (connect_asterisk()) {
618 if (login_asterisk()) {
630 while(connect_asterisk()) {
633 if (login_asterisk()) {