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/asterisk/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()) {
684 #ifndef HAVE_SBIN_LAUNCHD
687 fprintf(stderr
, "Mac OS X detected. Use 'launchd -d muted -f' to launch.\n");
694 while(connect_asterisk()) {
697 if (login_asterisk()) {