/* Note: I'm sure that some of these may be improved and would appreciate any feedback */ /* A set of routines to allow a user to program in BDS C words and phrases using a VOTRAX SC-01 chip synthesizer. The main functions is "talk" which takes a character string argument that looks like phonemes. The subroutine "pronounce" does the port I/O and will need to be be individually modified, most likely. Read the comments above it */ /* written by Mark Zachmann Sept. 5 1981 Version 0.2 */ /* main talking functions... what a slow way to do things */ /* it reads a character string corresponding to the phoneme descriptions in the VOTRAX phoneme chart (page 3 of SC-01 data sheet) and decodes them into numbers (type char, though), then says them. Note that because it takes a while to talk I say each phoneme seperately, and thus, while saying one phoneme, the next can be decoded from the character string input */ talk(line) char *line; { while(0!=*line) { pronounce(phoneme(line)); /* transmit to chip */ while(','!=*line++); /* increment counter to start of next phoneme */ } } /* Subroutine to take a character string and return a number (type char) corresponding to the first phoneme found in the string. Returns 0xFF if no match (although partial matches will have undefined results, e.g. O5 may be the same as O1) */ phoneme(line) char *line; { switch(*line++) { case 'A': switch(*line) { case '1': return(5); /* A1 */ case '2': return(6); /* A2 */ case 'Y': return(0x21); /* AY */ case 'E': { if (','==*++line) return(0x2e); /* AE */ return(0x2f); /* AE1 */ } case 'H': { if (','==*++line) return(0x24); /* AH */ if('1'==*line) return(0x15); /* AH1 */ return(8); /* AH2 */ } case 'W': { if(','==*++line) return(0x3d); /* AW */ if('1'==*line) return(0x13); /* AW1 */ return(0x30); /* AW2 */ } } case 'B': return(0xe); /* B */ case 'C': return(0x10); /* CH */ case 'D': { if(','==*line) return(0x1e); /* D */ return(0x4); /* DT */ } case 'E': switch(*line) { case ',': return(0x2c); /* E */ case 'R': return(0x3a); /* ER */ case '1': return(0x3c); /* E1 */ case 'H': switch(*++line) { case ',': return(0x3b); /* EH */ case '1': return(2); /* EH1 */ case '2': return(1); /* EH2 */ default: return(0); /* EH3 */ } } case 'F': return(0x1d); /* F */ case 'G': return(0x1c); /* G */ case 'H': return(0x1b); /* H */ case 'I': switch(*line) { case ',': return(0x27); /* I */ case 'U': return(0x36); /* IU */ case '1': return(0x0b); /* I1 */ case '2': return(0x0a); /* I2 */ default: return(9); /* I3 */ } case 'J': return(0x1a); /* J */ case 'K': return(0x19); /* K */ case 'L': return(0x18); /* L */ case 'M': return(0x0c); /* M */ case 'N': if(','==*line) return(0x0d); /* N */ return(0x14); /* NG */ case 'O': switch(*line) { case ',': return(0x26); /* O */ case '1': return(0x35); /* O1 */ case '2': return(0x34); /* O2 */ case 'O': { if(','==*++line) return(0x17); /* OO */ return(0x16); /* OO1 */ } } case 'P': { if(','==*line) return(0x25); /* P */ if('0'==*++line) return(3); /* PA0 */ return(0x3e); /* PA1 */ } case 'R': return(0x2b); /* R */ case 'S': { if(','==*line) return(0x1f); /* S */ if('H'==*line) return(0x11); /* SH */ return(0x3f); /* STOP */ } case 'T': { if(','==*line) return(0x2a); /* T */ if(','==*++line) return(0x39); /* TH */ return(0x38); /* THV */ } case 'U': switch(*line) { case ',': return(0x28); /* U */ case '1': return(0x37); /* U1 */ default: switch(*++line) { case ',': return(0x33); /* UH */ case '1': return(0x32); /* UH1 */ case '2': return(0x31); /* UH2 */ default: return(0x23); /* UH3 */ } } case 'V': return(0x0f); /* V */ case 'W': return(0x2d); /* W */ case 'Y': { if(','==*line) return(0x29); /* Y */ return(0x22); /* Y1 */ } case 'Z': { if(','==*line) return(0x12); /* Z */ return(7); /* ZH */ } default : return(0xff); /* error !! */ } } /* this is the pronunciation subroutine. It sends a single phoneme to the Votrax synthesis chip. I have implemented mine by way of the iobyte. When the list device is set to UC3: my BIOS sends output to the synthesizer. Thus all handshaking is external to this program. !! You will need to modify either this or your BIOS. I suggest the BIOS as then BASIC and etc. stupid compilers can easily interface to the chip */ pronounce(c) char(c); { char iobyt; iobyt=bdos(7); /* read iobyte */ bdos(8,0xc0); /* set list to UC3: */ bios(5,(c|0x80)); /* send character, I set my pitch to 80h always */ bdos(8,iobyt); /* reset IOBYTE to whatever it was */ } /* program to say a number argument assumes unsigned input, and calls itself recusively. Not terribly fast */ saynum(numb) unsigned numb; { char c,d,some; unsigned j; some=0; /* nothing said yet */ /* first process the 1000's */ if(0!=(j=numb/1000)) { saynum(j); /* Recur to do thousands */ talk("TH,AH1,W,Z,I1,N,D,PA1,"); /* thousand */ numb%=1000; /* continue with left over */ some=1 ; /* flag it as having said something */ } /* now the 100's */ if(0!=(j=numb/100)) { saynum(j); /* recurring */ talk("H,H,UH,N,D,R,I3,D,PA1,"); /* hundred */ numb%=100; /* continue again */ some=1 ; /* flag it */ } /* now separate the 10's off (if > 20) */ if((numb>=20)&&0!=(j=numb%10)) /* recur again if not simple */ { saynum(numb-j); /* first do the tens case */ numb=j; /* now the remainder */ some=1; } /* main non-recursive portion */ c=numb; /* turn into a char for speed */ d=(c>=20)? c/10 : c%10 ; /* the kernel is here */ switch(d) { case 1 : { if(c==1) talk("W,UH1,I3,N,"); /* 1 */ else talk("E1,L,EH1,V,I1,N,"); /* 11 */ break; } case 2 : { if(c==20) talk("T,PA0,W,EH2,N,"); /* 20 */ else if(c==12) talk("T,PA0,W,EH2,UH2,L,V,"); /* 12 */ else talk("T,PA0,U,W,"); /* 2 */ break; } case 3 : { if(c==3) talk("TH,R,E1,Y,"); /* 3 */ else talk("TH,UH2,ER,"); /* 13,31 */ break ; } case 4 : { if(c==4) talk("F,O,ER,"); /* 4 */ else talk("F,O1,R,"); /* 14,41 */ break; } case 5 : { if(c==5) talk("F,AH1,Y1,V,"); /* 5 */ else talk("F,I1,F,"); /* 15,51 */ break; } case 6 : talk("S,I,K,S,"); break; /* 6's */ case 7 : talk("S,EH,V,I3,N,"); break; /* 7's */ case 8 : talk("A1,Y,T,"); break ; /* 8's */ case 9 : talk("N,AH2,Y,N,"); break ; /* 9's */ case 0 : { if(c==10) talk("T,EH,N,"); /* 10 */ else if(!some) talk("Z,E1,R,O1,W,"); /* 0 */ break ; } } if(c>=20) talk("STOP,T,E1,PA0,"); /* ty */ else if(c>12) talk("STOP,T,E1,E1,N,PA0,"); /* teen */ else talk("PA1,"); /* done */ } /* */ /* says the day of the week, 0 is sunday 6 is saturday */ sayday(dayow) char dayow; { switch(dayow) { case 0: talk("S,UH,N,N,"); break; case 1: talk("M,UH,N,"); break; case 2: talk("T,PA0,U,EH3,Z,STOP,"); break; case 3: talk("W,EH,N,Z,"); break; case 4: talk("TH,U1,ER,Z,"); break; case 5: talk("F,R,AH1,Y,"); break; case 6: talk("S,AE1,EH3,T,ER,"); break; default: talk("H,W,UH,T,"); break; /* what? */ } talk("D,A1,AY,Y1,STOP,STOP,"); /* day */ } /* */ /* says the month desired */ saymonth(month) /* month 0-January...11-December */ char(month); { switch(month) { case 0: talk("D,J,I3,AE1,UH2,N,Y,IU,U1,W,AE1,EH3,R,Y,"); break; case 1: talk("F,EH1,B,Y,IU,U1,W,AE1,EH3,R,Y,"); break; case 2: talk("M,AH2,ER,T,CH,"); break; case 3: talk("A1,AY,P,R,OO1,L,"); break; case 4: talk("M,A,Y,"); break; case 5: talk("D,J,U1,U,N,"); break; case 6: talk("D,J,U1,UH3,L,AR1,E1,"); break; case 7: talk("AW,G,UH3,S,T,"); break; case 8: talk("S,EH1,P,STOP,T,EH1,EH2,M,B,R,"); break; case 9: talk("AH1,K,STOP,T,O,W,B,R,"); break; case 10: talk("N,O1,W,PA0,V,EH,M,B,R,"); break; case 11: talk("D,I2,Y,S,EH,M,B,R,"); break; default: talk("H,W,UH,T,"); break; } }