Skip to content
Snippets Groups Projects
muted.c 14.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    /*
    
     * Asterisk -- An open source telephony toolkit.
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * Copyright (C) 1999 - 2005, Digium, Inc.
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
     * Mark Spencer <markster@digium.com>
    
     * Updated for Mac OSX CoreAudio 
    
     * by Josh Roberson <josh@asteriasgi.com>
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * See http://www.asterisk.org for more information about
     * the Asterisk project. Please do not directly contact
     * any of the maintainers of this project for assistance;
     * the project provides a web site, mailing lists and IRC
     * channels for your use.
     *
     * This program is free software, distributed under the terms of
     * the GNU General Public License Version 2. See the LICENSE file
     * at the top of the source tree.
     */
    
    /*
     * Mute Daemon
     *
     * Specially written for Malcolm Davenport, but I think I'll use it too
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
     */
    
    #ifndef __Darwin__
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <linux/soundcard.h>
    
    #else
    #include <CoreAudio/AudioHardware.h> 
    #endif
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <stdio.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <string.h>
    #include <netdb.h>
    #include <sys/socket.h>
    #include <sys/ioctl.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    static char *config = "/etc/muted.conf";
    
    
    static char host[256] = "";
    static char user[256] = "";
    static char pass[256] = "";
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int smoothfade = 0;
    static int mutelevel = 20;
    static int muted = 0;
    static int needfork = 1;
    static int debug = 0;
    static int stepsize = 3;
    
    #ifndef __Darwin__
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int mixchan = SOUND_MIXER_VOLUME;
    
    struct subchannel {
    	char *name;
    	struct subchannel *next;
    };
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static struct channel {
    	char *tech;
    	char *location;
    	struct channel *next;
    
    	struct subchannel *subs;
    
    Mark Spencer's avatar
    Mark Spencer committed
    } *channels;
    
    static void add_channel(char *tech, char *location)
    {
    	struct channel *chan;
    	chan = malloc(sizeof(struct channel));
    	if (chan) {
    		memset(chan, 0, sizeof(struct channel));
    		chan->tech = strdup(tech);
    		chan->location = strdup(location);
    		chan->next = channels;
    		channels = chan;
    	}
    	
    }
    
    static int load_config(void)
    {
    	FILE *f;
    	char buf[1024];
    	char *val;
    	char *val2;
    	int lineno=0;
    	int x;
    	f = fopen(config, "r");
    	if (!f) {
    		fprintf(stderr, "Unable to open config file '%s': %s\n", config, strerror(errno));
    		return -1;
    	}
    	while(!feof(f)) {
    		fgets(buf, sizeof(buf), f);
    		if (!feof(f)) {
    			lineno++;
    			val = strchr(buf, '#');
    			if (val) *val = '\0';
    			while(strlen(buf) && (buf[strlen(buf) - 1] < 33))
    				buf[strlen(buf) - 1] = '\0';
    			if (!strlen(buf))
    				continue;
    			val = buf;
    			while(*val) {
    				if (*val < 33)
    					break;
    				val++;
    			}
    			if (*val) {
    				*val = '\0';
    				val++;
    				while(*val && (*val < 33)) val++;
    			}
    			if (!strcasecmp(buf, "host")) {
    				if (val && strlen(val))
    
    					strncpy(host, val, sizeof(host) - 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				else
    					fprintf(stderr, "host needs an argument (the host) at line %d\n", lineno);
    			} else if (!strcasecmp(buf, "user")) {
    				if (val && strlen(val))
    
    					strncpy(user, val, sizeof(user) - 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				else
    					fprintf(stderr, "user needs an argument (the user) at line %d\n", lineno);
    			} else if (!strcasecmp(buf, "pass")) {
    				if (val && strlen(val))
    
    					strncpy(pass, val, sizeof(pass) - 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				else
    					fprintf(stderr, "pass needs an argument (the password) at line %d\n", lineno);
    			} else if (!strcasecmp(buf, "smoothfade")) {
    				smoothfade = 1;
    			} else if (!strcasecmp(buf, "mutelevel")) {
    				if (val && (sscanf(val, "%d", &x) == 1) && (x > -1) && (x < 101)) {
    					mutelevel = x;
    				} else 
    					fprintf(stderr, "mutelevel must be a number from 0 (most muted) to 100 (no mute) at line %d\n", lineno);
    			} else if (!strcasecmp(buf, "channel")) {
    				if (val && strlen(val)) {
    					val2 = strchr(val, '/');
    					if (val2) {
    						*val2 = '\0';
    						val2++;
    						add_channel(val, val2);
    					} else
    						fprintf(stderr, "channel needs to be of the format Tech/Location at line %d\n", lineno);
    				} else
    					fprintf(stderr, "channel needs an argument (the channel) at line %d\n", lineno);
    			} else {
    				fprintf(stderr, "ignoring unknown keyword '%s'\n", buf);
    			}
    		}
    	}
    	fclose(f);
    	if (!strlen(host))
    		fprintf(stderr, "no 'host' specification in config file\n");
    	else if (!strlen(user))
    		fprintf(stderr, "no 'user' specification in config file\n");
    	else if (!channels) 
    		fprintf(stderr, "no 'channel' specifications in config file\n");
    	else
    		return 0;
    	return -1;
    }
    
    static FILE *astf;
    
    #ifndef __Darwin__
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int mixfd;
    
    static int open_mixer(void)
    {
    	mixfd = open("/dev/mixer", O_RDWR);
    	if (mixfd < 0) {
    		fprintf(stderr, "Unable to open /dev/mixer: %s\n", strerror(errno));
    		return -1;
    	}
    	return 0;
    }
    
    #endif /* !__Darwin */
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static int connect_asterisk(void)
    {
    	int sock;
    	struct hostent *hp;
    	char *ports;
    	int port = 5038;
    	struct sockaddr_in sin;
    	ports = strchr(host, ':');
    	if (ports) {
    		*ports = '\0';
    		ports++;
    		if ((sscanf(ports, "%d", &port) != 1) || (port < 1) || (port > 65535)) {
    			fprintf(stderr, "'%s' is not a valid port number in the hostname\n", ports);
    			return -1;
    		}
    	}
    	hp = gethostbyname(host);
    	if (!hp) {
    		fprintf(stderr, "Can't find host '%s'\n", host);
    		return -1;
    	}
    	sock = socket(AF_INET, SOCK_STREAM, 0);
    	if (sock < 0) {
    		fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
    		return -1;
    	}
    	sin.sin_family = AF_INET;
    	sin.sin_port = htons(port);
    	memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
    	if (connect(sock, &sin, sizeof(sin))) {
    		fprintf(stderr, "Failed to connect to '%s' port '%d': %s\n", host, port, strerror(errno));
    		close(sock);
    		return -1;
    	}
    	astf = fdopen(sock, "r+");
    	if (!astf) {
    		fprintf(stderr, "fdopen failed: %s\n", strerror(errno));
    		close(sock);
    		return -1;
    	}
    	return 0;
    }
    
    static char *get_line(void)
    {
    	static char buf[1024];
    	if (fgets(buf, sizeof(buf), astf)) {
    		while(strlen(buf) && (buf[strlen(buf) - 1] < 33))
    			buf[strlen(buf) - 1] = '\0';
    		return buf;
    	} else
    		return NULL;
    }
    
    static int login_asterisk(void)
    {
    	char *welcome;
    	char *resp;
    	if (!(welcome = get_line())) {
    		fprintf(stderr, "disconnected (1)\n");
    		return -1;
    	}
    	fprintf(astf, 
    		"Action: Login\r\n"
    		"Username: %s\r\n"
    		"Secret: %s\r\n\r\n", user, pass);
    	if (!(welcome = get_line())) {
    		fprintf(stderr, "disconnected (2)\n");
    		return -1;
    	}
    	if (strcasecmp(welcome, "Response: Success")) {
    		fprintf(stderr, "login failed ('%s')\n", welcome);
    		return -1;
    	}
    	/* Eat the rest of the event */
    	while((resp = get_line()) && strlen(resp));
    	if (!resp) {
    		fprintf(stderr, "disconnected (3)\n");
    		return -1;
    	}
    	fprintf(astf, 
    		"Action: Status\r\n\r\n");
    	if (!(welcome = get_line())) {
    		fprintf(stderr, "disconnected (4)\n");
    		return -1;
    	}
    	if (strcasecmp(welcome, "Response: Success")) {
    		fprintf(stderr, "status failed ('%s')\n", welcome);
    		return -1;
    	}
    	/* Eat the rest of the event */
    	while((resp = get_line()) && strlen(resp));
    	if (!resp) {
    		fprintf(stderr, "disconnected (5)\n");
    		return -1;
    	}
    	return 0;
    }
    
    static struct channel *find_channel(char *channel)
    {
    	char tmp[256] = "";
    	char *s, *t;
    	struct channel *chan;
    
    	strncpy(tmp, channel, sizeof(tmp) - 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	s = strchr(tmp, '/');
    	if (s) {
    		*s = '\0';
    		s++;
    		t = strrchr(s, '-');
    		if (t) {
    			*t = '\0';
    		}
    		if (debug)
    			printf("Searching for '%s' tech, '%s' location\n", tmp, s);
    		chan = channels;
    		while(chan) {
    			if (!strcasecmp(chan->tech, tmp) && !strcasecmp(chan->location, s)) {
    				if (debug)
    					printf("Found '%s'/'%s'\n", chan->tech, chan->location);
    				break;
    			}
    			chan = chan->next;
    		}
    	} else
    		chan = NULL;
    	return chan;
    }
    
    
    #ifndef __Darwin__
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int getvol(void)
    {
    	int vol;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (ioctl(mixfd, MIXER_READ(mixchan), &vol)) {
    
    #else
    static float getvol(void)
    {
    	float volumeL, volumeR, vol;
    	OSStatus err;
    	AudioDeviceID device;
    	UInt32 size;
    	UInt32 channels[2];
    
    	size = sizeof(device);
    	err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device);
    	size = sizeof(channels);
    	if (!err) 
    		err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels);
    	size = sizeof(vol);
    	if (!err)
    		err = AudioDeviceGetProperty(device, channels[0], false, kAudioDevicePropertyVolumeScalar, &size, &volumeL);
    	if (!err)
    		err = AudioDeviceGetProperty(device, channels[1], false, kAudioDevicePropertyVolumeScalar, &size, &volumeR);
    	if (!err)
    		vol = (volumeL < volumeR) ? volumeR : volumeL;
    	else {
    #endif
    
    Mark Spencer's avatar
    Mark Spencer committed
    		fprintf(stderr, "Unable to read mixer volume: %s\n", strerror(errno));
    		return -1;
    	}
    	return vol;
    }
    
    
    #ifndef __Darwin__
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int setvol(int vol)
    
    #else
    static int setvol(float vol)
    #endif
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    #ifndef __Darwin__
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (ioctl(mixfd, MIXER_WRITE(mixchan), &vol)) {
    
    #else	
    	float volumeL = vol;
    	float volumeR = vol;
    	OSStatus err;
    	AudioDeviceID device;
    	UInt32 size;
    	UInt32 channels[2];
    
    	size = sizeof(device);
    	err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device);
    	size = sizeof(channels);
    	err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels);
    	size = sizeof(vol);
    	if (!err)
    		err = AudioDeviceSetProperty(device, 0, channels[0], false, kAudioDevicePropertyVolumeScalar, size, &volumeL);
    	if (!err)
    		err = AudioDeviceSetProperty(device, 0, channels[1], false, kAudioDevicePropertyVolumeScalar, size, &volumeR); 
    	if (err) {
    #endif
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    		fprintf(stderr, "Unable to write mixer volume: %s\n", strerror(errno));
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	return 0;
    }
    
    
    #ifndef __Darwin__
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int oldvol = 0;
    static int mutevol = 0;
    
    #else
    static float oldvol = 0;
    static float mutevol = 0;
    #endif
    
    Josh Roberson's avatar
    Josh Roberson committed
    #ifndef __Darwin__
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int mutedlevel(int orig, int mutelevel)
    {
    	int l = orig >> 8;
    	int r = orig & 0xff;
    	l = (float)(mutelevel) * (float)(l) / 100.0;
    	r = (float)(mutelevel) * (float)(r) / 100.0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return (l << 8) | r;
    
    Josh Roberson's avatar
    Josh Roberson committed
    #else
    static float mutedlevel(float orig, float mutelevel)
    {
    	float master = orig;
    	master = mutelevel * master / 100.0;
    	return master;
    #endif
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    static void mute(void)
    {
    
    #ifndef __Darwin__
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int vol;
    
    Josh Roberson's avatar
    Josh Roberson committed
    	int start;
    	int x;
    
    #else
    	float vol;
    
    Josh Roberson's avatar
    Josh Roberson committed
    	float start = 1.0;
    	float x;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	vol = getvol();
    	oldvol = vol;
    
    Josh Roberson's avatar
    Josh Roberson committed
    	if (smoothfade)
    #ifdef __Darwin__ 
    		start = mutelevel;
    #else
    
    Mark Spencer's avatar
    Mark Spencer committed
    		start = 100;
    	else
    		start = mutelevel;
    
    Josh Roberson's avatar
    Josh Roberson committed
    #endif
    
    Mark Spencer's avatar
    Mark Spencer committed
    	for (x=start;x>=mutelevel;x-=stepsize) {
    		mutevol = mutedlevel(vol, x);
    		setvol(mutevol);
    		/* Wait 0.01 sec */
    		usleep(10000);
    	}
    	mutevol = mutedlevel(vol, mutelevel);
    	setvol(mutevol);
    	if (debug)
    
    #ifdef __Darwin__
    		printf("Mute from '%f' to '%f'!\n", oldvol, mutevol);
    #else
    
    Mark Spencer's avatar
    Mark Spencer committed
    		printf("Mute from '%04x' to '%04x'!\n", oldvol, mutevol);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	muted = 1;
    }
    
    static void unmute(void)
    {
    
    #ifdef __Darwin__
    	float vol;
    
    Josh Roberson's avatar
    Josh Roberson committed
    	float start;
    	float x;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int vol;
    	int start;
    	int x;
    
    Josh Roberson's avatar
    Josh Roberson committed
    #endif
    
    Mark Spencer's avatar
    Mark Spencer committed
    	vol = getvol();
    	if (debug)
    
    #ifdef __Darwin__
    		printf("Unmute from '%f' (should be '%f') to '%f'!\n", vol, mutevol, oldvol);
    
    Josh Roberson's avatar
    Josh Roberson committed
    	mutevol = vol;
    	if (vol == mutevol) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		printf("Unmute from '%04x' (should be '%04x') to '%04x'!\n", vol, mutevol, oldvol);
    
    	if ((int)vol == mutevol) {
    
    Josh Roberson's avatar
    Josh Roberson committed
    #endif
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (smoothfade)
    			start = mutelevel;
    		else
    
    Josh Roberson's avatar
    Josh Roberson committed
    #ifdef __Darwin__
    			start = 1.0;
    #else
    
    Mark Spencer's avatar
    Mark Spencer committed
    			start = 100;
    
    Josh Roberson's avatar
    Josh Roberson committed
    #endif
    
    Mark Spencer's avatar
    Mark Spencer committed
    		for (x=start;x<100;x+=stepsize) {
    			mutevol = mutedlevel(oldvol, x);
    			setvol(mutevol);
    			/* Wait 0.01 sec */
    			usleep(10000);
    		}
    		setvol(oldvol);
    	} else
    		printf("Whoops, it's already been changed!\n");
    	muted = 0;
    }
    
    static void check_mute(void)
    {
    	int offhook = 0;
    	struct channel *chan;
    	chan = channels;
    	while(chan) {
    
    		if (chan->subs) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			offhook++;
    			break;
    		}
    		chan = chan->next;
    	}
    	if (offhook && !muted)
    		mute();
    	else if (!offhook && muted)
    		unmute();
    }
    
    
    static void delete_sub(struct channel *chan, char *name)
    {
    	struct subchannel *sub, *prev;
    	prev = NULL;
    	sub = chan->subs;
    	while(sub) {
    		if (!strcasecmp(sub->name, name)) {
    			if (prev)
    				prev->next = sub->next;
    			else
    				chan->subs = sub->next;
    			free(sub->name);
    			free(sub);
    			return;
    		}
    		prev = sub;
    		sub = sub->next;
    	}
    }
    
    static void append_sub(struct channel *chan, char *name)
    {
    	struct subchannel *sub;
    	sub = chan->subs;
    	while(sub) {
    		if (!strcasecmp(sub->name, name)) 
    			return;
    		sub = sub->next;
    	}
    	sub = malloc(sizeof(struct subchannel));
    	if (sub) {
    		memset(sub, 0, sizeof(struct subchannel));
    		sub->name = strdup(name);
    		sub->next = chan->subs;
    		chan->subs = sub;
    	}
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void hangup_chan(char *channel)
    {
    	struct channel *chan;
    
    	if (debug)
    		printf("Hangup '%s'\n", channel);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	chan = find_channel(channel);
    	if (chan)
    
    		delete_sub(chan, channel);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	check_mute();
    }
    
    static void offhook_chan(char *channel)
    {
    	struct channel *chan;
    
    	if (debug)
    		printf("Offhook '%s'\n", channel);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	chan = find_channel(channel);
    	if (chan)
    
    		append_sub(chan, channel);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	check_mute();
    }
    
    static int wait_event(void)
    {
    	char *resp;
    
    	char event[120]="";
    	char channel[120]="";
    	char oldname[120]="";
    	char newname[120]="";
    
    Mark Spencer's avatar
    Mark Spencer committed
    	resp = get_line();
    	if (!resp) {
    		fprintf(stderr, "disconnected (6)\n");
    		return -1;
    	}
    	if (!strncasecmp(resp, "Event: ", strlen("Event: "))) {
    
    		strncpy(event, resp + strlen("Event: "), sizeof(event) - 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Consume the rest of the non-event */
    		while((resp = get_line()) && strlen(resp)) {
    			if (!strncasecmp(resp, "Channel: ", strlen("Channel: ")))
    
    				strncpy(channel, resp + strlen("Channel: "), sizeof(channel) - 1);
    
    			if (!strncasecmp(resp, "Newname: ", strlen("Newname: ")))
    
    				strncpy(newname, resp + strlen("Newname: "), sizeof(newname) - 1);
    
    			if (!strncasecmp(resp, "Oldname: ", strlen("Oldname: ")))
    
    				strncpy(oldname, resp + strlen("Oldname: "), sizeof(oldname) - 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		if (strlen(channel)) {
    			if (!strcasecmp(event, "Hangup")) 
    				hangup_chan(channel);
    			else
    				offhook_chan(channel);
    		}
    
    		if (strlen(newname) && strlen(oldname)) {
    			if (!strcasecmp(event, "Rename")) {
    				hangup_chan(oldname);
    				offhook_chan(newname);
    			}
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    		/* Consume the rest of the non-event */
    		while((resp = get_line()) && strlen(resp));
    	}
    	if (!resp) {
    		fprintf(stderr, "disconnected (7)\n");
    		return -1;
    	}
    	return 0;
    }
    
    static void usage(void)
    {
    	printf("Usage: muted [-f] [-d]\n"
    	       "        -f : Do not fork\n"
    		   "        -d : Debug (implies -f)\n");
    }
    
    int main(int argc, char *argv[])
    {
    	int x;
    	while((x = getopt(argc, argv, "fhd")) > 0) {
    		switch(x) {
    		case 'd':
    			debug = 1;
    			needfork = 0;
    			break;
    		case 'f':
    			needfork = 0;
    			break;
    		case 'h':
    			/* Fall through */
    		default:
    			usage();
    			exit(1);
    		}
    	}
    	if (load_config())
    		exit(1);
    
    #ifndef __Darwin__
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (open_mixer())
    		exit(1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (connect_asterisk()) {
    
    #ifndef __Darwin__
    
    Mark Spencer's avatar
    Mark Spencer committed
    		close(mixfd);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		exit(1);
    	}
    	if (login_asterisk()) {
    
    #ifndef __Darwin__		
    
    Mark Spencer's avatar
    Mark Spencer committed
    		close(mixfd);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		fclose(astf);
    		exit(1);
    	}
    	if (needfork)
    		daemon(0,0);
    	for(;;) {
    
    		if (wait_event()) {
    			fclose(astf);
    			while(connect_asterisk()) {
    				sleep(5);
    			}
    			if (login_asterisk()) {
    				fclose(astf);
    				exit(1);
    			}
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	exit(0);
    }