Speech Synthesizer

Date: 1984
Type: Program
Platform(s): TS 2068

This package is a speech synthesizer system consisting of three components: a demonstration program, a word library compiler, and reusable speech and library-display subroutines. Words are stored as numeric indices in a machine code library residing in high memory around address 65022, and speech is triggered by writing a two-byte word number into fixed memory locations (65023–65024) and then calling the machine code routine at address 65077 via RANDOMIZE USR. The compiler program allows users to select words from one or more existing libraries, sorts selected word entries by their memory addresses, copies them using a machine code block-move routine at address 65195, and merges multiple sub-libraries into a single consolidated CODE file. The demo program shows the synthesizer counting from one to ten, speaking directional words while animating PLOT/DRAW graphics, and accepts free-text numeric sentences entered by the user. The SOUND keyword is used throughout for audio feedback chimes between operations.


Program Analysis

The listing contains three distinct programs that together form a speech synthesizer system. The first (lines 10–9010) is a demonstration program. The second (lines 1–9010, second occurrence) is a word library compiler. Shared subroutine blocks for speaking, library display, and the SOUND chime appear in both programs as well as in two additional standalone subroutine excerpts.

Program Structure

ProgramKey Line RangePurpose
Demo10–110Animated speech demo: counting, directions, graphics
Demo (input mode)2000–2060User-entered numeric sentence playback
Demo (utilities)3000–9010Library display, speak subroutine, save/verify, SOUND chime
Compiler1–510Selects, sorts, copies, and merges word blocks from libraries
Compiler (utilities)3000–9010Same library display, speak, save/verify, chime subroutines
Standalone subroutines2000–4005 (×2)Sentence parser and speak routine fragments

Machine Code Interface

All speech output passes through a common BASIC subroutine at line 4002. It writes the word number NO as a big-endian 16-bit value into addresses 65023–65024, then writes the same value plus a two-byte header (length=1, data=0) into the low-memory pointer area starting at the address held in DW. The speech machine code is then invoked with RANDOMIZE USR 65077.

  • 65077 — main speech playback routine entry point
  • 65150 — used in the compiler to retrieve the address of the most recently spoken word (USR 65150+4)
  • 65195 — block-copy/move routine, called after POKEing source address, source length, and destination address into fixed parameter locations
  • 65207–65208 — two-byte pointer to DW, the current low boundary of the word library in high memory
  • 65022 — top of word library; the library is traversed downward from here

The word library itself is stored in high memory as a series of variable-length records. Each record consists of a two-byte word number, a two-byte length field, and the raw phoneme or audio data. The library display subroutine at line 3000 walks this structure downward: it reads NO from PEEK WA * 256 + PEEK (WA-1) and the block length NL from PEEK (WA-2) * 256 + PEEK (WA-3), then steps WA = WA - 4 - NL until WA <= DW.

Compiler Logic

The compiler (second program) lets the user select up to 50 words per library pass. After selection, the word entries are sorted in descending address order using a straightforward bubble sort (lines 200–230). Any word whose stored address falls at or below DW is flagged as not present and excluded (line 250). Selected words are then block-copied into a fresh library starting at address 65022 and growing downward using the machine code mover at 65195 (lines 300–350). The new bottom boundary is stored back into 65207–65208.

When multiple source libraries are specified (NL1 > 1), the compiler saves each sub-library as a CODE block, then reloads and merges them in order (lines 460–485). The merge step calculates offsets relative to 65022 and calls the block-move routine again to concatenate all selected words into a single contiguous library.

Sentence Parser

The sentence input routine at line 2000 appends a trailing space to the user’s input, then scans character by character for spaces. Each space-delimited token is converted to a number with VAL S$(B TO X-1) and spoken via GO SUB 4002. This means sentences are entered as space-separated word index numbers, not natural language text.

Demo Program Techniques

The demo at lines 10–110 uses a DATA statement at line 9010 containing the numeric word indices for all phrases spoken. RESTORE at line 12 resets the data pointer, and repeated READ NO: GO SUB 4002 loops consume words in sequence. Lines 37 prints each number on screen as it is spoken, providing a visual cue. Graphics animation (lines 50–90) uses PLOT and DRAW, then immediately redraws with INVERSE 1 to erase the line, creating a simple animation effect synchronized with spoken directional words.

