home *** CD-ROM | disk | FTP | other *** search
- /* saystring.c [version 0.8 for NeXT Release 2.1]
- * A command line program to speak a text string through NeXT audio out.
- *
- * This is essentially a program that plays concatenated sound files of words with
- * some special treatment for numbers. As such, it only can speak words that have
- * corresponding sound files. Numbers are spoken by concatenating digit sounds.
- * The program is very simple and low overhead, but should be quite usable for
- * applications that needs only limited vocaburary. This has been written to
- * provide voice alerts for monitoring experimental parameters in a biology lab.
- * (Commands come from remote devices via rsh.)
- *
- * Usage: saystring "A line to be spoken"
- * Example: saystring "temperature #39.4 degrees is too_high"
- * Example: saystring "our phone number is #6 #4 #2 _ #6 #4 #4 #0"
- *
- * You must prepare sound files yourself for numbers and any other words you use.
- * Sound files should be in /usr/local/lib/saystring_sounds directory, but can be changed
- * by SOUNDDIR define below and recompiling. All sound files currently have to
- * be in CODEC (8-bit mu-law, ~8kHz sampling) format, but this can be changed as well.
- *
- * To compile: cc -Wall -o saystring saystring.c
- * will work. You can use strip, or other options to make the executable smaller.
- *
- * This program is in the Public Domain. Do anything you want with it.
- *
- * Version 0.8
- * 92-08-22 Izumi Ohzawa
- * This is a sloppy one-night hack. Only good for single shot uses (Program should
- * terminate for each use) because memory deallocation is pretty much ingnored.
- * Fix malloc/free if you want to incorporate into a long running app as a subroutine.
- * Only handles floats and integers in the range of +/- 9999.9999999...
- * It should not be hard to extend it if you want to.
- */
-
- #import <stdio.h>
- #import <string.h> /* for strlen, rindex, etc */
- #import <stdlib.h>
- #import <sys/param.h>
- #import <sound/sound.h>
-
- /* Change this if you want a different directory to store sound files */
- #define SOUNDDIR "/usr/local/lib/saystring_sounds"
- #define TAG 1
- #define MAXWORDS 200
-
- /*==== Global Vars for sloppy programming ===================================================== */
- /* You must prepare the following *.snd files for numbers to be spoken correctly.
- * In addition, you will need sound files for: "minus", "point", "thousand", "hundred".
- */
- char *dig09[] = {"zero","one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };
- char *teens[] = {"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen",
- "seventeen", "eighteen", "nineteen" };
- char *tens[] = {"", "ten","twenty","thirty","forty", "fifty", "sixty",
- "seventy", "eighty", "ninety" };
-
- char *word[MAXWORDS];
- int nwords = 0;
- char *p;
- char buf[128];
-
- void do_number(void)
- {
- int minus = 0; /* non-zero if negative */
- int isfrac = 0; /* non-zero if there is a decimal point */
- int ipart, temp, saveipart;
- char ch[2];
- char *frac;
- ch[1] =0;
- sscanf(p, "%s", buf); /* get the number into string */
- if(strlen(buf) == 0) return;
- if(atof(buf) < 0.0) /* say "minus" first ? */
- {
- minus = 1; /* indicate negative */
- word[nwords++] = "minus";
- }
- if((frac = index(buf, '.'))) /* Do we have a decimal point? */
- {
- *frac = '\0'; /* replace decimal pt with null char */
- frac++; /* fraction part */
- if(strlen(frac)) /* if there is anything after '.', it is a float # */
- isfrac =1;
- }
- ipart = atoi(buf); /* integer part. buf==NULL if e.g. #.123, which is OK */
- if(ipart < 0) ipart = -ipart; /* make positive */
- if(ipart > 9999) ipart = 9999; /* Our current implementation limit */
- saveipart = ipart; /* save a copy for 0 */
-
- /* do thousands */
- if((temp = ipart/1000))
- {
- word[nwords++] = dig09[temp];
- word[nwords++] = "thousand";
- }
-
- /* hundreds */
- ipart -= temp *1000; /* get rid of thousands */
- if((temp = ipart/100))
- {
- word[nwords++] = dig09[temp];
- word[nwords++] = "hundred";
- }
-
- /* tens */
- ipart -= temp *100; /* get rid of hundreds */
- if((temp = ipart/10))
- {
- if(temp ==1) /* teens */
- {
- word[nwords++] = teens[ipart-10];
- goto do_fraction;
- }
- else
- word[nwords++] = tens[temp];
- }
-
- /* ones */
- ipart -= temp*10; /* get rid of tens */
- if(ipart || saveipart == 0)
- word[nwords++] = dig09[ipart];
-
- do_fraction:
- if(isfrac) /* only if there is anything after '.' */
- {
- word[nwords++] = "point";
- while(*frac != '\0') /* just speak digits sequentiall */
- {
- ch[0] = *frac++;
- temp = atoi(ch);
- word[nwords++] = dig09[temp];
- }
- }
- }
-
-
- void parse_words(char *str)
- {
- p = str;
- while(1)
- {
- while( *p == ' ' && *p != '\0' ) p++; /* position p to head of word */
- if( *p == '\0') break; /* EXIT endless loop */
- sscanf(p, "%s", buf); /* get a word */
- if(buf[0] == '#')
- {
- p++; /* char following # */
- do_number();
- }
- else
- {
- word[nwords] = (char *)malloc((size_t)(strlen(buf)+1));
- strcpy(word[nwords++], buf);
- }
- while( *p != ' ' && *p != '\0') p++;
- }
- }
-
-
- void Serror(int r, int num)
- {
- fprintf(stderr,"saystring[%d]: %s\n", num, SNDSoundError(r));
- }
-
-
- int AppendSoundFromFile(char *file, SNDSoundStruct *toSound)
- {
- int r, r2;
- SNDSoundStruct *sound2;
- char fullpath[MAXPATHLEN];
- sprintf(fullpath, "%s/%s.snd", SOUNDDIR, file);
- r = SNDReadSoundfile(fullpath, &sound2);
- if(r) return(r);
- r = SNDInsertSamples(toSound, sound2, SNDSampleCount(toSound));
- r2 = SNDFree(sound2);
- if(r)
- return(r);
- else
- return(r2);
- }
-
-
- int main(int argc, char *argv[])
- {
- int r, i;
- int priority = 5;
- int preempt = 0;
- SNDSoundStruct *sound1;
-
- if(argc != 2) {
- printf("Usage example: saystring \"temperature #41.3 degrees is too_high\"\n");
- printf(" another: saystring \"our fax number is #1 #2 #3 _ #4 #5 #6 #7\"\n");
- printf(" will speak the strings. All words except for numbers must have\n");
- printf(" a corresponding soundfile in directory %s.\n", SOUNDDIR);
- printf(" The string must be enclosed in quotation marks unless it is\n");
- printf(" a single word. Numbers must be preceded by a # sign as above.\n");
- exit(2);
- }
-
- /* Hmmm, it seems that we don't need SNDAcquire(), SNDRelease() calls for this */
-
- /* Allocate an empty SNDSoundStruct */
- r = SNDAlloc(&sound1, 0, SND_FORMAT_MULAW_8, SND_RATE_CODEC, 1, 4);
- if(r) {
- Serror(r, 2);
- goto do_exit;
- }
-
- /* Put the words in word[] array. nwords will have the total # of words */
- parse_words(argv[1]);
-
- /* Concatenate all words into a single SNDSoundStruct */
- for(i=0; i<nwords; i++)
- r = AppendSoundFromFile(word[i], sound1); /* ignore non-existent sound files */
-
- r = SNDStartPlaying(sound1, TAG, priority, preempt, SND_NULL_FUN, SND_NULL_FUN);
- if(r) {
- Serror(r, 6);
- goto do_exit;
- }
-
- SNDWait(TAG); /* get blocked untill sound finishes */
-
- do_exit:
- exit(0);
- }
-