home *** CD-ROM | disk | FTP | other *** search
/ Peanuts NeXT Software Archives / Peanuts-2.iso / Unix / audio / saystring.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-18  |  6.7 KB  |  225 lines

  1. /* saystring.c [version 0.8 for NeXT Release 2.1]
  2.  * A command line program to speak a text string through NeXT audio out.
  3.  *
  4.  * This is essentially a program that plays concatenated sound files of words with
  5.  * some special treatment for numbers.  As such, it only can speak words that have
  6.  * corresponding sound files.  Numbers are spoken by concatenating digit sounds.
  7.  * The program is very simple and low overhead, but should be quite usable for
  8.  * applications that needs only limited vocaburary.  This has been written to
  9.  * provide voice alerts for monitoring experimental parameters in a biology lab.
  10.  * (Commands come from remote devices via rsh.)
  11.  *
  12.  * Usage:   saystring "A line to be spoken"
  13.  * Example: saystring "temperature #39.4 degrees is too_high"
  14.  * Example: saystring "our phone number is #6 #4 #2 _ #6 #4 #4 #0"
  15.  *
  16.  * You must prepare sound files yourself for numbers and any other words you use.
  17.  * Sound files should be in /usr/local/lib/saystring_sounds directory, but can be changed
  18.  * by SOUNDDIR define below and recompiling.  All sound files currently have to
  19.  * be in CODEC (8-bit mu-law, ~8kHz sampling) format, but this can be changed as well.
  20.  *
  21.  * To compile: cc -Wall -o saystring saystring.c
  22.  *    will work.  You can use strip, or other options to make the executable smaller.
  23.  *
  24.  * This program is in the Public Domain.  Do anything you want with it.
  25.  *
  26.  * Version 0.8
  27.  * 92-08-22 Izumi Ohzawa
  28.  * This is a sloppy one-night hack.  Only good for single shot uses (Program should
  29.  * terminate for each use) because memory deallocation is pretty much ingnored.
  30.  * Fix malloc/free if you want to incorporate into a long running app as a subroutine.
  31.  * Only handles floats and integers in the range of +/- 9999.9999999...
  32.  * It should not be hard to extend it if you want to.
  33.  */
  34.  
  35. #import <stdio.h>
  36. #import <string.h>        /* for strlen, rindex, etc */
  37. #import <stdlib.h>
  38. #import <sys/param.h>
  39. #import <sound/sound.h>
  40.  
  41. /* Change this if you want a different directory to store sound files */
  42. #define    SOUNDDIR    "/usr/local/lib/saystring_sounds"
  43. #define    TAG        1
  44. #define MAXWORDS    200
  45.  
  46. /*==== Global Vars for sloppy programming ===================================================== */
  47. /* You must prepare the following *.snd files for numbers to be spoken correctly.
  48.  * In addition, you will need sound files for: "minus", "point", "thousand", "hundred".
  49.  */
  50. char *dig09[] = {"zero","one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };
  51. char *teens[] = {"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen",
  52.          "seventeen", "eighteen", "nineteen" };
  53. char *tens[] = {"", "ten","twenty","thirty","forty", "fifty", "sixty",
  54.         "seventy", "eighty", "ninety" };
  55.  
  56. char *word[MAXWORDS];
  57. int nwords = 0;
  58. char *p;
  59. char buf[128];
  60.  
  61. void do_number(void)
  62. {
  63. int minus = 0;        /* non-zero if negative */
  64. int isfrac = 0;        /* non-zero if there is a decimal point */
  65. int ipart, temp, saveipart;
  66. char ch[2];
  67. char *frac;
  68.     ch[1] =0;
  69.     sscanf(p, "%s", buf);        /* get the number into string */
  70.     if(strlen(buf) == 0) return;
  71.     if(atof(buf) < 0.0)        /* say "minus" first ? */
  72.     {
  73.         minus = 1;            /* indicate negative */
  74.         word[nwords++] = "minus";
  75.     }
  76.     if((frac = index(buf, '.')))    /* Do we have a decimal point? */
  77.     {
  78.         *frac = '\0';        /* replace decimal pt with null char */
  79.         frac++;            /* fraction part */
  80.         if(strlen(frac))        /* if there is anything after '.', it is a float # */
  81.             isfrac =1;
  82.     }
  83.     ipart = atoi(buf);        /* integer part. buf==NULL if e.g. #.123, which is OK */
  84.     if(ipart < 0) ipart = -ipart;    /* make positive */
  85.     if(ipart > 9999) ipart = 9999;    /* Our current implementation limit */
  86.     saveipart = ipart;        /* save a copy for 0 */
  87.  
  88.     /* do thousands */
  89.     if((temp = ipart/1000))
  90.     {
  91.         word[nwords++] = dig09[temp];
  92.         word[nwords++] = "thousand";
  93.     }
  94.  
  95.     /* hundreds */
  96.     ipart -= temp *1000;        /* get rid of thousands */
  97.     if((temp = ipart/100))
  98.     {
  99.         word[nwords++] = dig09[temp];
  100.         word[nwords++] = "hundred";
  101.     }
  102.  
  103.     /* tens */
  104.     ipart -= temp *100;        /* get rid of hundreds */
  105.     if((temp = ipart/10))
  106.     {
  107.         if(temp ==1)        /* teens */
  108.         {
  109.         word[nwords++] = teens[ipart-10];
  110.             goto do_fraction;
  111.         }
  112.         else
  113.         word[nwords++] = tens[temp];
  114.     }
  115.  
  116.     /* ones */
  117.     ipart -= temp*10;        /* get rid of tens */
  118.     if(ipart || saveipart == 0)
  119.         word[nwords++] = dig09[ipart];
  120.  
  121. do_fraction:
  122.     if(isfrac)            /* only if there is anything after '.' */
  123.     {
  124.         word[nwords++] = "point";
  125.         while(*frac != '\0')    /* just speak digits sequentiall */
  126.         {
  127.         ch[0] = *frac++;
  128.         temp = atoi(ch);
  129.         word[nwords++] = dig09[temp];
  130.         }
  131.     }
  132. }
  133.  
  134.  
  135. void parse_words(char *str)
  136. {
  137.     p = str;
  138.     while(1)
  139.     {
  140.     while( *p == ' ' && *p != '\0' ) p++;        /* position p to head of word */
  141.     if( *p == '\0') break;                /* EXIT endless loop */
  142.     sscanf(p, "%s", buf);                /* get a word */
  143.     if(buf[0] == '#')
  144.     {
  145.         p++;                    /* char following # */
  146.         do_number();
  147.     }
  148.     else
  149.     {
  150.         word[nwords] = (char *)malloc((size_t)(strlen(buf)+1));
  151.         strcpy(word[nwords++], buf);
  152.     }
  153.     while( *p != ' ' && *p != '\0') p++;
  154.     }
  155. }
  156.  
  157.  
  158. void Serror(int r, int num)
  159. {
  160.     fprintf(stderr,"saystring[%d]: %s\n", num, SNDSoundError(r));
  161. }
  162.  
  163.  
  164. int AppendSoundFromFile(char *file, SNDSoundStruct *toSound)
  165. {
  166. int r, r2;
  167. SNDSoundStruct *sound2;
  168. char fullpath[MAXPATHLEN];
  169.     sprintf(fullpath, "%s/%s.snd", SOUNDDIR, file);
  170.     r = SNDReadSoundfile(fullpath, &sound2);
  171.     if(r) return(r);
  172.     r = SNDInsertSamples(toSound, sound2, SNDSampleCount(toSound));
  173.     r2 = SNDFree(sound2);
  174.     if(r)
  175.         return(r);
  176.     else
  177.     return(r2);
  178. }
  179.  
  180.  
  181. int main(int argc, char *argv[])
  182. {
  183. int r, i;
  184. int priority = 5;
  185. int preempt = 0;
  186. SNDSoundStruct *sound1;
  187.  
  188.     if(argc != 2) {
  189.     printf("Usage example: saystring \"temperature #41.3 degrees is too_high\"\n");
  190.     printf("      another: saystring \"our fax number is #1 #2 #3 _ #4 #5 #6 #7\"\n");
  191.     printf("   will speak the strings.  All words except for numbers must have\n");
  192.     printf("   a corresponding soundfile in directory %s.\n", SOUNDDIR);
  193.     printf("   The string must be enclosed in quotation marks unless it is\n");
  194.     printf("   a single word.  Numbers must be preceded by a # sign as above.\n");
  195.     exit(2);
  196.     }
  197.  
  198.     /* Hmmm, it seems that we don't need SNDAcquire(), SNDRelease() calls for this */
  199.  
  200.     /* Allocate an empty SNDSoundStruct */
  201.     r = SNDAlloc(&sound1, 0, SND_FORMAT_MULAW_8, SND_RATE_CODEC, 1, 4);
  202.     if(r) {
  203.     Serror(r, 2);
  204.     goto do_exit;
  205.     }
  206.  
  207.     /* Put the words in word[] array. nwords will have the total # of words */
  208.     parse_words(argv[1]);
  209.  
  210.     /* Concatenate all words into a single SNDSoundStruct */
  211.     for(i=0; i<nwords; i++)
  212.     r = AppendSoundFromFile(word[i], sound1);    /* ignore non-existent sound files */
  213.  
  214.     r = SNDStartPlaying(sound1, TAG, priority, preempt, SND_NULL_FUN, SND_NULL_FUN);
  215.     if(r) {
  216.     Serror(r, 6);
  217.     goto do_exit;
  218.     }
  219.  
  220.     SNDWait(TAG);        /* get blocked untill sound finishes */
  221.  
  222. do_exit:
  223.     exit(0);
  224. }
  225.