This is saystring.c in view mode; [Download] [Up]
/* 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);
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.