Sat Sep 16 05:47:46 2006

Asterisk developer's documentation


muted.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * Updated for Mac OSX CoreAudio 
00009  * by Josh Roberson <josh@asteriasgi.com>
00010  *
00011  * See http://www.asterisk.org for more information about
00012  * the Asterisk project. Please do not directly contact
00013  * any of the maintainers of this project for assistance;
00014  * the project provides a web site, mailing lists and IRC
00015  * channels for your use.
00016  *
00017  * This program is free software, distributed under the terms of
00018  * the GNU General Public License Version 2. See the LICENSE file
00019  * at the top of the source tree.
00020  */
00021 
00022 /*! \file
00023  * \brief Mute Daemon
00024  *
00025  * \note Specially written for Malcolm Davenport, but I think I'll use it too
00026  * Connects to the Asterisk Manager Interface, AMI, and listens for events
00027  * on certain devices. If a phone call is connected to one of the devices (phones)
00028  * the local sound is muted to a lower volume during the call.
00029  *
00030  */
00031 
00032 #ifndef __Darwin__
00033 #include <linux/soundcard.h>
00034 #else
00035 #include <CoreAudio/AudioHardware.h> 
00036 #endif
00037 #include <stdio.h>
00038 #include <errno.h>
00039 #include <stdlib.h>
00040 #include <unistd.h>
00041 #include <fcntl.h>
00042 #include <string.h>
00043 #include <netdb.h>
00044 #include <sys/socket.h>
00045 #include <sys/ioctl.h>
00046 #include <netinet/in.h>
00047 #include <arpa/inet.h>
00048 
00049 static char *config = "/etc/muted.conf";
00050 
00051 static char host[256] = "";
00052 static char user[256] = "";
00053 static char pass[256] = "";
00054 static int smoothfade = 0;
00055 static int mutelevel = 20;
00056 static int muted = 0;
00057 static int needfork = 1;
00058 static int debug = 0;
00059 static int stepsize = 3;
00060 #ifndef __Darwin__
00061 static int mixchan = SOUND_MIXER_VOLUME;
00062 #endif
00063 
00064 struct subchannel {
00065    char *name;
00066    struct subchannel *next;
00067 };
00068 
00069 static struct channel {
00070    char *tech;
00071    char *location;
00072    struct channel *next;
00073    struct subchannel *subs;
00074 } *channels;
00075 
00076 static void add_channel(char *tech, char *location)
00077 {
00078    struct channel *chan;
00079    chan = malloc(sizeof(struct channel));
00080    if (chan) {
00081       memset(chan, 0, sizeof(struct channel));
00082       chan->tech = strdup(tech);
00083       chan->location = strdup(location);
00084       chan->next = channels;
00085       channels = chan;
00086    }
00087    
00088 }
00089 
00090 static int load_config(void)
00091 {
00092    FILE *f;
00093    char buf[1024];
00094    char *val;
00095    char *val2;
00096    int lineno=0;
00097    int x;
00098    f = fopen(config, "r");
00099    if (!f) {
00100       fprintf(stderr, "Unable to open config file '%s': %s\n", config, strerror(errno));
00101       return -1;
00102    }
00103    while(!feof(f)) {
00104       fgets(buf, sizeof(buf), f);
00105       if (!feof(f)) {
00106          lineno++;
00107          val = strchr(buf, '#');
00108          if (val) *val = '\0';
00109          while(strlen(buf) && (buf[strlen(buf) - 1] < 33))
00110             buf[strlen(buf) - 1] = '\0';
00111          if (!strlen(buf))
00112             continue;
00113          val = buf;
00114          while(*val) {
00115             if (*val < 33)
00116                break;
00117             val++;
00118          }
00119          if (*val) {
00120             *val = '\0';
00121             val++;
00122             while(*val && (*val < 33)) val++;
00123          }
00124          if (!strcasecmp(buf, "host")) {
00125             if (val && strlen(val))
00126                strncpy(host, val, sizeof(host) - 1);
00127             else
00128                fprintf(stderr, "host needs an argument (the host) at line %d\n", lineno);
00129          } else if (!strcasecmp(buf, "user")) {
00130             if (val && strlen(val))
00131                strncpy(user, val, sizeof(user) - 1);
00132             else
00133                fprintf(stderr, "user needs an argument (the user) at line %d\n", lineno);
00134          } else if (!strcasecmp(buf, "pass")) {
00135             if (val && strlen(val))
00136                strncpy(pass, val, sizeof(pass) - 1);
00137             else
00138                fprintf(stderr, "pass needs an argument (the password) at line %d\n", lineno);
00139          } else if (!strcasecmp(buf, "smoothfade")) {
00140             smoothfade = 1;
00141          } else if (!strcasecmp(buf, "mutelevel")) {
00142             if (val && (sscanf(val, "%d", &x) == 1) && (x > -1) && (x < 101)) {
00143                mutelevel = x;
00144             } else 
00145                fprintf(stderr, "mutelevel must be a number from 0 (most muted) to 100 (no mute) at line %d\n", lineno);
00146          } else if (!strcasecmp(buf, "channel")) {
00147             if (val && strlen(val)) {
00148                val2 = strchr(val, '/');
00149                if (val2) {
00150                   *val2 = '\0';
00151                   val2++;
00152                   add_channel(val, val2);
00153                } else
00154                   fprintf(stderr, "channel needs to be of the format Tech/Location at line %d\n", lineno);
00155             } else
00156                fprintf(stderr, "channel needs an argument (the channel) at line %d\n", lineno);
00157          } else {
00158             fprintf(stderr, "ignoring unknown keyword '%s'\n", buf);
00159          }
00160       }
00161    }
00162    fclose(f);
00163    if (!strlen(host))
00164       fprintf(stderr, "no 'host' specification in config file\n");
00165    else if (!strlen(user))
00166       fprintf(stderr, "no 'user' specification in config file\n");
00167    else if (!channels) 
00168       fprintf(stderr, "no 'channel' specifications in config file\n");
00169    else
00170       return 0;
00171    return -1;
00172 }
00173 
00174 static FILE *astf;
00175 #ifndef __Darwin__
00176 static int mixfd;
00177 
00178 static int open_mixer(void)
00179 {
00180    mixfd = open("/dev/mixer", O_RDWR);
00181    if (mixfd < 0) {
00182       fprintf(stderr, "Unable to open /dev/mixer: %s\n", strerror(errno));
00183       return -1;
00184    }
00185    return 0;
00186 }
00187 #endif /* !__Darwin */
00188 
00189 /*! Connect to the asterisk manager interface */
00190 static int connect_asterisk(void)
00191 {
00192    int sock;
00193    struct hostent *hp;
00194    char *ports;
00195    int port = 5038;
00196    struct sockaddr_in sin;
00197 
00198    ports = strchr(host, ':');
00199    if (ports) {
00200       *ports = '\0';
00201       ports++;
00202       if ((sscanf(ports, "%d", &port) != 1) || (port < 1) || (port > 65535)) {
00203          fprintf(stderr, "'%s' is not a valid port number in the hostname\n", ports);
00204          return -1;
00205       }
00206    }
00207    hp = gethostbyname(host);
00208    if (!hp) {
00209       fprintf(stderr, "Can't find host '%s'\n", host);
00210       return -1;
00211    }
00212    sock = socket(AF_INET, SOCK_STREAM, 0);
00213    if (sock < 0) {
00214       fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
00215       return -1;
00216    }
00217    sin.sin_family = AF_INET;
00218    sin.sin_port = htons(port);
00219    memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
00220    if (connect(sock, &sin, sizeof(sin))) {
00221       fprintf(stderr, "Failed to connect to '%s' port '%d': %s\n", host, port, strerror(errno));
00222       close(sock);
00223       return -1;
00224    }
00225    astf = fdopen(sock, "r+");
00226    if (!astf) {
00227       fprintf(stderr, "fdopen failed: %s\n", strerror(errno));
00228       close(sock);
00229       return -1;
00230    }
00231    return 0;
00232 }
00233 
00234 static char *get_line(void)
00235 {
00236    static char buf[1024];
00237    if (fgets(buf, sizeof(buf), astf)) {
00238       while(strlen(buf) && (buf[strlen(buf) - 1] < 33))
00239          buf[strlen(buf) - 1] = '\0';
00240       return buf;
00241    } else
00242       return NULL;
00243 }
00244 
00245 /*! Login to the asterisk manager interface */
00246 static int login_asterisk(void)
00247 {
00248    char *welcome;
00249    char *resp;
00250    if (!(welcome = get_line())) {
00251       fprintf(stderr, "disconnected (1)\n");
00252       return -1;
00253    }
00254    fprintf(astf, 
00255       "Action: Login\r\n"
00256       "Username: %s\r\n"
00257       "Secret: %s\r\n\r\n", user, pass);
00258    if (!(welcome = get_line())) {
00259       fprintf(stderr, "disconnected (2)\n");
00260       return -1;
00261    }
00262    if (strcasecmp(welcome, "Response: Success")) {
00263       fprintf(stderr, "login failed ('%s')\n", welcome);
00264       return -1;
00265    }
00266    /* Eat the rest of the event */
00267    while((resp = get_line()) && strlen(resp));
00268    if (!resp) {
00269       fprintf(stderr, "disconnected (3)\n");
00270       return -1;
00271    }
00272    fprintf(astf, 
00273       "Action: Status\r\n\r\n");
00274    if (!(welcome = get_line())) {
00275       fprintf(stderr, "disconnected (4)\n");
00276       return -1;
00277    }
00278    if (strcasecmp(welcome, "Response: Success")) {
00279       fprintf(stderr, "status failed ('%s')\n", welcome);
00280       return -1;
00281    }
00282    /* Eat the rest of the event */
00283    while((resp = get_line()) && strlen(resp));
00284    if (!resp) {
00285       fprintf(stderr, "disconnected (5)\n");
00286       return -1;
00287    }
00288    return 0;
00289 }
00290 
00291 static struct channel *find_channel(char *channel)
00292 {
00293    char tmp[256] = "";
00294    char *s, *t;
00295    struct channel *chan;
00296    strncpy(tmp, channel, sizeof(tmp) - 1);
00297    s = strchr(tmp, '/');
00298    if (s) {
00299       *s = '\0';
00300       s++;
00301       t = strrchr(s, '-');
00302       if (t) {
00303          *t = '\0';
00304       }
00305       if (debug)
00306          printf("Searching for '%s' tech, '%s' location\n", tmp, s);
00307       chan = channels;
00308       while(chan) {
00309          if (!strcasecmp(chan->tech, tmp) && !strcasecmp(chan->location, s)) {
00310             if (debug)
00311                printf("Found '%s'/'%s'\n", chan->tech, chan->location);
00312             break;
00313          }
00314          chan = chan->next;
00315       }
00316    } else
00317       chan = NULL;
00318    return chan;
00319 }
00320 
00321 #ifndef __Darwin__
00322 static int getvol(void)
00323 {
00324    int vol;
00325 
00326    if (ioctl(mixfd, MIXER_READ(mixchan), &vol)) {
00327 #else
00328 static float getvol(void)
00329 {
00330    float volumeL, volumeR, vol;
00331    OSStatus err;
00332    AudioDeviceID device;
00333    UInt32 size;
00334    UInt32 channels[2];
00335 
00336    size = sizeof(device);
00337    err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device);
00338    size = sizeof(channels);
00339    if (!err) 
00340       err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels);
00341    size = sizeof(vol);
00342    if (!err)
00343       err = AudioDeviceGetProperty(device, channels[0], false, kAudioDevicePropertyVolumeScalar, &size, &volumeL);
00344    if (!err)
00345       err = AudioDeviceGetProperty(device, channels[1], false, kAudioDevicePropertyVolumeScalar, &size, &volumeR);
00346    if (!err)
00347       vol = (volumeL < volumeR) ? volumeR : volumeL;
00348    else {
00349 #endif
00350       fprintf(stderr, "Unable to read mixer volume: %s\n", strerror(errno));
00351       return -1;
00352    }
00353    return vol;
00354 }
00355 
00356 #ifndef __Darwin__
00357 static int setvol(int vol)
00358 #else
00359 static int setvol(float vol)
00360 #endif
00361 {
00362 #ifndef __Darwin__
00363    if (ioctl(mixfd, MIXER_WRITE(mixchan), &vol)) {
00364 #else 
00365    float volumeL = vol;
00366    float volumeR = vol;
00367    OSStatus err;
00368    AudioDeviceID device;
00369    UInt32 size;
00370    UInt32 channels[2];
00371 
00372    size = sizeof(device);
00373    err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device);
00374    size = sizeof(channels);
00375    err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels);
00376    size = sizeof(vol);
00377    if (!err)
00378       err = AudioDeviceSetProperty(device, 0, channels[0], false, kAudioDevicePropertyVolumeScalar, size, &volumeL);
00379    if (!err)
00380       err = AudioDeviceSetProperty(device, 0, channels[1], false, kAudioDevicePropertyVolumeScalar, size, &volumeR); 
00381    if (err) {
00382 #endif
00383 
00384       fprintf(stderr, "Unable to write mixer volume: %s\n", strerror(errno));
00385       return -1;
00386 
00387    }
00388    return 0;
00389 }
00390 
00391 #ifndef __Darwin__
00392 static int oldvol = 0;
00393 static int mutevol = 0;
00394 #else
00395 static float oldvol = 0;
00396 static float mutevol = 0;
00397 #endif
00398 
00399 #ifndef __Darwin__
00400 static int mutedlevel(int orig, int mutelevel)
00401 {
00402    int l = orig >> 8;
00403    int r = orig & 0xff;
00404    l = (float)(mutelevel) * (float)(l) / 100.0;
00405    r = (float)(mutelevel) * (float)(r) / 100.0;
00406 
00407    return (l << 8) | r;
00408 #else
00409 static float mutedlevel(float orig, float mutelevel)
00410 {
00411    float master = orig;
00412    master = mutelevel * master / 100.0;
00413    return master;
00414 #endif
00415    
00416 }
00417 
00418 static void mute(void)
00419 {
00420 #ifndef __Darwin__
00421    int vol;
00422    int start;
00423    int x;
00424 #else
00425    float vol;
00426    float start = 1.0;
00427    float x;
00428 #endif
00429    vol = getvol();
00430    oldvol = vol;
00431    if (smoothfade)
00432 #ifdef __Darwin__ 
00433       start = mutelevel;
00434 #else
00435       start = 100;
00436    else
00437       start = mutelevel;
00438 #endif
00439    for (x=start;x>=mutelevel;x-=stepsize) {
00440       mutevol = mutedlevel(vol, x);
00441       setvol(mutevol);
00442       /* Wait 0.01 sec */
00443       usleep(10000);
00444    }
00445    mutevol = mutedlevel(vol, mutelevel);
00446    setvol(mutevol);
00447    if (debug)
00448 #ifdef __Darwin__
00449       printf("Mute from '%f' to '%f'!\n", oldvol, mutevol);
00450 #else
00451       printf("Mute from '%04x' to '%04x'!\n", oldvol, mutevol);
00452 #endif
00453    muted = 1;
00454 }
00455 
00456 static void unmute(void)
00457 {
00458 #ifdef __Darwin__
00459    float vol;
00460    float start;
00461    float x;
00462 #else
00463    int vol;
00464    int start;
00465    int x;
00466 #endif
00467    vol = getvol();
00468    if (debug)
00469 #ifdef __Darwin__
00470       printf("Unmute from '%f' (should be '%f') to '%f'!\n", vol, mutevol, oldvol);
00471    mutevol = vol;
00472    if (vol == mutevol) {
00473 #else
00474       printf("Unmute from '%04x' (should be '%04x') to '%04x'!\n", vol, mutevol, oldvol);
00475    if ((int)vol == mutevol) {
00476 #endif
00477       if (smoothfade)
00478          start = mutelevel;
00479       else
00480 #ifdef __Darwin__
00481          start = 1.0;
00482 #else
00483          start = 100;
00484 #endif
00485       for (x=start;x<100;x+=stepsize) {
00486          mutevol = mutedlevel(oldvol, x);
00487          setvol(mutevol);
00488          /* Wait 0.01 sec */
00489          usleep(10000);
00490       }
00491       setvol(oldvol);
00492    } else
00493       printf("Whoops, it's already been changed!\n");
00494    muted = 0;
00495 }
00496 
00497 static void check_mute(void)
00498 {
00499    int offhook = 0;
00500    struct channel *chan;
00501    chan = channels;
00502    while(chan) {
00503       if (chan->subs) {
00504          offhook++;
00505          break;
00506       }
00507       chan = chan->next;
00508    }
00509    if (offhook && !muted)
00510       mute();
00511    else if (!offhook && muted)
00512       unmute();
00513 }
00514 
00515 static void delete_sub(struct channel *chan, char *name)
00516 {
00517    struct subchannel *sub, *prev;
00518    prev = NULL;
00519    sub = chan->subs;
00520    while(sub) {
00521       if (!strcasecmp(sub->name, name)) {
00522          if (prev)
00523             prev->next = sub->next;
00524          else
00525             chan->subs = sub->next;
00526          free(sub->name);
00527          free(sub);
00528          return;
00529       }
00530       prev = sub;
00531       sub = sub->next;
00532    }
00533 }
00534 
00535 static void append_sub(struct channel *chan, char *name)
00536 {
00537    struct subchannel *sub;
00538    sub = chan->subs;
00539    while(sub) {
00540       if (!strcasecmp(sub->name, name)) 
00541          return;
00542       sub = sub->next;
00543    }
00544    sub = malloc(sizeof(struct subchannel));
00545    if (sub) {
00546       memset(sub, 0, sizeof(struct subchannel));
00547       sub->name = strdup(name);
00548       sub->next = chan->subs;
00549       chan->subs = sub;
00550    }
00551 }
00552 
00553 static void hangup_chan(char *channel)
00554 {
00555    struct channel *chan;
00556    if (debug)
00557       printf("Hangup '%s'\n", channel);
00558    chan = find_channel(channel);
00559    if (chan)
00560       delete_sub(chan, channel);
00561    check_mute();
00562 }
00563 
00564 static void offhook_chan(char *channel)
00565 {
00566    struct channel *chan;
00567    if (debug)
00568       printf("Offhook '%s'\n", channel);
00569    chan = find_channel(channel);
00570    if (chan)
00571       append_sub(chan, channel);
00572    check_mute();
00573 }
00574 
00575 static int wait_event(void)
00576 {
00577    char *resp;
00578    char event[120]="";
00579    char channel[120]="";
00580    char oldname[120]="";
00581    char newname[120]="";
00582 
00583    resp = get_line();
00584    if (!resp) {
00585       fprintf(stderr, "disconnected (6)\n");
00586       return -1;
00587    }
00588    if (!strncasecmp(resp, "Event: ", strlen("Event: "))) {
00589       strncpy(event, resp + strlen("Event: "), sizeof(event) - 1);
00590       /* Consume the rest of the non-event */
00591       while((resp = get_line()) && strlen(resp)) {
00592          if (!strncasecmp(resp, "Channel: ", strlen("Channel: ")))
00593             strncpy(channel, resp + strlen("Channel: "), sizeof(channel) - 1);
00594          if (!strncasecmp(resp, "Newname: ", strlen("Newname: ")))
00595             strncpy(newname, resp + strlen("Newname: "), sizeof(newname) - 1);
00596          if (!strncasecmp(resp, "Oldname: ", strlen("Oldname: ")))
00597             strncpy(oldname, resp + strlen("Oldname: "), sizeof(oldname) - 1);
00598       }
00599       if (strlen(channel)) {
00600          if (!strcasecmp(event, "Hangup")) 
00601             hangup_chan(channel);
00602          else
00603             offhook_chan(channel);
00604       }
00605       if (strlen(newname) && strlen(oldname)) {
00606          if (!strcasecmp(event, "Rename")) {
00607             hangup_chan(oldname);
00608             offhook_chan(newname);
00609          }
00610       }
00611    } else {
00612       /* Consume the rest of the non-event */
00613       while((resp = get_line()) && strlen(resp));
00614    }
00615    if (!resp) {
00616       fprintf(stderr, "disconnected (7)\n");
00617       return -1;
00618    }
00619    return 0;
00620 }
00621 
00622 static void usage(void)
00623 {
00624    printf("Usage: muted [-f] [-d]\n"
00625           "        -f : Do not fork\n"
00626           "        -d : Debug (implies -f)\n");
00627 }
00628 
00629 int main(int argc, char *argv[])
00630 {
00631    int x;
00632    while((x = getopt(argc, argv, "fhd")) > 0) {
00633       switch(x) {
00634       case 'd':
00635          debug = 1;
00636          needfork = 0;
00637          break;
00638       case 'f':
00639          needfork = 0;
00640          break;
00641       case 'h':
00642          /* Fall through */
00643       default:
00644          usage();
00645          exit(1);
00646       }
00647    }
00648    if (load_config())
00649       exit(1);
00650 #ifndef __Darwin__
00651    if (open_mixer())
00652       exit(1);
00653 #endif
00654    if (connect_asterisk()) {
00655 #ifndef __Darwin__
00656       close(mixfd);
00657 #endif
00658       exit(1);
00659    }
00660    if (login_asterisk()) {
00661 #ifndef __Darwin__      
00662       close(mixfd);
00663 #endif
00664       fclose(astf);
00665       exit(1);
00666    }
00667    if (needfork)
00668       daemon(0,0);
00669    for(;;) {
00670       if (wait_event()) {
00671          fclose(astf);
00672          while(connect_asterisk()) {
00673             sleep(5);
00674          }
00675          if (login_asterisk()) {
00676             fclose(astf);
00677             exit(1);
00678          }
00679       }
00680    }
00681    exit(0);
00682 }

Generated on Sat Sep 16 05:47:46 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7