SOUND Chime Subroutine

Line 8000 implements an audio feedback chime using two SOUND statements: SOUND 0,41;1,0;8,13;7,62 followed by a short PAUSE 5 and then SOUND 8,0;7,63. This subroutine is called between major operations (after saving, verifying, etc.) as a ready signal.

Notable Idioms and Anomalies

  • The compiler uses a variable named B as both a loop scalar in the bubble sort (line 220) and as an array B() holding word numbers — these are distinct in BASIC but easy to confuse on reading.
  • Line 40 tests A$="n" OR a$="N" — mixing case of the variable name A$ vs a$. In standard Spectrum BASIC, variable names are case-insensitive for numeric variables but string variable names are matched case-sensitively on some systems; both forms are used defensively here.
  • The variable NL is used both as “number of libraries” in the compiler’s outer scope and as the per-record byte-length field in the library display subroutine (line 3020), which could cause a naming conflict if the display subroutine is called from within the compiler loop — this is a latent bug.
  • Line 9000 (READ NO: GO SUB 4002: GO TO 9000) is an infinite loop for speaking all DATA items sequentially; it is never called from the main demo flow but serves as a utility entry point.
  • The variable W1 is computed at line 460 (LET W1=W+367) but never subsequently used — this appears to be dead code left over from development.

Content

Appears On

Related Products

Machine language program, multiple vocabularies, flexible memory requirements, very intelligible. Nine programs include 273 high quality digitized words in variable...

Related Articles

Related Content

Image Gallery

