home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume26 / radio2.0.2 / part02 / broadcast.c next >
Encoding:
C/C++ Source or Header  |  1993-04-15  |  11.8 KB  |  540 lines

  1. /***********************************************************
  2. Copyright 1991, 1992 by Stichting Mathematisch Centrum, Amsterdam, The
  3. Netherlands.
  4.  
  5.                         All Rights Reserved
  6.  
  7. Permission to use, copy, modify, and distribute this software and its 
  8. documentation for any purpose and without fee is hereby granted, 
  9. provided that the above copyright notice appear in all copies and that
  10. both that copyright notice and this permission notice appear in 
  11. supporting documentation, and that the names of Stichting Mathematisch
  12. Centrum or CWI not be used in advertising or publicity pertaining to
  13. distribution of the software without specific, written prior permission.
  14.  
  15. STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
  16. THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  17. FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
  18. FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  19. WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  20. ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
  21. OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  22.  
  23. ******************************************************************/
  24.  
  25. /* Broadcast audio packets over UDP.
  26.    Standard input should be an audio source, e.g. /dev/audio
  27.    (on a Sun) or a pipe from a program generating audio, e.g.
  28.    recordulaw or cdsend (on an SGI Indigo).  Default input format is
  29.    8-bit U-LAW; use -l to read 16-bit linear instead (all mono 8000
  30.    samples/sec).
  31.  
  32.    Command line options:
  33.  
  34.    -a        ADPCM encoding -- half the data, slightly worse sound
  35.    -A        ADPCM encoding without state, even worse sound
  36.    -b ipaddr    IP address to broadcast to; may be repeated
  37.         (default broadcast on local net)
  38.    -c port    listen to this control port (default 54319)
  39.    -d        debug output
  40.    -l        take linear input (signed shorts in native byte order)
  41.    -m ttl    Multicast TTL (0 host, 1 subnet, 32 site, 64 region)
  42.    -n        no silence detection
  43.    -p port    broadcast to this port number (default 54321)
  44.    -t           time output; use when input is faster than realtime
  45.         (e.g., read from a file file)
  46.    -N name    station name (default your username)
  47.    -L file    log file (default /ufs/<username>/CDlog)
  48.    -P file    program file (default /ufs/<username>/CD)
  49. */
  50.  
  51. #include "radio.h"
  52. #include "adpcm.h"
  53.  
  54. #include <stdio.h>
  55. #include <errno.h>
  56. #include <stdlib.h>
  57. #include <fcntl.h>
  58.  
  59. #include <sys/types.h>
  60. #include <sys/socket.h>
  61. #include <netinet/in.h>
  62. #include <netdb.h>
  63. #include <sys/time.h>
  64. #include <sys/stat.h>
  65.  
  66. extern long time();
  67.  
  68. #ifdef SUNHACKS
  69. #include <sys/sockio.h>        /* For SIOCGIF* */
  70. #include <net/if.h>        /* For struct if* */
  71. #endif
  72.  
  73. #ifdef REMHDR
  74. #include <multimedia/libaudio.h>
  75. #include <multimedia/audio_filehdr.h>
  76. #endif
  77.  
  78. #ifdef NeXT
  79. #include <sound/sound.h>
  80. #endif
  81.  
  82. #ifdef __sgi
  83. #include <limits.h>
  84. #include <sys/prctl.h>
  85. #include <sys/schedctl.h>
  86. #endif
  87.  
  88. extern int optind;
  89. extern char * optarg;
  90.  
  91. char *progname;
  92. char infostring[CTLPKTSIZE];
  93. struct timeval zerotime;
  94. struct timeval tstart;
  95. int pdebug = 0;
  96.  
  97. #define NBCADDR 10        /* Max number of broadcast addresses */
  98. int nbcaddr = 0;        /* Number of broadcast address options */
  99. struct sockaddr_in bcaddr[NBCADDR];    /* Broadcast addresses */
  100. struct sockaddr_in infoaddr[NBCADDR];    /* Ditto for info messages */
  101.  
  102. int port = RCVPORT;
  103. char *name = 0;
  104. char *logfile = 0;
  105. char *programfile = 0;
  106. char *user;
  107. char *home;
  108.  
  109. long packetcount = 0;
  110. int transmitting = 1;
  111. int encoding = PCM_64;
  112.  
  113. char *
  114. whoami()
  115. {
  116.     char *user = getenv("LOGNAME");
  117.     if (user == NULL) {
  118.         user = getenv("USER");
  119.         if (user == NULL)
  120.             user = "???";
  121.         /* XXX should use getpwbyuid(getuid) if HOME missing */
  122.     }
  123.     return user;
  124. }
  125.  
  126. char *
  127. whereami()
  128. {
  129.     return getenv("HOME");
  130.     /* XXX should use getpwbyname(user) if HOME missing */
  131. }
  132.  
  133. int
  134. makeinfo()
  135. {
  136.     FILE *fp;
  137.     int n;
  138.     struct stat s;
  139.     long age;
  140.  
  141.     if (stat(programfile, &s) >= 0)
  142.         age = time((long*)0) - s.st_mtime;
  143.     else
  144.         age = -1;
  145.     sprintf(infostring, "radio:S:%s:%d:%d:%s:%ld:",
  146.         name, port, transmitting, logfile, age);
  147.     n = strlen(infostring);
  148.     fp = fopen(programfile, "r");
  149.     if (fp != NULL) {
  150.         fgets(infostring + n, sizeof infostring - n, fp);
  151.         fclose(fp);
  152.         n = strlen(infostring);
  153.         if (infostring[n-1] == '\n')
  154.             infostring[--n] = '\0';
  155.     }
  156.     return n;
  157. }
  158.  
  159. void
  160. sendinfo(s, addr, addrsize)
  161.     int s; /* Socket */
  162.     struct sockaddr *addr;
  163.     int addrsize;
  164. {
  165.     int n = makeinfo();
  166.     if (sendto(s, infostring, n, 0, addr, addrsize) < 0)
  167.         perror("sendto in sendinfo");
  168. }
  169.  
  170. main(argc, argv)
  171.     int argc;
  172.     char **argv;
  173. {
  174.     char *broadcastaddr = NULL;
  175.     char real_buf[HEADERSIZE + 3 + BUFFERSIZE];
  176.     char tmp_buf[BUFFERSIZE];
  177.     short lin_buf[BUFFERSIZE];
  178.     char *buf;
  179.     int on = 1;
  180.     int i, n;
  181.     int s, ctls;
  182.     int c;
  183.     int timing = 0;
  184.     fd_set inputav;
  185.     struct sockaddr_in locsin;
  186.     struct sockaddr_in ctlsin;
  187.     int ctlsinsize;
  188.     int ctlport = BCASTCTLPORT;
  189.     int noisy = 0;
  190.     int linear = 0;
  191.     struct adpcm_state state;
  192. #ifdef HAVE_MCAST
  193.     u_char ttl = MULTICAST_TTL;
  194. #endif
  195.  
  196. #ifdef __sgi
  197.     (void) schedctl(NDPRI, 0, NDPNORMMAX);
  198.     setuid(getuid());
  199. #endif
  200.  
  201.     progname = argv[0];
  202.  
  203. /* Always change these two macros and the following switch together! */
  204. #define OPTIONS "Aab:c:dlm:np:tL:N:P:"
  205. #define USAGE    \
  206.     "usage: %s [-A] [-a] [-b broadcastaddr] ... [-c ctlport] [-d] [-l]\n\
  207. \t[-m ttl] [-n] [-p port] [-t]\n\t[-N name] [-L logfile] [-P programfile]\n"
  208.  
  209.     while ((c = getopt(argc, argv, OPTIONS)) != EOF) {
  210.         switch (c) {
  211.         default:
  212.         case '?':
  213.             fprintf(stderr, USAGE, progname);
  214.             exit(2);
  215.         case 'A':
  216.             encoding = ADPCM_32;
  217.             break;
  218.         case 'a':
  219.             encoding = ADPCM_32_W_STATE;
  220.             break;
  221.         case 'b':
  222.             if (nbcaddr >= NBCADDR) {
  223.                 fprintf(stderr,
  224.                     "%s: too many -b options (max %d)\n",
  225.                     progname, NBCADDR);
  226.                 exit(2);
  227.             }
  228.             if (setipaddr(optarg, &bcaddr[nbcaddr]) < 0) {
  229.                 fprintf(stderr,
  230.                     "%s: bad broadcast address '%s'\n",
  231.                     progname, broadcastaddr);
  232.                 exit(2);
  233.             }
  234.             nbcaddr++;
  235.             break;
  236.         case 'c':
  237.             ctlport = atoi(optarg);
  238.             break;
  239.         case 'd':
  240.             pdebug = 1;
  241.             break;
  242.         case 'l':
  243.             linear = 1;
  244.             break;
  245.         case 'm':
  246. #ifdef HAVE_MCAST
  247.             ttl = atoi(optarg);
  248. #else
  249.             fprintf(stderr, "(-m not supported here)\n");
  250. #endif
  251.             break;
  252.         case 'n':
  253.             noisy = 1;
  254.             break;
  255.         case 'p':
  256.             port = atoi(optarg);
  257.             break;
  258.         case 't':
  259.             timing = 1;
  260.             break;
  261.         case 'L':
  262.             logfile = optarg;
  263.             break;
  264.         case 'N':
  265.             name = optarg;
  266.             break;
  267.         case 'P':
  268.             programfile = optarg;
  269.             break;
  270.         }
  271.     }
  272.  
  273.     user = whoami();
  274.     home = whereami();
  275.  
  276.     if (logfile == 0) {
  277.         static char logbuf[100];
  278.         sprintf(logbuf, "%s/.CDlog", home);
  279.         logfile = logbuf;
  280.     }
  281.     if (name == 0)
  282.         name = user;
  283.     if (programfile == 0) {
  284.         static char programbuf[100];
  285.         sprintf(programbuf, "%s/.CD", home);
  286.         programfile = programbuf;
  287.     }
  288.  
  289.     s = opensock("data", (char *)NULL, SENDPORT, (char *)NULL, 0, 1);
  290.  
  291.     if (nbcaddr == 0) {
  292. #if defined(HAVE_MCAST) && defined (DEFMCAST)
  293.         if (setipaddr(DEFMCAST, &bcaddr[nbcaddr]) < 0) {
  294.             fprintf(stderr,
  295.                 "%s: bad broadcast address '%s'\n",
  296.                 progname, broadcastaddr);
  297.             exit(2);
  298.         }
  299. #else
  300.         configure(s, &bcaddr[0]);
  301. #endif
  302.         nbcaddr = 1;
  303.     }
  304.  
  305.     for (i = 0; i < nbcaddr; i++) {
  306.         bcaddr[i].sin_port = htons(port);
  307.         bcaddr[i].sin_family = AF_INET;
  308.         infoaddr[i] = bcaddr[i];
  309.         infoaddr[i].sin_port = htons(INFOPORT);
  310.     }
  311.  
  312. #ifdef HAVE_MCAST
  313.     if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
  314.         perror("mcast ttl");
  315. #endif
  316.  
  317.     ctls = opensock("control", (char *)NULL, ctlport, (char *)NULL, 0, 0);
  318.  
  319.     if(timing) {
  320.         if (!linear) {
  321. #ifdef REMHDR
  322.             Audio_hdr hp;
  323.             (void) audio_read_filehdr(0, &hp, NULL, NULL);
  324. #endif
  325. #ifdef NeXT
  326.             SNDSoundStruct s;
  327.             (void) fread((void *)&s, sizeof(SNDSoundStruct), 1,
  328.                      stdin);
  329. #endif
  330.         }
  331.         gettimeofday(&tstart, 0);
  332.     }
  333.  
  334.     real_buf[0] = AUDIO_TYPE;
  335.     real_buf[1] = encoding;
  336.  
  337.     state.valprev = 0;
  338.     state.index = 0;
  339.  
  340.     for (;;) {
  341.         if (linear) {
  342.             n = fread(lin_buf, sizeof(short), BUFFERSIZE, stdin);
  343.             buf = real_buf + HEADERSIZE;
  344.         }
  345.         else {
  346.             if (encoding == PCM_64)
  347.                 buf = real_buf + HEADERSIZE;
  348.             else
  349.                 buf = tmp_buf;
  350.             n = fread(buf, 1, BUFFERSIZE, stdin);
  351.         }
  352.         if (n <= 0) {
  353.             if (n < 0)
  354.                 perror("fread");
  355.             break;
  356.         }
  357.         if(timing)
  358.             waiting(SAMPLINGRATE, BUFFERSIZE);
  359.         if (!linear && !noisy && silent(buf, n)) {
  360.             if (transmitting) {
  361.                 if (pdebug)
  362.                     fprintf(stderr, "start silence\n");
  363.                 transmitting = 0;
  364.             }
  365.         }
  366.         else {
  367.             if (!transmitting) {
  368.                 if (pdebug)
  369.                     fprintf(stderr, "end silence\n");
  370.                 packetcount = 0;
  371.                 transmitting = 1;
  372.             }
  373.             switch (encoding) {
  374.             case PCM_64:
  375.                 if (linear) {
  376.                     for (i = 0; i < n; i++) {
  377.                         buf[i] = st_linear_to_ulaw(
  378.                              lin_buf[i]);
  379.                     }
  380.                 }
  381.                 break;
  382.             case ADPCM_32:
  383.                 buf = real_buf + HEADERSIZE;
  384.                 if (linear)
  385.                     adpcm_coder(lin_buf, buf, n,
  386.                          (struct adpcm_state *)0);
  387.                 else
  388.                     ulaw_adpcm_coder(tmp_buf, buf, n,
  389.                          (struct adpcm_state *)0);
  390.                 n = (n+1)/2;
  391.                 break;
  392.             case ADPCM_32_W_STATE:
  393.                 buf = real_buf + HEADERSIZE;
  394.                 *buf++ = (state.valprev>>8) & 0xff;
  395.                 *buf++ = state.valprev & 0xff;
  396.                 *buf++ = state.index;
  397.                 if (linear)
  398.                     adpcm_coder(lin_buf, buf, n, &state);
  399.                 else
  400.                     ulaw_adpcm_coder(tmp_buf, buf, n,
  401.                              &state);
  402.                 n = (n+1)/2 + 3;
  403.                 break;
  404.             }
  405.             for (i = 0; i < nbcaddr; i++) {
  406.                 /* Send data packets to all bcast ports */
  407.                 if (sendto(s, real_buf, HEADERSIZE+n, 0,
  408.                        &bcaddr[i], sizeof bcaddr[i]) !=
  409.                                                 HEADERSIZE+n) {
  410.                     perror("sendto");
  411.                 }
  412.             }
  413.             if (packetcount % INFOFREQ == 0) {
  414.                 /* Send info packets to all info ports
  415.                    and to all bcast ports */
  416.                 if (pdebug)
  417.                     fprintf(stderr, "sending info\n");
  418.                 for (i = 0; i < nbcaddr; i++) {
  419.                     sendinfo(s, &infoaddr[i],
  420.                          sizeof infoaddr[i]);
  421.                     sendinfo(s, &bcaddr[i],
  422.                          sizeof bcaddr[i]);
  423.                 }
  424.             }
  425.             if (pdebug) {
  426.                 if(packetcount % 8 == 0) {
  427.                     fprintf(stderr, "%ld packets sent\n",
  428.                         packetcount);
  429.                 }
  430.             }
  431.             packetcount++;
  432.         }
  433.         if (ctls >= 0) {
  434.             FD_ZERO(&inputav);
  435.             FD_SET(ctls, &inputav);
  436.             if (select(ctls+1, &inputav, 0, 0, &zerotime) == 1) {
  437.                 ctlsinsize = sizeof(ctlsin);
  438.                 n = recvfrom(ctls, buf, BUFFERSIZE, 0,
  439.                          &ctlsin, &ctlsinsize);
  440.                 if (n < 0) {
  441.                     perror("recvfrom");
  442.                     exit(1);
  443.                 }
  444.                 if (n >= 7 &&
  445.                     strncmp(buf, "radio:s", 7) == 0) {
  446.                     sendinfo(ctls, &ctlsin, ctlsinsize);
  447.                 }
  448.                 else {
  449.                     fprintf(stderr,
  450.                         "%s: Funny ctl message\n",
  451.                         progname);
  452.                 }
  453.             }
  454.         }
  455.     }
  456.  
  457.     exit(0);
  458. }
  459.  
  460. configure(s, addr_ret)
  461.     int s;
  462.     struct sockaddr_in *addr_ret;
  463. {
  464. #ifdef SUNHACKS
  465.     char buf[BUFSIZ];
  466.     struct ifconf ifc;
  467.     struct ifreq ifreq;
  468.  
  469.     ifc.ifc_len = sizeof(buf);
  470.     ifc.ifc_buf = buf;
  471.     if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
  472.         perror("ioctl SIOCGIFCONF");
  473.         exit(1);
  474.     }
  475.     ifreq = *ifc.ifc_req;
  476.     if (ioctl(s, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
  477.         perror("ioctl SIOCGIFBRDADDR");
  478.         exit(1);
  479.     }
  480.     * (struct sockaddr *) addr_ret = ifreq.ifr_broadaddr;
  481. #else
  482.     addr_ret->sin_addr.s_addr = INADDR_BROADCAST;
  483. #endif
  484. }
  485.  
  486. /*
  487.  * routine to sleep between consecutive packets
  488.  */
  489.  
  490. waiting(rate, data)
  491.     int rate;
  492.     int data;
  493. {
  494.     static int bytes = 0; /* packets already sent */
  495.  
  496.     struct timeval tnow;
  497.     int tsleep;
  498.  
  499.     bytes += data;
  500.     gettimeofday(&tnow, 0);
  501.  
  502.     tsleep =  ((double) bytes/(double) rate) * 1000000
  503.         - ((tnow.tv_sec - tstart.tv_sec) * 1000000
  504.                     + tnow.tv_usec - tstart.tv_usec);
  505.     if (tsleep > 0) {
  506.         struct timeval t;
  507.  
  508.         t.tv_sec = tsleep / 1000000;
  509.         t.tv_usec = tsleep % 1000000;
  510.         (void) select(0, NULL, NULL, NULL, &t);
  511.     }
  512. }
  513.  
  514. /*
  515.  * Silence detection.
  516.  * You may have to play with these parameters.
  517.  * Our input is rather noisy, hence we have a rather high threshold.
  518.  */
  519.  
  520. #define DEADTIME (20*SAMPLINGRATE) /* After this much silence we cut off */
  521. #define THRESHOLD 75 /* Max silent U-LAW value (after normalization) */
  522.  
  523. silent(buf, n)
  524.     register char *buf;
  525.     register int n;
  526. {
  527.     static int dead = DEADTIME; /* State */
  528.     register int abs;
  529.  
  530.     dead += n;
  531.     while (--n >= 0) {
  532.         abs = 127 - ((*buf++) & 127);
  533.         if (abs > THRESHOLD) {
  534.             dead = 0;
  535.             return 0;
  536.         }
  537.     }
  538.     return (dead > DEADTIME);
  539. }
  540.