'
'              ÜÜÛßßÜÜ           ÜÜÜÜßßÜÜÜ
'          ÜÜÜÛ       ßßßßßßßßßßßßß       ÛÜ
'       Ûßß                                 ßßÛ
'     ßÛÜßßßßÜÜÜÜÜÜÜÜ  H V S S  ÜÜÜÜÜÜÜÜÜßßßßßÛß
'        ßÛ                                  Üß
'          ßÛ   HUMAN VOICE SPEECH SYSTEM  Üßß
'            ßßßßßÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜßßßßßßßß
'
'           Copyright (C) 2001 Sebastian Mate
'
'          ### Real-Time TSS-player source ###
'
'
' Please read the documentation for information!
' You have to agree with the terms and conditions in it!
'
' This source is free for non-commercial use. Please give me credits!
'
' Have fun! Sebastian Mate

REM $STATIC
DECLARE SUB DoPlayIt ()
DECLARE SUB InitSnd ()
DECLARE SUB DMAPlay (Segment&, Offset&, Length&, Freq&)
DECLARE FUNCTION Number2Text$ (IN$)
DECLARE SUB WriteDSP (byte%)

COMMON SHARED BasePort%, LenPort%, Channel%, IRQ%, DMA%
DIM SHARED WavBuffer AS STRING * 22050 ' Our wave-buffer for the DMA-player
DIM SHARED WaveBuffer1 AS STRING       ' Temporairy wave buffer for work
DIM SHARED ToDelay!, Zeit!, PC%

ON ERROR GOTO Fehler:    ' "Fehler" is German and means "error".

IF COMMAND$ = "" THEN
 PRINT "HVSS 1.00: Real-Time Text-To-Speech player for Sound-Blaster."
 PRINT ""
 PRINT "USAGE: READ FILE.TXT"
 END
END IF

' Read SpeedUp!-Variable. The bigger the value, the faster the voice.
OPEN "READ.INI" FOR INPUT AS #1
 INPUT #1, SpeedUp!
CLOSE

InitSnd

NewInput:

CLS : COLOR 15, 1: PRINT " HVSS - HUMAN VOICE SPEECH SYSTEM - 1.00 (C) 2001 Sebastian Mate                ": COLOR 7, 0: PRINT
OPEN COMMAND$ FOR INPUT AS #1
Zeit! = TIMER

ERRC$ = "ERROR: Change the directory to the HVSS base directory!"
OPEN "HVSS.INI" FOR INPUT AS #10
 INPUT #10, ISCONFIG%
CLOSE 10
IF ISCONFIG% = 0 THEN PRINT "ERROR: Run CONFIG.EXE before you use this program!": END
ERRC$ = ""

DO

  Out$ = ""
  DO

NewWord:

  WRD$ = ""

  DO

   DO

