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 <linux/soundcard.h>
41 #include <CoreAudio/AudioHardware.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 chan
->tech
= strdup(tech
);
89 chan
->location
= strdup(location
);
90 chan
->next
= channels
;
96 static int load_config(void)
104 f
= fopen(config
, "r");
106 fprintf(stderr
, "Unable to open config file '%s': %s\n", config
, strerror(errno
));
110 fgets(buf
, sizeof(buf
), f
);
113 val
= strchr(buf
, '#');
114 if (val
) *val
= '\0';
115 while(strlen(buf
) && (buf
[strlen(buf
) - 1] < 33))
116 buf
[strlen(buf
) - 1] = '\0';
128 while(*val
&& (*val
< 33)) val
++;
130 if (!strcasecmp(buf
, "host")) {
131 if (val
&& strlen(val
))
132 strncpy(host
, val
, sizeof(host
) - 1);
134 fprintf(stderr
, "host needs an argument (the host) at line %d\n", lineno
);
135 } else if (!strcasecmp(buf
, "user")) {
136 if (val
&& strlen(val
))
137 strncpy(user
, val
, sizeof(user
) - 1);
139 fprintf(stderr
, "user needs an argument (the user) at line %d\n", lineno
);
140 } else if (!strcasecmp(buf
, "pass")) {
141 if (val
&& strlen(val
))
142 strncpy(pass
, val
, sizeof(pass
) - 1);
144 fprintf(stderr
, "pass needs an argument (the password) at line %d\n", lineno
);
145 } else if (!strcasecmp(buf
, "smoothfade")) {
147 } else if (!strcasecmp(buf
, "mutelevel")) {
148 if (val
&& (sscanf(val
, "%d", &x
) == 1) && (x
> -1) && (x
< 101)) {
151 fprintf(stderr
, "mutelevel must be a number from 0 (most muted) to 100 (no mute) at line %d\n", lineno
);
152 } else if (!strcasecmp(buf
, "channel")) {
153 if (val
&& strlen(val
)) {
154 val2
= strchr(val
, '/');
158 add_channel(val
, val2
);
160 fprintf(stderr
, "channel needs to be of the format Tech/Location at line %d\n", lineno
);
162 fprintf(stderr
, "channel needs an argument (the channel) at line %d\n", lineno
);
164 fprintf(stderr
, "ignoring unknown keyword '%s'\n", buf
);
170 fprintf(stderr
, "no 'host' specification in config file\n");
171 else if (!strlen(user
))
172 fprintf(stderr
, "no 'user' specification in config file\n");
174 fprintf(stderr
, "no 'channel' specifications in config file\n");
184 static int open_mixer(void)
186 mixfd
= open("/dev/mixer", O_RDWR
);
188 fprintf(stderr
, "Unable to open /dev/mixer: %s\n", strerror(errno
));
193 #endif /* !__Darwin */
195 /*! Connect to the asterisk manager interface */
196 static int connect_asterisk(void)
202 struct sockaddr_in sin
;
204 ports
= strchr(host
, ':');
208 if ((sscanf(ports
, "%d", &port
) != 1) || (port
< 1) || (port
> 65535)) {
209 fprintf(stderr
, "'%s' is not a valid port number in the hostname\n", ports
);
213 hp
= gethostbyname(host
);
215 fprintf(stderr
, "Can't find host '%s'\n", host
);
218 sock
= socket(AF_INET
, SOCK_STREAM
, 0);
220 fprintf(stderr
, "Failed to create socket: %s\n", strerror(errno
));
223 sin
.sin_family
= AF_INET
;
224 sin
.sin_port
= htons(port
);
225 memcpy(&sin
.sin_addr
, hp
->h_addr
, sizeof(sin
.sin_addr
));
226 if (connect(sock
, (struct sockaddr
*)&sin
, sizeof(sin
))) {
227 fprintf(stderr
, "Failed to connect to '%s' port '%d': %s\n", host
, port
, strerror(errno
));
231 astf
= fdopen(sock
, "r+");
233 fprintf(stderr
, "fdopen failed: %s\n", strerror(errno
));
240 static char *get_line(void)
242 static char buf
[1024];
243 if (fgets(buf
, sizeof(buf
), astf
)) {
244 while(strlen(buf
) && (buf
[strlen(buf
) - 1] < 33))
245 buf
[strlen(buf
) - 1] = '\0';
251 /*! Login to the asterisk manager interface */
252 static int login_asterisk(void)
256 if (!(welcome
= get_line())) {
257 fprintf(stderr
, "disconnected (1)\n");
263 "Secret: %s\r\n\r\n", user
, pass
);
264 if (!(welcome
= get_line())) {
265 fprintf(stderr
, "disconnected (2)\n");
268 if (strcasecmp(welcome
, "Response: Success")) {
269 fprintf(stderr
, "login failed ('%s')\n", welcome
);
272 /* Eat the rest of the event */
273 while((resp
= get_line()) && strlen(resp
));
275 fprintf(stderr
, "disconnected (3)\n");
279 "Action: Status\r\n\r\n");
280 if (!(welcome
= get_line())) {
281 fprintf(stderr
, "disconnected (4)\n");
284 if (strcasecmp(welcome
, "Response: Success")) {
285 fprintf(stderr
, "status failed ('%s')\n", welcome
);
288 /* Eat the rest of the event */
289 while((resp
= get_line()) && strlen(resp
));
291 fprintf(stderr
, "disconnected (5)\n");
297 static struct channel
*find_channel(char *channel
)
301 struct channel
*chan
;
302 strncpy(tmp
, channel
, sizeof(tmp
) - 1);
303 s
= strchr(tmp
, '/');
312 printf("Searching for '%s' tech, '%s' location\n", tmp
, s
);
315 if (!strcasecmp(chan
->tech
, tmp
) && !strcasecmp(chan
->location
, s
)) {
317 printf("Found '%s'/'%s'\n", chan
->tech
, chan
->location
);
328 static int getvol(void)
332 if (ioctl(mixfd
, MIXER_READ(mixchan
), &vol
)) {
334 static float getvol(void)
336 float volumeL
, volumeR
, vol
;
338 AudioDeviceID device
;
342 size
= sizeof(device
);
343 err
= AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice
, &size
, &device
);
344 size
= sizeof(channels
);
346 err
= AudioDeviceGetProperty(device
, 0, false, kAudioDevicePropertyPreferredChannelsForStereo
, &size
, &channels
);
349 err
= AudioDeviceGetProperty(device
, channels
[0], false, kAudioDevicePropertyVolumeScalar
, &size
, &volumeL
);
351 err
= AudioDeviceGetProperty(device
, channels
[1], false, kAudioDevicePropertyVolumeScalar
, &size
, &volumeR
);
353 vol
= (volumeL
< volumeR
) ? volumeR
: volumeL
;
356 fprintf(stderr
, "Unable to read mixer volume: %s\n", strerror(errno
));
363 static int setvol(int vol
)
365 static int setvol(float vol
)
369 if (ioctl(mixfd
, MIXER_WRITE(mixchan
), &vol
)) {
374 AudioDeviceID device
;
378 size
= sizeof(device
);
379 err
= AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice
, &size
, &device
);
380 size
= sizeof(channels
);
381 err
= AudioDeviceGetProperty(device
, 0, false, kAudioDevicePropertyPreferredChannelsForStereo
, &size
, &channels
);
384 err
= AudioDeviceSetProperty(device
, 0, channels
[0], false, kAudioDevicePropertyVolumeScalar
, size
, &volumeL
);
386 err
= AudioDeviceSetProperty(device
, 0, channels
[1], false, kAudioDevicePropertyVolumeScalar
, size
, &volumeR
);
390 fprintf(stderr
, "Unable to write mixer volume: %s\n", strerror(errno
));
398 static int oldvol
= 0;
399 static int mutevol
= 0;
401 static float oldvol
= 0;
402 static float mutevol
= 0;
406 static int mutedlevel(int orig
, int mutelevel
)
410 l
= (float)(mutelevel
) * (float)(l
) / 100.0;
411 r
= (float)(mutelevel
) * (float)(r
) / 100.0;
415 static float mutedlevel(float orig
, float mutelevel
)
418 master
= mutelevel
* master
/ 100.0;
424 static void mute(void)
445 for (x
=start
;x
>=mutelevel
;x
-=stepsize
) {
446 mutevol
= mutedlevel(vol
, x
);
451 mutevol
= mutedlevel(vol
, mutelevel
);
455 printf("Mute from '%f' to '%f'!\n", oldvol
, mutevol
);
457 printf("Mute from '%04x' to '%04x'!\n", oldvol
, mutevol
);
462 static void unmute(void)
476 printf("Unmute from '%f' (should be '%f') to '%f'!\n", vol
, mutevol
, oldvol
);
478 if (vol
== mutevol
) {
480 printf("Unmute from '%04x' (should be '%04x') to '%04x'!\n", vol
, mutevol
, oldvol
);
481 if ((int)vol
== mutevol
) {
491 for (x
=start
;x
<100;x
+=stepsize
) {
492 mutevol
= mutedlevel(oldvol
, x
);
499 printf("Whoops, it's already been changed!\n");
503 static void check_mute(void)
506 struct channel
*chan
;
515 if (offhook
&& !muted
)
517 else if (!offhook
&& muted
)
521 static void delete_sub(struct channel
*chan
, char *name
)
523 struct subchannel
*sub
, *prev
;
527 if (!strcasecmp(sub
->name
, name
)) {
529 prev
->next
= sub
->next
;
531 chan
->subs
= sub
->next
;
541 static void append_sub(struct channel
*chan
, char *name
)
543 struct subchannel
*sub
;
546 if (!strcasecmp(sub
->name
, name
))
550 sub
= malloc(sizeof(struct subchannel
));
552 memset(sub
, 0, sizeof(struct subchannel
));
553 sub
->name
= strdup(name
);
554 sub
->next
= chan
->subs
;
559 static void hangup_chan(char *channel
)
561 struct channel
*chan
;
563 printf("Hangup '%s'\n", channel
);
564 chan
= find_channel(channel
);
566 delete_sub(chan
, channel
);
570 static void offhook_chan(char *channel
)
572 struct channel
*chan
;
574 printf("Offhook '%s'\n", channel
);
575 chan
= find_channel(channel
);
577 append_sub(chan
, channel
);
581 static int wait_event(void)
585 char channel
[120]="";
586 char oldname
[120]="";
587 char newname
[120]="";
591 fprintf(stderr
, "disconnected (6)\n");
594 if (!strncasecmp(resp
, "Event: ", strlen("Event: "))) {
595 strncpy(event
, resp
+ strlen("Event: "), sizeof(event
) - 1);
596 /* Consume the rest of the non-event */
597 while((resp
= get_line()) && strlen(resp
)) {
598 if (!strncasecmp(resp
, "Channel: ", strlen("Channel: ")))
599 strncpy(channel
, resp
+ strlen("Channel: "), sizeof(channel
) - 1);
600 if (!strncasecmp(resp
, "Newname: ", strlen("Newname: ")))
601 strncpy(newname
, resp
+ strlen("Newname: "), sizeof(newname
) - 1);
602 if (!strncasecmp(resp
, "Oldname: ", strlen("Oldname: ")))
603 strncpy(oldname
, resp
+ strlen("Oldname: "), sizeof(oldname
) - 1);
605 if (strlen(channel
)) {
606 if (!strcasecmp(event
, "Hangup"))
607 hangup_chan(channel
);
609 offhook_chan(channel
);
611 if (strlen(newname
) && strlen(oldname
)) {
612 if (!strcasecmp(event
, "Rename")) {
613 hangup_chan(oldname
);
614 offhook_chan(newname
);
618 /* Consume the rest of the non-event */
619 while((resp
= get_line()) && strlen(resp
));
622 fprintf(stderr
, "disconnected (7)\n");
628 static void usage(void)
630 printf("Usage: muted [-f] [-d]\n"
631 " -f : Do not fork\n"
632 " -d : Debug (implies -f)\n");
635 int main(int argc
, char *argv
[])
638 while((x
= getopt(argc
, argv
, "fhd")) > 0) {
660 if (connect_asterisk()) {
666 if (login_asterisk()) {
678 while(connect_asterisk()) {
681 if (login_asterisk()) {