Source Code

   10 REM ***  DEMO PROGRAM *1984              SPEECH SYNTHESIZER 
   11 PRINT AT 10,10; FLASH 1;"ATTENTION"
   12 RESTORE 
   15 GO SUB 8000: GO SUB 8000: PAUSE 20
   20 GO SUB 8000: GO SUB 8000
   30 FOR X=1 TO 2: READ NO: GO SUB 4002: NEXT X: PAUSE 45
   31 CLS : PRINT "LISTEN AND WATCH THE DISPLAY AS I COUNT TO 10"
   32 FOR X=1 TO 10: READ NO: GO SUB 4002: NEXT X: PAUSE 45
   37 FOR X=1 TO 10: READ NO: PRINT NO;" ";: GO SUB 4002: NEXT X: PAUSE 45
   38 PRINT '"I CAN COUNT TO A MILLION IF I   WANT TO"
   40 FOR X=1 TO 10: READ NO: GO SUB 4002: NEXT X: PAUSE 45
   42 CLS : PRINT '"I AM THE COMPUTER, THIS HAPPENS TO BE MY NAME"
   45 FOR X=1 TO 10: READ NO: GO SUB 4002: NEXT X: PAUSE 45
   47 CLS : PRINT '"I CAN WATCH A PIXEL:"
   50 FOR X=1 TO 5: READ NO: GO SUB 4002: NEXT X: FOR X=-10 TO 0: PLOT 127,40+X: DRAW -80,X: NEXT X
   51 FOR X=-10 TO 0: PLOT INVERSE 1;127,40+X: DRAW INVERSE 1;-80,X: NEXT X
   55 PRINT "LEFT"
   60 READ NO: GO SUB 4002
   70 READ NO: GO SUB 4002: FOR X=0 TO 10: PLOT 127,40+X: DRAW 80,X: NEXT X
   71 FOR X=0 TO 10: PLOT INVERSE 1;127,40+X: DRAW INVERSE 1;80,X: NEXT X
   75 PRINT "RIGHT"
   80 READ NO: GO SUB 4002: PRINT "UP": FOR X=-10 TO 0: PLOT 127+X,77: DRAW X,70: NEXT X
   81 FOR X=-10 TO 0: PLOT INVERSE 1;127+X,77: DRAW INVERSE 1;X,70: NEXT X
   90 READ NO: PRINT "AND DOWN": GO SUB 4002: READ NO: GO SUB 4002: FOR X=0 TO 10: PLOT 150+X,77: DRAW X,-70: NEXT X
  100 PAUSE 20: CLS : PRINT "BUT YOU DO NOT KNOW THIS,YOU ARE ___": FOR X=1 TO 10: READ NO: GO SUB 4002: NEXT X
  110 STOP 
 2000 INPUT "ENTER SENTENCE TO BE SPOKEN ";S$: LET S$=S$+" "
 2010 LET B=1: FOR X=1 TO LEN S$
 2020 IF S$(X TO X)=" " THEN LET NO=VAL S$(B TO X-1): GO SUB 4002: LET B=X
 2050 NEXT X
 2060 STOP 
 3000 REM **ROUTINE TO DISPLAY LIBRARY**
 3005 LET WA=65022
 3010 LET NO=PEEK WA*256+PEEK (WA-1)
 3020 LET NL=PEEK (WA-2)*256+PEEK (WA-3)
 3030 PRINT NO;" ";
 3040 GO SUB 4002
 3050 LET WA=WA-4-NL
 3060 IF WA>DW THEN GO TO 3010
 3065 PRINT 
 3070 RETURN 
 4000 INPUT "enter # TO be spoken";no
 4002 POKE 65024,INT (no/256): POKE 65023,no-(INT (no/256)*256)
 4003 LET dw=PEEK 65208*256+PEEK 65207: POKE dw,INT (no/256): POKE dw-1,no-(INT (no/256)*256): POKE dw-2,0: POKE dw-3,1
 4005 RANDOMIZE USR 65077: RETURN 
 4010 POKE 65023,1: GO SUB 4005: POKE 65023,100: GO SUB 4005: STOP 
 5000 SAVE "DEMO"
 5500 GO SUB 8000
 6500 PRINT AT 11,8; FLASH 1;"READY TO VERIFY "
 7000 VERIFY ""
 7500 GO SUB 8000
 7750 CLS : STOP 
 8000 SOUND 0,41;1,0;8,13;7,62: PAUSE 5: SOUND 8,0;7,63: RETURN 
 9000 READ NO: GO SUB 4002: GO TO 9000
 9010 DATA 41,41,152,27,254,231,87,36,135,77,2,10,1,2,3,4,5,6,7,8,9,10,135,63,77,2,16,162,136,135,252,2,135,25,231,74,236,125,2,48,170,171,135,63,254,16,187,148,27,200,249,27,95,56,273,89,175,174,236,273,34,222
    1 REM *** PROGRAM TO COMPILE LIBRARIES OF WORDS * 1984 ***
    2 INPUT "ENTER THE NUMBER OF LIBRARIES YOU PLAN TO TAKE WORDS FROM ";NL1
    3 DIM L(NL1)
    5 FOR V=1 TO NL1
   10 DIM A(50): DIM B(50)
   15 REM ** ROUTINE TO EDIT LIBRARY **
   20 INPUT "DO YOU WANT A LISTING OF THE LIBRARY THAT IS PRESENTLY IN MEMORY? ";S$: IF S$(1)="Y" OR S$(1)="y" THEN GO SUB 3000
   22 LET T=0
   25 LET dw=PEEK 65208*256+PEEK 65207
   30 INPUT "ENTER # OF WORD YOU WISH TO USE (ENTER N AFTER LAST WORD) ";A$
   40 IF A$="n" OR a$="N" THEN GO TO 200
   50 LET T=T+1: LET NO=VAL A$
   60 PRINT NO;" ";
   70 GO SUB 4002
   80 LET AD=USR 65150+4
   90 LET B(T)=NO: LET A(T)=AD
  100 GO TO 30
  200 FOR X=1 TO T
  210 FOR Y=X TO T
  220 IF A(X)<A(Y) THEN LET B=A(Y): LET A(Y)=A(X): LET A(X)=B: LET B=B(X): LET B(X)=B(Y): LET B(Y)=B
  230 NEXT Y: NEXT X
  240 LET T1=T: FOR X=1 TO T1
  250 IF A(X)<=DW THEN PRINT '"WORD ";B(X);" IS NOT IN LIBRARY": LET T=T-1
  260 NEXT X
  300 LET CW=65022: FOR X=1 TO T
  310 LET C1=PEEK (A(X)-2): LET C2=PEEK (A(X)-3)
  320 POKE 65197,C1: POKE 65196,C2: POKE 65200,INT (A(X)/256): POKE 65199,A(X)-(INT (A(X)/256)*256): POKE 65203,INT (CW/256): POKE 65202,CW-(INT (CW/256)*256)
  335 RANDOMIZE USR 65195
  340 LET CW=CW-4-(C1*256+C2)
  350 NEXT X
  360 LET DW=CW
  370 POKE 65208,INT (CW/256): POKE 65207,CW-(INT (CW/256)*256)
  375 IF NL1=1 THEN GO TO 450
  380 PRINT FLASH 1;'"PREPARE TO SAVE": PRINT  " CODE IS FROM ";CW;" TO 65389": PAUSE 60
  390 SAVE "NEWLIB"CODE CW,(65389-CW)
  400 GO SUB 8000
  420 LET L(V)=DW: CLS 
  435 IF V=NL1 THEN GO TO 450
  440 PRINT '"YOU MAY NOW LOAD ANOTHER LIBRARY": LOAD ""CODE : GO SUB 8000: NEXT V
  450 IF NL1=1 THEN GO TO 490
  455 PRINT '" READY TO COMBINE-LOAD PREVIOUS LIBRARIES IN ORDER"
  460 FOR Z=1 TO NL1-1: LET W=DW-(65389-L(Z)): LET W1=W+367: LOAD ""CODE W,65389-L(Z)
  465 LET DA=DW-367: LET Q1=65022-L(Z): LET C1=INT (Q1/256): LET C2=Q1-INT (Q1/256)*256
  470 POKE 65197,C1: POKE 65196,C2: POKE 65200,INT (DA/256): POKE 65199,DA-(INT (DA/256)*256): POKE 65203,INT (DW/256): POKE 65202,DW-(INT (DW/256)*256): RANDOMIZE USR 65195
  480 POKE 65208,INT ((DW-Q1)/256): POKE 65207,(DW-Q1)-(INT ((DW-Q1)/256)*256)
  482 LET DW=PEEK 65208*256+PEEK 65207
  485 NEXT Z
  490 GO SUB 3000: PRINT '"READY TO SAVE"
  500 SAVE "NEWLIB"CODE dw,65389-dw
  510 STOP 
 3000 REM **ROUTINE TO DISPLAY LIBRARY**
 3001 PRINT 
 3005 LET WA=65022
 3010 LET NO=PEEK WA*256+PEEK (WA-1)
 3020 LET NL=PEEK (WA-2)*256+PEEK (WA-3)
 3030 PRINT NO;" ";
 3040 GO SUB 4002
 3050 LET WA=WA-4-NL
 3060 IF WA>DW THEN GO TO 3010
 3065 PRINT 
 3070 RETURN 
 4000 INPUT "enter # TO be spoken";no
 4002 POKE 65024,INT (no/256): POKE 65023,no-(INT (no/256)*256)
 4003 LET dw=PEEK 65208*256+PEEK 65207: POKE dw,INT (no/256): POKE dw-1,no-(INT (no/256)*256): POKE dw-2,0: POKE dw-3,1
 4005 RANDOMIZE USR 65077: RETURN 
 4010 POKE 65023,1: GO SUB 4005: POKE 65023,100: GO SUB 4005: STOP 
 5000 SAVE "compile"
 5500 GO SUB 8000
 6500 PRINT AT 11,8; FLASH 1;"READY TO VERIFY "
 7000 VERIFY ""
 7500 GO SUB 8000
 7750 CLS : STOP 
 8000 SOUND 0,41;1,0;8,13;7,62: PAUSE 5: SOUND 8,0;7,63: RETURN 
 9000 READ NO: GO SUB 4002: GO TO 9000
 9010 DATA 1,134,70,9
 2000 INPUT "ENTER SENTENCE TO BE SPOKEN ";S$
 2010 LET S$=S$+" ": LET B=1: FOR X=1 TO LEN S$
 2020 IF S$(X TO X)=" " THEN LET NO=VAL S$(B TO X-1): GO SUB 4002: LET B=X
 2050 NEXT X
 2060 RETURN 
 3000 REM **ROUTINE TO DISPLAY LIBRARY**
 3005 LET WA=65022
 3010 LET NO=PEEK WA*256+PEEK (WA-1)
 3020 LET NL=PEEK (WA-2)*256+PEEK (WA-3)
 3030 PRINT NO;" ";
 3040 GO SUB 4002
 3050 LET WA=WA-4-NL
 3060 IF WA>DW THEN GO TO 3010
 3065 PRINT 
 3070 RETURN 
 3999 INPUT "enter # TO be spoken";no
 4000 REM ** ROUTINE TO SPEAK-LET NO=DESIRED WORD **
 4002 POKE 65024,INT (no/256): POKE 65023,no-(INT (no/256)*256)
 4003 LET dw=PEEK 65208*256+PEEK 65207: POKE dw,INT (no/256): POKE dw-1,no-(INT (no/256)*256): POKE dw-2,0: POKE dw-3,1
 4005 RANDOMIZE USR 65077: RETURN 
 
 2000 INPUT "ENTER SENTENCE TO BE SPOKEN ";S$
 2010 LET S$=S$+" ": LET B=1: FOR X=1 TO LEN S$
 2020 IF S$(X TO X)=" " THEN LET NO=VAL S$(B TO X-1): GO SUB 4002: LET B=X
 2050 NEXT X
 2060 RETURN 
 3000 REM **ROUTINE TO DISPLAY LIBRARY**
 3005 LET WA=65022
 3010 LET NO=PEEK WA*256+PEEK (WA-1)
 3020 LET NL=PEEK (WA-2)*256+PEEK (WA-3)
 3030 PRINT NO;" ";
 3040 GO SUB 4002
 3050 LET WA=WA-4-NL
 3060 IF WA>DW THEN GO TO 3010
 3065 PRINT 
 3070 RETURN 
 3999 INPUT "enter # TO be spoken";no
 4000 REM ** ROUTINE TO SPEAK-LET NO=DESIRED WORD **
 4002 POKE 65024,INT (no/256): POKE 65023,no-(INT (no/256)*256)
 4003 LET dw=PEEK 65208*256+PEEK 65207: POKE dw,INT (no/256): POKE dw-1,no-(INT (no/256)*256): POKE dw-2,0: POKE dw-3,1
 4005 RANDOMIZE USR 65077: RETURN 
 
    1 REM *** PROGRAM TO COMPILE LIBRARIES OF WORDS * 1984 ***
    2 INPUT "ENTER THE NUMBER OF LIBRARIES YOU PLAN TO TAKE WORDS FROM ";NL1
    3 DIM L(NL1)
    5 FOR V=1 TO NL1
   10 DIM A(50): DIM B(50)
   15 REM ** ROUTINE TO EDIT LIBRARY **
   20 INPUT "DO YOU WANT A LISTING OF THE LIBRARY THAT IS PRESENTLY IN MEMORY? ";S$: IF S$(1)="Y" OR S$(1)="y" THEN GO SUB 3000
   22 LET T=0
   25 LET dw=PEEK 65208*256+PEEK 65207
   30 INPUT "ENTER # OF WORD YOU WISH TO USE (ENTER N AFTER LAST WORD) ";A$
   40 IF A$="n" OR a$="N" THEN GO TO 200
   50 LET T=T+1: LET NO=VAL A$
   60 PRINT NO;" ";
   70 GO SUB 4002
   80 LET AD=USR 65150+4
   90 LET B(T)=NO: LET A(T)=AD
  100 GO TO 30
  200 FOR X=1 TO T
  210 FOR Y=X TO T
  220 IF A(X)<A(Y) THEN LET B=A(Y): LET A(Y)=A(X): LET A(X)=B: LET B=B(X): LET B(X)=B(Y): LET B(Y)=B
  230 NEXT Y: NEXT X
  240 LET T1=T: FOR X=1 TO T1
  250 IF A(X)<=DW THEN PRINT '"WORD ";B(X);" IS NOT IN LIBRARY": LET T=T-1
  260 NEXT X
  300 LET CW=65022: FOR X=1 TO T
  310 LET C1=PEEK (A(X)-2): LET C2=PEEK (A(X)-3)
  320 POKE 65197,C1: POKE 65196,C2: POKE 65200,INT (A(X)/256): POKE 65199,A(X)-(INT (A(X)/256)*256): POKE 65203,INT (CW/256): POKE 65202,CW-(INT (CW/256)*256)
  335 RANDOMIZE USR 65195
  340 LET CW=CW-4-(C1*256+C2)
  350 NEXT X
  360 LET DW=CW
  370 POKE 65208,INT (CW/256): POKE 65207,CW-(INT (CW/256)*256)
  375 IF NL1=1 THEN GO TO 450
  380 PRINT FLASH 1;'"PREPARE TO SAVE": PRINT  " CODE IS FROM ";CW;" TO 65389": PAUSE 60
  390 SAVE "NEWLIB"CODE CW,(65389-CW)
  400 GO SUB 8000
  420 LET L(V)=DW: CLS 
  435 IF V=NL1 THEN GO TO 450
  440 PRINT '"YOU MAY NOW LOAD ANOTHER LIBRARY": LOAD ""CODE : GO SUB 8000: NEXT V
  450 IF NL1=1 THEN GO TO 490
  455 PRINT '" READY TO COMBINE-LOAD PREVIOUS LIBRARIES IN ORDER"
  460 FOR Z=1 TO NL1-1: LET W=DW-(65389-L(Z)): LET W1=W+367: LOAD ""CODE W,65389-L(Z)
  465 LET DA=DW-367: LET Q1=65022-L(Z): LET C1=INT (Q1/256): LET C2=Q1-INT (Q1/256)*256
  470 POKE 65197,C1: POKE 65196,C2: POKE 65200,INT (DA/256): POKE 65199,DA-(INT (DA/256)*256): POKE 65203,INT (DW/256): POKE 65202,DW-(INT (DW/256)*256): RANDOMIZE USR 65195
  480 POKE 65208,INT ((DW-Q1)/256): POKE 65207,(DW-Q1)-(INT ((DW-Q1)/256)*256)
  482 LET DW=PEEK 65208*256+PEEK 65207
  485 NEXT Z
  490 GO SUB 3000: PRINT '"READY TO SAVE"
  500 SAVE "NEWLIB"CODE dw,65389-dw
  510 STOP 
 3000 REM **ROUTINE TO DISPLAY LIBRARY**
 3001 PRINT 
 3005 LET WA=65022
 3010 LET NO=PEEK WA*256+PEEK (WA-1)
 3020 LET NL=PEEK (WA-2)*256+PEEK (WA-3)
 3030 PRINT NO;" ";
 3040 GO SUB 4002
 3050 LET WA=WA-4-NL
 3060 IF WA>DW THEN GO TO 3010
 3065 PRINT 
 3070 RETURN 
 4000 INPUT "enter # TO be spoken";no
 4002 POKE 65024,INT (no/256): POKE 65023,no-(INT (no/256)*256)
 4003 LET dw=PEEK 65208*256+PEEK 65207: POKE dw,INT (no/256): POKE dw-1,no-(INT (no/256)*256): POKE dw-2,0: POKE dw-3,1
 4005 RANDOMIZE USR 65077: RETURN 
 4010 POKE 65023,1: GO SUB 4005: POKE 65023,100: GO SUB 4005: STOP 
 5000 SAVE "compile"
 5500 GO SUB 8000
 6500 PRINT AT 11,8; FLASH 1;"READY TO VERIFY "
 7000 VERIFY ""
 7500 GO SUB 8000
 7750 CLS : STOP 
 8000 SOUND 0,41;1,0;8,13;7,62: PAUSE 5: SOUND 8,0;7,63: RETURN 
 9000 READ NO: GO SUB 4002: GO TO 9000
 9010 DATA 1,134,70,9

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

People

No people associated with this content.

Scroll to Top