' =============================== WORD-IN ==================================

     ' This part of the program reads the text-file and gets the words. If
     ' required, this part will convert a number to text with the
     ' Number2Text function. Can be compared eyes that read a text.

     IF NUMBER% = 0 THEN A$ = INPUT$(1, #1)

     IF NUMBER% = 1 THEN
      IF NOT EOF(4) THEN A$ = INPUT$(1, #4)
      IF EOF(4) THEN CLOSE #4: NUMBER% = 0
     END IF
     
     IF ASC(A$) >= 48 AND ASC(A$) <= 57 THEN
      NUM$ = A$
      PRINT NUM$;
      DO
        A$ = INPUT$(1, #1)
        PRINT A$;
        IF ASC(A$) >= 48 AND ASC(A$) <= 57 OR A$ = "." OR A$ = "," THEN NUM$ = NUM$ + A$
      LOOP WHILE ASC(A$) >= 48 AND ASC(A$) <= 57 OR A$ = "." OR A$ = ","
      OPEN "TEMP2.TXT" FOR OUTPUT AS #4
      PRINT #4, Number2Text$(NUM$)
      CLOSE #4: OPEN "TEMP2.TXT" FOR INPUT AS #4: NUMBER% = 1
     END IF
     
     IF ASC(A$) <> 10 AND NUMBER% = 0 THEN PRINT A$;
     IF ASC(A$) >= 65 AND ASC(A$) < 122 OR A$ = "!" OR A$ = "?" OR A$ = "," OR A$ = "." OR A$ = ";" THEN
      IF ASC(A$) < 91 OR ASC(A$) > 96 THEN WRD$ = WRD$ + A$
     END IF
   LOOP UNTIL ASC(A$) < 65 OR ASC(A$) > 122
   LAST$ = A$

  WRD$ = LTRIM$(RTRIM$(WRD$))

  LOOP UNTIL WRD$ <> ""
  WRD$ = UCASE$(WRD$)

  ' Now we have a word (WRD$) !!!

' =============================== WORD-TO-PHONEMS ===========================

  ' This routine finds out how to speak the word, that means it converts
  ' the word to its phonems by a database look-up (here it uses the CMU-
  ' phonetic dictionairy, see README).
  
'((
  IF LEN(WRD$) = 1 AND WRD$ = "." OR WRD$ = "!" OR WRD$ = "?" OR WRD$ = "," OR WRD$ = ";" THEN GOTO NewWord:
  ENDING$ = MID$(WRD$, LEN(WRD$), 1)

  IF ENDING$ = "!" OR ENDING$ = ":" OR ENDING$ = "?" OR ENDING$ = "," OR ENDING$ = "." OR ENDING$ = ";" THEN
   WRD$ = MID$(WRD$, 1, LEN(WRD$) - 1)
  ELSE
   ENDING$ = ""
  END IF                                      ' )) Currently not supported because of DSP


'  #####################################################################
'  #                                                                   #
'  #  PLEASE VISIT http://www.sm-gimi.de FOR UPDATES AND OTHER COOL    #
'  #    SOFTWARE FOR QBASIC. REMOVE THIS "ADVERTISING" TO COMPILE      #
'  #       THE SOURCE CODE. THANK YOU. YOUR SEBASTIAN MATE.            #
'  #                                                                   #
'  #####################################################################
 

  ' Open the correct DAT-file which contains the word...
  IF LEN(WRD$) > 1 THEN OPEN "DICT\" + MID$(WRD$, 1, 1) + "\" + MID$(WRD$, 2, 1) + ".DAT" FOR INPUT AS #2
  IF LEN(WRD$) = 1 THEN OPEN "DICT\" + MID$(WRD$, 1, 1) + ".DAT" FOR INPUT AS #2

    '... and search it.
    DO
     IF NOT EOF(2) THEN LINE INPUT #2, B$
    LOOP UNTIL MID$(B$, 1, LEN(WRD$)) = WRD$ OR EOF(2)
   
    IF NOT EOF(2) THEN Out$ = B$ ' Not EOF: word found in database!

  CLOSE #2


  ' Dirty unknown-word-handler:
  ' Replaces the unknown word with one or more known words. I will change
  ' this dirty IF-thingy somewhere in the next versions.
  IF Out$ = "" THEN
   CHNG$ = ""
   IF WRD$ = "HVSS" THEN CHNG$ = "H V S S"
   IF WRD$ = "TTS" THEN CHNG$ = "T T S"
   IF WRD$ = "GIMI" THEN CHNG$ = "GIMME"
   IF WRD$ = "QBASIC" THEN CHNG$ = "Q BASIC"
   IF WRD$ = "QUICKBASIC" THEN CHNG$ = "QUICK BASIC"
   IF WRD$ = "README" THEN CHNG$ = "READ ME"
   IF WRD$ = "FREEWARE" THEN CHNG$ = "FREE WARE"
   IF WRD$ = "INTERFACED" THEN CHNG$ = "INTER FACE"
   IF CHNG$ <> "" THEN
    OPEN "TEMP2.TXT" FOR OUTPUT AS #4
    PRINT #4, CHNG$
    CLOSE #4: OPEN "TEMP2.TXT" FOR INPUT AS #4: NUMBER% = 1
    ' This uses the NUMBER%-variable because it works like the number
    ' handler.
   END IF
  END IF

 LOOP UNTIL Out$ <> ""
 
 ' And here's the phonetic string with all phonems of our word:
 Vocal$ = MID$(Out$, LEN(WRD$) + 3, LEN(Out$) - LEN(WRD$) - 1)

' =============================== PHONEMS-TO-WAVE ===========================
  ' The rest of the program just plays the phonems as described in the
  ' Vocal$-varible.

 S% = 1
 DO

   'Get next phonem-name (which is also the name of the wave-file):
   PHONEM$ = ""
   DO
    A$ = MID$(Vocal$, S%, 1)
    S% = S% + 1
    IF A$ <> "1" AND A$ <> "2" AND A$ <> "0" AND A$ <> " " THEN PHONEM$ = PHONEM$ + A$
   LOOP UNTIL A$ = "1" OR A$ = "2" OR A$ = "0" OR A$ = " " OR S% > LEN(Vocal$)

   ' Is there any stress on that sound?
   STRESS$ = "": IF A$ = "1" THEN STRESS$ = "1"
   IF A$ = "2" THEN STRESS$ = "2"
 
   IF PHONEM$ <> "" THEN
 
     IF AP% = 1 THEN ' For word's like "It's", "don't", "doesn't", etc.
      IF UCASE$(WRD$) = "T" THEN PHONEM$ = "T"
      IF UCASE$(WRD$) = "S" THEN PHONEM$ = "S"
     END IF
   
     ' Open the wave file, ...
     OPEN "WAVE\" + STRESS$ + PHONEM$ + ".WAV" FOR BINARY AS #3
   
     ' ... get the part of it we want (SPEEDUP!), ...
     SIZE% = LOF(3) - 44
     REMSPDUP% = (SIZE% / 3)
     SIZE% = SIZE% - REMSPDUP%
     temp$ = INPUT$(44 + REMSPDUP%, #3)
     
     DO
     
       ' ... add it to our "work-buffer" if there's enouth free space in it ...
       IF LEN(WaveBuffer1) + SIZE% <= 22050 THEN
        IF NOT EOF(3) THEN IN$ = INPUT$(SIZE%, #3)
        WaveBuffer1$ = WaveBuffer1$ + IN$
       END IF

       ' ... or play it (the buffer) if not.
       IF LEN(WaveBuffer1) + SIZE% > 22050 THEN DoPlayIt
  
     LOOP UNTIL EOF(3)
  
   END IF
  
N:

    CLOSE #3
    IF AP% = 1 THEN AP% = 0: GOTO NewWord:


 LOOP UNTIL S% > LEN(Vocal$) ' Next word

 
 IF LAST$ = CHR$(39) THEN AP% = 1
 
 ' IF ENDING$ <> "" THEN    ' Delay on .,;:!?  -  Does not work due to the DSP-system!
 '  DOPLAYIT
 '  Z! = TIMER
 '  DO: LOOP UNTIL Z! + .5 < TIMER
 ' END IF

 IF INKEY$ <> "" THEN
  OUT BasePort% + 4, &H22 'Set Volume
  OUT BasePort% + 5, 0 AND &HFF
  END
 END IF

LOOP WHILE NOT EOF(1)

Fehler:
RESUME Fehler2:
Fehler2:

PC% = 0: DO: DoPlayIt: LOOP UNTIL PC% = 1
DO: LOOP WHILE Zeit! + ToDelay! - .05 > TIMER

OUT BasePort% + 4, &H22 'Set Volume
OUT BasePort% + 5, 0 AND &HFF

PRINT ERRC$;
PRINT
PRINT "Copyright (C) 2001 Sebastian Mate"
END

SUB DMAPlay (Segment&, Offset&, Length&, Freq&)
' DMA-Player based on Mike Huff!
' Transfers and plays the contents of the buffer.
Length& = Length& - 1
Page% = 0
MemLoc& = Segment& * 16 + Offset&
SELECT CASE Channel%
    CASE 0
       PgPort% = &H87
       AddPort% = &H0
       LenPort% = &H1
       ModeReg% = &H48
    CASE 1
       PgPort% = &H83
       AddPort% = &H2
       LenPort% = &H3
       ModeReg% = &H49
    CASE 2
       PgPort% = &H81
       AddPort% = &H4
       LenPort% = &H5
       ModeReg% = &H4A
    CASE 3
       PgPort% = &H82
       AddPort% = &H6
       LenPort% = &H7
       ModeReg% = &H4B
    CASE ELSE
       PRINT "DMA channels 0-3 only are supported."
       EXIT SUB
END SELECT

OUT &HA, &H4 + Channel%
OUT &HC, &H0
OUT &HB, ModeReg%
OUT AddPort%, MemLoc& AND &HFF
OUT AddPort%, (MemLoc& AND &HFFFF&) \ &H100
IF (MemLoc& AND 65536) THEN Page% = Page% + 1
IF (MemLoc& AND 131072) THEN Page% = Page% + 2
IF (MemLoc& AND 262144) THEN Page% = Page% + 4
IF (MemLoc& AND 524288) THEN Page% = Page% + 8
OUT PgPort%, Page%
OUT LenPort%, Length& AND &HFF
OUT LenPort%, (Length& AND &HFFFF&) \ &H100
OUT &HA, Channel%

IF Freq& < 23000 THEN
   TimeConst% = 256 - 1000000 \ Freq&
   WriteDSP &H40
   WriteDSP TimeConst%
   WriteDSP &H14
   WriteDSP (Length& AND &HFF)
   WriteDSP ((Length& AND &HFFFF&) \ &H100)
ELSE

   WriteDSP &HE1
   DO: LOOP UNTIL INP(BasePort% + 14) AND &H80
   ReadDSP% = INP(BasePort% + 10)

   temp% = ReadDSP%
   Temp2% = ReadDSP%
   DSPVersion! = VAL(STR$(temp%) + "." + STR$(Temp2%))

   IF DSPVersion! >= 3 THEN
      TimeConst% = ((65536 - 256000000 \ Freq&) AND &HFFFF&) \ &H100
      WriteDSP &H40
      WriteDSP TimeConst%
      WriteDSP (Length& AND &HFF)
      WriteDSP ((Length& AND &HFFFF&) \ &H100)
      WriteDSP &H91
   ELSE
      PRINT "You need a Sound Blaster with a DSP v3.x+ to play at high speed."
      EXIT SUB
   END IF
END IF
END SUB

SUB DoPlayIt ' Plays the wave data if required

    
    ' Exit if our "old" wave has not yet finished playing:
    IF Zeit! + ToDelay! - .05 > TIMER THEN EXIT SUB
    PC% = 1
    
    'Reset Sound:
    InitSnd

    WavBuffer = WaveBuffer1' Put our temporairy created wave to the DMA-
                           ' buffer.

    Freq& = 22050
    DMAPlay VARSEG(WavBuffer), VARPTR(WavBuffer), Length&, Freq&

    ToDelay! = LEN(WaveBuffer1) / Freq& ' Set how long it will play
    Zeit! = TIMER
    
    WaveBuffer1 = "" ' ... and make spave for the "new" temporairy buffer our
                     ' program uses for the next wave data while the old
                     ' one is still playing!
    
END SUB

SUB InitSnd
' Initialize Sound Blaster

' Get Sound Blaster Information
FOR Length% = 1 TO LEN(ENVIRON$("BLASTER"))
   SELECT CASE MID$(ENVIRON$("BLASTER"), Length%, 1)
      CASE "A"
        BasePort% = VAL("&H" + MID$(ENVIRON$("BLASTER"), Length% + 1, 3))
      CASE "I"
        IRQ% = VAL(MID$(ENVIRON$("BLASTER"), Length% + 1, 1))
      CASE "D"
        DMA% = VAL(MID$(ENVIRON$("BLASTER"), Length% + 1, 1))
        Channel% = DMA%
   END SELECT
NEXT

' Resets the DSP
OUT BasePort% + 6, 1
FOR Count% = 1 TO 4
   junk% = INP(BasePort% + 6)
NEXT
OUT BasePort% + 6, 0

WriteDSP &HD1 ' Turn Speaker on

' Set Volume:
OUT BasePort% + 4, &H22
OUT BasePort% + 5, (13 + 13 * 16) AND &HFF

END SUB

FUNCTION Number2Text$ (IN$)
' Fast "number-to-text" converter. Converts f.e. the string "239" to
' "TWO HUNDRED THIRTY NINE" to make it "readable"...

INNUMBER$ = IN$
orgnumber$ = "": DOT% = 0

FOR R% = 1 TO LEN(INNUMBER$)
 SCAN% = R%
 IF ASC(MID$(INNUMBER$, R%, 1)) >= 48 AND ASC(MID$(INNUMBER$, R%, 1)) <= 57 THEN orgnumber$ = orgnumber$ + MID$(INNUMBER$, R%, 1)
 IF MID$(INNUMBER$, R%, 1) = "." AND R% < LEN(INNUMBER$) THEN DOT% = 1: GOTO RESOLVE:
NEXT R%

RESOLVE:


O$ = ""
IF orgnumber$ = "0" THEN O$ = "ZERO "

DO

 V$ = ""
 NUMBER$ = orgnumber$

 IF LEN(orgnumber$) <= 3 THEN orgnumber$ = ""

 IF LEN(orgnumber$) = 4 THEN NUMBER$ = MID$(orgnumber$, 1, 1): V$ = "THOUSAND ": orgnumber$ = MID$(orgnumber$, 2, LEN(orgnumber$) - 1)
 IF LEN(orgnumber$) = 5 THEN NUMBER$ = MID$(orgnumber$, 1, 2): V$ = "THOUSAND ": orgnumber$ = MID$(orgnumber$, 3, LEN(orgnumber$) - 2)
 IF LEN(orgnumber$) = 6 THEN NUMBER$ = MID$(orgnumber$, 1, 3): V$ = "THOUSAND ": orgnumber$ = MID$(orgnumber$, 4, LEN(orgnumber$) - 3)

 IF LEN(orgnumber$) = 7 THEN NUMBER$ = MID$(orgnumber$, 1, 1): V$ = "MILLION ": orgnumber$ = MID$(orgnumber$, 2, LEN(orgnumber$) - 1)
 IF LEN(orgnumber$) = 8 THEN NUMBER$ = MID$(orgnumber$, 1, 2): V$ = "MILLION ": orgnumber$ = MID$(orgnumber$, 3, LEN(orgnumber$) - 2)
 IF LEN(orgnumber$) = 9 THEN NUMBER$ = MID$(orgnumber$, 1, 3): V$ = "MILLION ": orgnumber$ = MID$(orgnumber$, 4, LEN(orgnumber$) - 3)

 IF LEN(orgnumber$) = 10 THEN NUMBER$ = MID$(orgnumber$, 1, 1): V$ = "BILLION ": orgnumber$ = MID$(orgnumber$, 2, LEN(orgnumber$) - 1)
 IF LEN(orgnumber$) = 11 THEN NUMBER$ = MID$(orgnumber$, 1, 2): V$ = "BILLION ": orgnumber$ = MID$(orgnumber$, 3, LEN(orgnumber$) - 2)
 IF LEN(orgnumber$) = 12 THEN NUMBER$ = MID$(orgnumber$, 1, 3): V$ = "BILLION ": orgnumber$ = MID$(orgnumber$, 4, LEN(orgnumber$) - 3)

 IF LEN(orgnumber$) = 13 THEN NUMBER$ = MID$(orgnumber$, 1, 1): V$ = "TRILLION ": orgnumber$ = MID$(orgnumber$, 2, LEN(orgnumber$) - 1)
 IF LEN(orgnumber$) = 14 THEN NUMBER$ = MID$(orgnumber$, 1, 2): V$ = "TRILLION ": orgnumber$ = MID$(orgnumber$, 3, LEN(orgnumber$) - 2)
 IF LEN(orgnumber$) = 15 THEN NUMBER$ = MID$(orgnumber$, 1, 3): V$ = "TRILLION ": orgnumber$ = MID$(orgnumber$, 4, LEN(orgnumber$) - 3)

 DO

   IF LEN(NUMBER$) MOD 3 = 0 THEN
      D$ = MID$(NUMBER$, 1, 1)

      IF D$ = "1" THEN O$ = O$ + "ONE "
      IF D$ = "2" THEN O$ = O$ + "TWO "
      IF D$ = "3" THEN O$ = O$ + "THREE "
      IF D$ = "4" THEN O$ = O$ + "FOUR "
      IF D$ = "5" THEN O$ = O$ + "FIVE "
      IF D$ = "6" THEN O$ = O$ + "SIX "
      IF D$ = "7" THEN O$ = O$ + "SEVEN "
      IF D$ = "8" THEN O$ = O$ + "EIGHT "
      IF D$ = "9" THEN O$ = O$ + "NINE "
      IF D$ <> "0" THEN O$ = O$ + "HUNDRED "
      NUMBER$ = MID$(NUMBER$, 2, LEN(NUMBER$) - 1)

   END IF

   IF LEN(NUMBER$) MOD 2 = 0 THEN
      D$ = MID$(NUMBER$, 1, 1)
      D2$ = MID$(NUMBER$, 2, 1)

      IF D$ = "0" THEN
       IF D2$ = "1" THEN O$ = O$ + "ONE "
       IF D2$ = "2" THEN O$ = O$ + "TWO "
       IF D2$ = "3" THEN O$ = O$ + "THREE "
       IF D2$ = "4" THEN O$ = O$ + "FOUR "
       IF D2$ = "5" THEN O$ = O$ + "FIVE "
       IF D2$ = "6" THEN O$ = O$ + "SIX "
       IF D2$ = "7" THEN O$ = O$ + "SEVEN "
       IF D2$ = "8" THEN O$ = O$ + "EIGHT "
       IF D2$ = "9" THEN O$ = O$ + "NINE "
       NUMBER$ = MID$(NUMBER$, 2, LEN(NUMBER$) - 1)
      END IF

      IF D$ = "1" THEN
       IF D2$ = "0" THEN O$ = O$ + "TEN "
       IF D2$ = "1" THEN O$ = O$ + "ELEVEN "
       IF D2$ = "2" THEN O$ = O$ + "TWELVE "
       IF D2$ = "3" THEN O$ = O$ + "THIRTEEN "
       IF D2$ = "4" THEN O$ = O$ + "FOURTEEN "
       IF D2$ = "5" THEN O$ = O$ + "FIFTEEN "
       IF D2$ = "6" THEN O$ = O$ + "SIXTEEN "
       IF D2$ = "7" THEN O$ = O$ + "SEVENTEEN "
       IF D2$ = "8" THEN O$ = O$ + "EIGHTEEN "
       IF D2$ = "9" THEN O$ = O$ + "NINETEEN "
       NUMBER$ = MID$(NUMBER$, 2, LEN(NUMBER$) - 1)

      END IF

      IF D$ = "2" THEN O$ = O$ + "TWENTY "
      IF D$ = "3" THEN O$ = O$ + "THIRTY "
      IF D$ = "4" THEN O$ = O$ + "FORTY "
      IF D$ = "5" THEN O$ = O$ + "FIFTY "
      IF D$ = "6" THEN O$ = O$ + "SIXTY "
      IF D$ = "7" THEN O$ = O$ + "SEVENTY "
      IF D$ = "8" THEN O$ = O$ + "EIGHTY "
      IF D$ = "9" THEN O$ = O$ + "NINETY "
      NUMBER$ = MID$(NUMBER$, 2, LEN(NUMBER$) - 1)
   END IF

   IF LEN(NUMBER$) MOD 2 = 1 THEN
      D$ = MID$(NUMBER$, 1, 1)

      IF D$ = "1" THEN O$ = O$ + "ONE "
      IF D$ = "2" THEN O$ = O$ + "TWO "
      IF D$ = "3" THEN O$ = O$ + "THREE "
      IF D$ = "4" THEN O$ = O$ + "FOUR "
      IF D$ = "5" THEN O$ = O$ + "FIVE "
      IF D$ = "6" THEN O$ = O$ + "SIX "
      IF D$ = "7" THEN O$ = O$ + "SEVEN "
      IF D$ = "8" THEN O$ = O$ + "EIGHT "
      IF D$ = "9" THEN O$ = O$ + "NINE "

      NUMBER$ = MID$(NUMBER$, 2, LEN(NUMBER$) - 1)
   END IF

 LOOP UNTIL NUMBER$ = ""


 O$ = O$ + V$

LOOP UNTIL orgnumber$ = ""

IF DOT% = 1 THEN
 FOR N% = SCAN% TO LEN(INNUMBER$)

  D$ = MID$(INNUMBER$, N%, 1)
  IF D$ = "." THEN O$ = O$ + "DOT "
  IF D$ = "0" THEN O$ = O$ + "ZERO "
  IF D$ = "1" THEN O$ = O$ + "ONE "
  IF D$ = "2" THEN O$ = O$ + "TWO "
  IF D$ = "3" THEN O$ = O$ + "THREE "
  IF D$ = "4" THEN O$ = O$ + "FOUR "
  IF D$ = "5" THEN O$ = O$ + "FIVE "
  IF D$ = "6" THEN O$ = O$ + "SIX "
  IF D$ = "7" THEN O$ = O$ + "SEVEN "
  IF D$ = "8" THEN O$ = O$ + "EIGHT "
  IF D$ = "9" THEN O$ = O$ + "NINE "

 NEXT N%
END IF

Number2Text$ = O$

END FUNCTION

SUB WriteDSP (byte%)
' Writes a byte to the DSP
'DO
'LOOP WHILE INP(BasePort% + 12) AND &H80
OUT BasePort% + 12, byte%
END SUB

