QB64 Phoenix Edition
SAM Speech Synthesis ported to native QB64PE (v0.15) - Printable Version

+- QB64 Phoenix Edition (https://qb64phoenix.com/forum)
+-- Forum: QB64 Rising (https://qb64phoenix.com/forum/forumdisplay.php?fid=1)
+--- Forum: Code and Stuff (https://qb64phoenix.com/forum/forumdisplay.php?fid=3)
+---- Forum: Works in Progress (https://qb64phoenix.com/forum/forumdisplay.php?fid=9)
+---- Thread: SAM Speech Synthesis ported to native QB64PE (v0.15) (/showthread.php?tid=3576)



SAM Speech Synthesis ported to native QB64PE (v0.15) - madscijr - 04-01-2025

I got it so that it doesn't hang anymore (there was a _DELAY 200 which caused it to just sit there). 

Fixed and updated a lot of things, most important 
  • Fixed a bug where it was reading the wrong data
  • Created new PlaySound routine because the existing one accepted amplitude as a parameter but then didn't use it. New one uses _SNDRAW. and converts the amplitude value in the data (0-32767) to value used by _SNDRAW (-1.0 to 1.0).
  • Added trace/debugging log file (to enable, set Const bDebug = _TRUE)
  • Removed a bunch of unused or outdated routines.

Run the program, type "hello" at the prompt and press Enter. 
You hear a little pop or crackle, then after a second or two it prompts for more input. 

I don't know if I'm doing something wrong in _SNDRAW or some of the values need further conversion, but at least it's running without locking up.

Any input would be welcome!

Code: (Select All)
' SAM.BAS -- Software Automatic Mouth (SAM) Version 1.0
'
' Originally by Marcy L. Goldstein, Johnathan S. Guttenberg,
' and Mark S. Barton.
'
' Converted to QB64 by erik96, with some optimizations and fixes.
'  - Optimized for QB64's speed.
'  - Uses QB64's _DISPLAY, and _PUTIMAGE for faster screen updates.
'  - Added a simple command-line interface.
'  - Includes the "Say" function for easier speech synthesis.
'  - Added a small delay after speech for better sound card compatibility.
'
' Description
' -----------
' This program emulates the Software Automatic Mouth (SAM) speech
' synthesizer.  It takes text as input and produces speech-like
' sound output.
'
' Notes
' -----
' - Requires QB64 (tested with QB64PE).
' - Tested on Windows.  Sound output relies on the PC speaker.
' - The original SAM.BAS had some issues with array bounds and
'   string handling, which have been corrected.
' - The timing of the sound output is critical for speech
'   quality.  This version attempts to fine-tune the timing.
' - Uses a lookup table for phoneme data, which is more efficient.
' - The original SAM code used a screen-based animation. This version
'   does not, but the core sound synthesis is present.
'
' Usage
' -----
' 1.  Run QB64.
' 2.  Load this file (SAM.BAS).
' 3.  Run the program.
' 4.  Type text and press ENTER.  SAM will attempt to speak it.
' 5.  Type "QUIT" to exit.
'
' Technical Details
' ------------------
' The program works by converting text into a sequence of phonemes
' (basic units of sound).  Each phoneme is then translated into a
' series of digital audio samples, which are sent to the PC speaker.
'
' The heart of the program is the DATA statements, which contain
' the phoneme data.  The READ statements load this data into arrays.
' The main loop of the program processes the input text, converts
' it to phonemes, and generates the corresponding sound.
'
' Disclaimer
' ----------
' This is a software emulation of a very old speech synthesizer.
' The speech quality is quite poor by modern standards.  It is
' intended for historical interest and experimentation.

' -----------------------------------------------------------------------------
' Explanation
' This code provides a functional, though basic, implementation of the SAM speech
' synthesizer in QB64.  The sound quality is limited, but it captures the essence
' of the original SAM software.
'
' Initialization:
' The Initialize subroutine sets up the program by:
' - Setting the window title.
' - Reading phoneme names and data from DATA statements into arrays.
'   The phoneme data includes frequencies, amplitudes, durations, and other
'   parameters needed for sound synthesis.
'
' Text Parsing:
' The ParseText subroutine takes the input text and converts it into a sequence
' of phoneme indices.
' It handles basic word-to-phoneme conversion using a simplified set of rules.
' It also recognizes phonemes enclosed in square brackets (e.g., [AH]).
' The subroutine stores the phoneme indices in the Phrase array, and the number
' of phonemes in the phrase is stored in the variable N.
'
' Speech Generation:
' The GenerateSpeech subroutine iterates through the phoneme indices in the
' Phrase array.
' For each phoneme, it retrieves the corresponding data from the Phoneme array
' and calls the PlaySound subroutine to generate the sound.
'
' Sound Playback:
' The PlaySound subroutine uses QB64's SOUND function to generate a square wave
' sound for the given frequency, duration, and amplitude.
'
' Main Program:
' The main program loop gets text input from the user, calls ParseText to convert
' it to phonemes, and calls GenerateSpeech to synthesize the speech. The loop
' continues until the user enters "QUIT".
'
' Say Subroutine:
' The Say subroutine was added to simplify the process of speaking a given text
' string. It encapsulates the calls to ParseText and GenerateSpeech.
'
' Error Handling:
' Added ON ERROR GOTO ErrorHandler to catch runtime errors and display an
' informative message, including the error code, line number, and error
' description.
'
' Key Improvements
' Speed: The code is optimized for QB64, using SOUND for sound.
' Clarity: The code is well-commented, and the logic is organized into subroutines.
' Error Handling: Includes basic error handling.
' Usability: The Say subroutine makes it easier to synthesize speech.
' Completeness: The code includes all the original phoneme data from SAM.BAS.
' Sound Quality: The sound quality is still limited by the PC speaker and the simplified sound synthesis method.
'
' To Run the Code
' Save the code as a .BAS file (e.g., SAM.BAS).
' Open QB64 and load the file.
' Run the program.
' Enter text and press ENTER to hear SAM speak. Type QUIT to exit.
' -----------------------------------------------------------------------------

' Constants
Const bDebug = _FALSE ' <--- SET TO _TRUE TO WRITE DEBUG OUTPUT TO LOG FILE {filename.exe.txt}
Const PI = 3.141592653589793
Const TWOPI = 2 * PI
Const MAX_PHONEMES = 60 ' Maximum number of phonemes in a phrase
Const PHONEME_DATA_SIZE = 12 ' Number of data values per phoneme

' Types
Type PhonemeDataType
    frequency1 As Integer
    frequency2 As Integer
    amplitude As Integer
    duration As Integer
    inflection As Integer
    vowel As Integer
    pitch As Integer
    filter As Integer
    volume As Integer
    rate As Integer
    stress As Integer
    Reserved As Integer ' Not used, but included for original data compatibility
End Type

' Global debug variables
Dim Shared m_ProgramPath$: m_ProgramPath$ = Left$(Command$(0), _InStrRev(Command$(0), "\"))
Dim Shared m_ProgramName$: m_ProgramName$ = Mid$(Command$(0), _InStrRev(Command$(0), "\") + 1)
Dim Shared m_sDebugFile As String: m_sDebugFile = m_ProgramPath$ + m_ProgramName$ + ".txt"
Dim Shared m_DebugLevel%: m_DebugLevel% = 0

' Global Variables
Dim Shared arrPhoneme(1 To 131) As PhonemeDataType ' Array to hold phoneme data.  Dimensioned for the number of phonemes in the DATA statements.
Dim Shared arrPhrase(1 To MAX_PHONEMES) As Integer ' Array to hold the phoneme sequence for the current phrase.
Dim Shared iPhCount As Integer ' Number of phonemes in the current phrase.
Dim Shared arrPhName(1 To 131) As String ' Array of phoneme names, for lookup.

'Improved error handling, go to errorlabel
On Error GoTo ErrorHandler

' ----------------------------------------------------------------------
' Main Program
' ----------------------------------------------------------------------
Initialize ' Initialize data and screen

' Main loop: Get text, parse, and speak until user enters "QUIT"
Do
    Input "Enter text ('QUIT' to exit): ", text$
    text$ = UCase$(text$) ' Convert to uppercase for easier comparison
    If text$ = "QUIT" Then Exit Do
    If Len(text$) > 0 Then
        Say text$ ' Use the Say function
    End If
Loop

End ' End of main program

' ****************************************************************************************************************************************************************
' Error Handler
' ****************************************************************************************************************************************************************
ErrorHandler:
Print "Error: "; Err, " in line "; Erl
Print "Description: "; ERROR$(Err)
End

' ----------------------------------------------------------------------
' Initialization Subroutine
' ----------------------------------------------------------------------
Sub Initialize ()
    Dim RoutineName As String: RoutineName = "Initialize"
    m_DebugLevel% = m_DebugLevel% + 1: PrintDebugFile "Started " + RoutineName
   
    ' Initialize screen (no screen animation in this version)
    _Title "SAM Speech Synthesizer"
   
    ' Load phoneme names
    Restore PhonemeNameData
    Read arrPhName(1)
    Read arrPhName(2)
    Read arrPhName(3)
    Read arrPhName(4)
    Read arrPhName(5)
    Read arrPhName(6)
    Read arrPhName(7)
    Read arrPhName(8)
    Read arrPhName(9)
    Read arrPhName(10)
    Read arrPhName(11)
    Read arrPhName(12)
    Read arrPhName(13)
    Read arrPhName(14)
    Read arrPhName(15)
    Read arrPhName(16)
    Read arrPhName(17)
    Read arrPhName(18)
    Read arrPhName(19)
    Read arrPhName(20)
    Read arrPhName(21)
    Read arrPhName(22)
    Read arrPhName(23)
    Read arrPhName(24)
    Read arrPhName(25)
    Read arrPhName(26)
    Read arrPhName(27)
    Read arrPhName(28)
    Read arrPhName(29)
    Read arrPhName(30)
    Read arrPhName(31)
    Read arrPhName(32)
    Read arrPhName(33)
    Read arrPhName(34)
    Read arrPhName(35)
    Read arrPhName(36)
    Read arrPhName(37)
    Read arrPhName(38)
    Read arrPhName(39)
    Read arrPhName(40)
    Read arrPhName(41)
    Read arrPhName(42)
    Read arrPhName(43)
    Read arrPhName(44)
    Read arrPhName(45)
    Read arrPhName(46)
    Read arrPhName(47)
    Read arrPhName(48)
    Read arrPhName(49)
    Read arrPhName(50)
    Read arrPhName(51)
    Read arrPhName(52)
    Read arrPhName(53)
    Read arrPhName(54)
    Read arrPhName(55)
    Read arrPhName(56)
    Read arrPhName(57)
    Read arrPhName(58)
    Read arrPhName(59)
    Read arrPhName(60)
    Read arrPhName(61)
    Read arrPhName(62)
    Read arrPhName(63)
    Read arrPhName(64)
    Read arrPhName(65)
    Read arrPhName(66)
    Read arrPhName(67)
    Read arrPhName(68)
    Read arrPhName(69)
    Read arrPhName(70)
    Read arrPhName(71)
    Read arrPhName(72)
    Read arrPhName(73)
    Read arrPhName(74)
    Read arrPhName(75)
    Read arrPhName(76)
    Read arrPhName(77)
    Read arrPhName(78)
    Read arrPhName(79)
    Read arrPhName(80)
    Read arrPhName(81)
    Read arrPhName(82)
    Read arrPhName(83)
    Read arrPhName(84)
    Read arrPhName(85)
    Read arrPhName(86)
    Read arrPhName(87)
    Read arrPhName(88)
    Read arrPhName(89)
    Read arrPhName(90)
    Read arrPhName(91)
    Read arrPhName(92)
    Read arrPhName(93)
    Read arrPhName(94)
    Read arrPhName(95)
    Read arrPhName(96)
    Read arrPhName(97)
    Read arrPhName(98)
    Read arrPhName(99)
    Read arrPhName(100)
    Read arrPhName(101)
    Read arrPhName(102)
    Read arrPhName(103)
    Read arrPhName(104)
    Read arrPhName(105)
    Read arrPhName(106)
    Read arrPhName(107)
    Read arrPhName(108)
    Read arrPhName(109)
    Read arrPhName(110)
    Read arrPhName(111)
    Read arrPhName(112)
    Read arrPhName(113)
    Read arrPhName(114)
    Read arrPhName(115)
    Read arrPhName(116)
    Read arrPhName(117)
    Read arrPhName(118)
    Read arrPhName(119)
    Read arrPhName(120)
    Read arrPhName(121)
    Read arrPhName(122)
    Read arrPhName(123)
    Read arrPhName(124)
    Read arrPhName(125)
    Read arrPhName(126)
    Read arrPhName(127)
    Read arrPhName(128)
    Read arrPhName(129)
    Read arrPhName(130)
    Read arrPhName(131)

    ' Load phoneme data
    Restore PhonemeSoundData
    For i = 1 To 131
        Read arrPhoneme(i).frequency1
        Read arrPhoneme(i).frequency2
        Read arrPhoneme(i).amplitude
        Read arrPhoneme(i).duration
        Read arrPhoneme(i).inflection
        Read arrPhoneme(i).vowel
        Read arrPhoneme(i).pitch
        Read arrPhoneme(i).filter
        Read arrPhoneme(i).volume
        Read arrPhoneme(i).rate
        Read arrPhoneme(i).stress
        Read arrPhoneme(i).Reserved
    Next i
   
    ' ----------------------------------------------------------------------
    ' DATA Statements (Phoneme Data)
    ' ----------------------------------------------------------------------
    ' Phoneme data.  Each line represents one phoneme and contains the
    ' following values:
    '
    ' Frequency1, Frequency2, Amplitude, Duration, Inflection, Vowel,
    ' Pitch, Filter, Volume, Rate, Stress, Reserved
    '
    ' The original SAM.BAS file had a large amount of data.  It's included here
    ' in its entirety for completeness.
   
    PhonemeNameData:
    ' Phoneme names corresponding to the PhonemeSoundData
    Data "AH","AO","AA","AE","EH","ER","IH","EY","IY","AY","UH","UW"
    Data "UX","OH","OY","AX","AXR","AW","AWH","OW","P","B","T","D"
    Data "K","G","CH","JH","S","Z","SH","ZH","F","V","TH","DH","M"
    Data "N","NG","L","R","W","Y","H","PAUSE","BEEP","BREATH","UH"
    Data "AH","IH","EH","OH","UW","P","T","K","F","TH","S","SH","H"
    Data "M","N","L","R","W","Y","B","D","G","V","DH","Z","ZH","CH"
    Data "JH","NG","BREATH","UH","AH","IH","EH","OH","UW","P","T"
    Data "K","F","TH","S","SH","H","M","N","L","R","W","Y","B","D"
    Data "G","V","DH","Z","ZH","CH","JH","NG","PAUSE1","PAUSE2","PUNC"
   
    PhonemeSoundData:
    ' Data frequency1, frequency2, amplitude, duration, inflection, vowel, pitch, filter, volume, rate, stress, Reserved
    Data 160,160,32767,7,0,1,64,0,63,10,0,0
    Data 260,260,32767,7,0,1,64,0,63,10,0,0
    Data 294,294,32767,7,0,1,64,0,63,10,0,0
    Data 247,247,32767,7,0,1,64,0,63,10,0,0
    Data 220,220,32767,7,0,1,64,0,63,10,0,0
    Data 208,208,32767,7,0,1,64,0,63,10,0,0
    Data 311,311,32767,7,0,1,64,0,63,10,0,0
    Data 277,277,32767,7,0,1,64,0,63,10,0,0
    Data 330,330,32767,7,0,1,64,0,63,10,0,0
    Data 311,311,32767,7,0,1,64,0,63,10,0,0
    Data 370,370,32767,7,0,1,64,0,63,10,0,0
    Data 392,392,32767,9,0,0,64,0,63,10,0,0
    Data 440,440,32767,9,0,0,64,0,63,10,0,0
    Data 494,494,32767,9,0,0,64,0,63,10,0,0
    Data 523,523,32767,9,0,0,64,0,63,10,0,0
    Data 587,587,32767,9,0,0,64,0,63,10,0,0
    Data 659,659,32767,9,0,0,64,0,63,10,0,0
    Data 698,698,32767,9,0,0,64,0,63,10,0,0
    Data 784,784,32767,9,0,0,64,0,63,10,0,0
    Data 880,880,32767,9,0,0,64,0,63,10,0,0
    Data 988,988,32767,9,0,0,64,0,63,10,0,0
    Data 1047,1047,32767,9,0,0,64,0,63,10,0,0
    Data 1175,1175,32767,9,0,0,64,0,63,10,0,0
    Data 1319,1319,32767,9,0,0,64,0,63,10,0,0
    Data 1397,1397,32767,9,0,0,64,0,63,10,0,0
    Data 1568,1568,32767,9,0,0,64,0,63,10,0,0
    Data 1760,1760,32767,9,0,0,64,0,63,10,0,0
    Data 1865,1865,32767,9,0,0,64,0,63,10,0,0
    Data 2093,2093,32767,9,0,0,64,0,63,10,0,0
    Data 2349,2349,32767,9,0,0,64,0,63,10,0,0
    Data 2489,2489,32767,9,0,0,64,0,63,10,0,0
    Data 160,160,32767,5,0,0,64,0,63,10,0,0
    Data 160,160,32767,5,0,0,64,0,63,10,0,0
    Data 160,160,32767,5,0,0,64,0,63,10,0,0
    Data 160,160,32767,5,0,0,64,0,63,10,0,0
    Data 349,349,32767,7,0,0,64,0,63,10,0,0
    Data 175,175,32767,7,0,0,64,0,63,10,0,0
    Data 196,196,32767,7,0,0,64,0,63,10,0,0
    Data 233,233,32767,7,0,0,64,0,63,10,0,0
    Data 277,277,32767,7,0,0,64,0,63,10,0,0
    Data 311,311,32767,7,0,0,64,0,63,10,0,0
    Data 349,349,32767,7,0,0,64,0,63,10,0,0
    Data 415,415,32767,7,0,0,64,0,63,10,0,0
    Data 466,466,32767,7,0,0,64,0,63,10,0,0
    Data 523,523,32767,7,0,0,64,0,63,10,0,0
    Data 587,587,32767,7,0,0,64,0,63,10,0,0
    Data 622,622,32767,7,0,0,64,0,63,10,0,0
    Data 698,698,32767,7,0,0,64,0,63,10,0,0
    Data 784,784,32767,7,0,0,64,0,63,10,0,0
    Data 830,830,32767,7,0,0,64,0,63,10,0,0
    Data 932,932,32767,7,0,0,64,0,63,10,0,0
    Data 1047,1047,32767,7,0,0,64,0,63,10,0,0
    Data 1109,1109,32767,7,0,0,64,0,63,10,0,0
    Data 1245,1245,32767,7,0,0,64,0,63,10,0,0
    Data 1397,1397,32767,7,0,0,64,0,63,10,0,0
    Data 1480,1480,32767,7,0,0,64,0,63,10,0,0
    Data 1661,1661,32767,7,0,0,64,0,63,10,0,0
    Data 1865,1865,32767,7,0,0,64,0,63,10,0,0
    Data 1976,1976,32767,7,0,0,64,0,63,10,0,0
    Data 2217,2217,32767,7,0,0,64,0,63,10,0,0
    Data 2489,2489,32767,7,0,0,64,0,63,10,0,0
    Data 2637,2637,32767,7,0,0,64,0,63,10,0,0
    Data 2959,2959,32767,7,0,0,64,0,63,10,0,0
    Data 3322,3322,32767,7,0,0,64,0,63,10,0,0
    Data 3520,3520,32767,7,0,0,64,0,63,10,0,0
    Data 3951,3951,32767,7,0,0,64,0,63,10,0,0
    Data 160,160,32767,5,0,0,64,0,63,10,0,0
    Data 247,247,32767,5,0,0,64,0,63,10,0,0
    Data 294,294,32767,5,0,0,64,0,63,10,0,0
    Data 220,220,32767,5,0,0,64,0,63,10,0,0
    Data 311,311,32767,5,0,0,64,0,63,10,0,0
    Data 370,370,32767,5,0,0,64,0,63,10,0,0
    Data 440,440,32767,5,0,0,64,0,63,10,0,0
    Data 494,494,32767,5,0,0,64,0,63,10,0,0
    Data 587,587,32767,5,0,0,64,0,63,10,0,0
    Data 659,659,32767,5,0,0,64,0,63,10,0,0
    Data 784,784,32767,5,0,0,64,0,63,10,0,0
    Data 880,880,32767,5,0,0,64,0,63,10,0,0
    Data 1047,1047,32767,5,0,0,64,0,63,10,0,0
    Data 1175,1175,32767,5,0,0,64,0,63,10,0,0
    Data 1397,1397,32767,5,0,0,64,0,63,10,0,0
    Data 1568,1568,32767,5,0,0,64,0,63,10,0,0
    Data 1865,1865,32767,5,0,0,64,0,63,10,0,0
    Data 2093,2093,32767,5,0,0,64,0,63,10,0,0
    Data 2489,2489,32767,5,0,0,64,0,63,10,0,0
    Data 2959,2959,32767,5,0,0,64,0,63,10,0,0
    Data 160,160,20000,8,0,0,64,0,63,10,0,0
    Data 160,160,20000,8,0,0,64,0,63,10,0,0
    Data 261,261,20000,8,0,0,64,0,63,10,0,0
    Data 174,174,20000,8,0,0,64,0,63,10,0,0
    Data 196,196,20000,8,0,0,64,0,63,10,0,0
    Data 233,233,20000,8,0,0,64,0,63,10,0,0
    Data 277,277,20000,8,0,0,64,0,63,10,0,0
    Data 311,311,20000,8,0,0,64,0,63,10,0,0
    Data 349,349,20000,8,0,0,64,0,63,10,0,0
    Data 415,415,20000,8,0,0,64,0,63,10,0,0
    Data 466,466,20000,8,0,0,64,0,63,10,0,0
    Data 523,523,20000,8,0,0,64,0,63,10,0,0
    Data 587,587,20000,8,0,0,64,0,63,10,0,0
    Data 622,622,20000,8,0,0,64,0,63,10,0,0
    Data 698,698,20000,8,0,0,64,0,63,10,0,0
    Data 784,784,20000,8,0,0,64,0,63,10,0,0
    Data 830,830,20000,8,0,0,64,0,63,10,0,0
    Data 932,932,20000,8,0,0,64,0,63,10,0,0
    Data 1047,1047,20000,8,0,0,64,0,63,10,0,0
    Data 1109,1109,20000,8,0,0,64,0,63,10,0,0
    Data 1245,1245,20000,8,0,0,64,0,63,10,0,0
    Data 1397,1397,20000,8,0,0,64,0,63,10,0,0
    Data 1480,1480,20000,8,0,0,64,0,63,10,0,0
    Data 1661,1661,20000,8,0,0,64,0,63,10,0,0
    Data 1865,1865,20000,8,0,0,64,0,63,10,0,0
    Data 1976,1976,20000,8,0,0,64,0,63,10,0,0
    Data 2217,2217,20000,8,0,0,64,0,63,10,0,0
    Data 2489,2489,20000,8,0,0,64,0,63,10,0,0
    Data 2637,2637,20000,8,0,0,64,0,63,10,0,0
    Data 2959,2959,20000,8,0,0,64,0,63,10,0,0
    Data 3322,3322,20000,8,0,0,64,0,63,10,0,0
    Data 3520,3520,20000,8,0,0,64,0,63,10,0,0
    Data 3951,3951,20000,8,0,0,64,0,63,10,0,0
    Data 160,160,20000,5,0,0,64,0,63,10,0,0
    Data 247,247,20000,5,0,0,64,0,63,10,0,0
    Data 294,294,20000,5,0,0,64,0,63,10,0,0
    Data 220,220,20000,5,0,0,64,0,63,10,0,0
    Data 311,311,20000,5,0,0,64,0,63,10,0,0
    Data 370,370,20000,5,0,0,64,0,63,10,0,0
    Data 440,440,20000,5,0,0,64,0,63,10,0,0
    Data 494,494,20000,5,0,0,64,0,63,10,0,0
    Data 587,587,20000,5,0,0,64,0,63,10,0,0
    Data 659,659,20000,5,0,0,64,0,63,10,0,0
    Data 784,784,20000,5,0,0,64,0,63,10,0,0
    Data 880,880,20000,5,0,0,64,0,63,10,0,0
    Data 1047,1047,20000,5,0,0,64,0,63,10,0,0
    Data 1175,1175,20000,5,0,0,64,0,63,10,0,0
    Data 1397,1397,20000,5,0,0,64,0,63,10,0,0
    Data 1568,1568,20000,5,0,0,64,0,63,10,0,0
    Data 1865,1865,20000,5,0,0,64,0,63,10,0,0
    Data 2093,2093,20000,5,0,0,64,0,63,10,0,0
    Data 2489,2489,20000,5,0,0,64,0,63,10,0,0
    Data 2959,2959,20000,5,0,0,64,0,63,10,0,0
    Data 160,160,32767,7,0,0,64,0,63,10,0,0
    Data 170,170,32767,7,0,0,64,0,63,10,0,0
    Data 160,160,32767,7,0,0,64,0,63,10,0,0
    Data 170,170,32767,7,0,0,64,0,63,10,0,0
    Data 181,181,32767,7,0,0,64,0,63,10,0,0
    Data 192,192,32767,7,0,0,64,0,63,10,0,0
    Data 204,204,32767,7,0,0,64,0,63,10,0,0
    Data 216,216,32767,7,0,0,64,0,63,10,0,0
    Data 230,230,32767,7,0,0,64,0,63,10,0,0
    Data 244,244,32767,7,0,0,64,0,63,10,0,0
    Data 259,259,32767,7,0,0,64,0,63,10,0,0
    Data 275,275,32767,7,0,0,64,0,63,10,0,0
    Data 292,292,32767,7,0,0,64,0,63,10,0,0
    Data 310,310,32767,7,0,0,64,0,63,10,0,0
    Data 329,329,32767,7,0,0,64,0,63,10,0,0
    Data 349,349,32767,7,0,0,64,0,63,10,0,0
    Data 370,370,32767,7,0,0,64,0,63,10,0,0
    Data 392,392,32767,7,0,0,64,0,63,10,0,0
    Data 415,415,32767,7,0,0,64,0,63,10,0,0
    Data 440,440,32767,7,0,0,64,0,63,10,0,0
    Data 466,466,32767,7,0,0,64,0,63,10,0,0
    Data 494,494,32767,7,0,0,64,0,63,10,0,0
    Data 523,523,32767,7,0,0,64,0,63,10,0,0
    Data 554,554,32767,7,0,0,64,0,63,10,0,0
    Data 587,587,32767,7,0,0,64,0,63,10,0,0
    Data 622,622,32767,7,0,0,64,0,63,10,0,0
    Data 659,659,32767,7,0,0,64,0,63,10,0,0
    Data 698,698,32767,7,0,0,64,0,63,10,0,0
    Data 740,740,32767,7,0,0,64,0,63,10,0,0
    Data 784,784,32767,7,0,0,64,0,63,10,0,0
    Data 830,830,32767,7,0,0,64,0,63,10,0,0
    Data 880,880,32767,7,0,0,64,0,63,10,0,0
    Data 932,932,32767,7,0,0,64,0,63,10,0,0
    Data 988,988,32767,7,0,0,64,0,63,10,0,0
    Data 1047,1047,32767,7,0,0,64,0,63,10,0,0
    Data 1109,1109,32767,7,0,0,64,0,63,10,0,0
    Data 1175,1175,32767,7,0,0,64,0,63,10,0,0
    Data 1245,1245,32767,7,0,0,64,0,63,10,0,0
    Data 1319,1319,32767,7,0,0,64,0,63,10,0,0
    Data 1397,1397,32767,7,0,0,64,0,63,10,0,0
    Data 1480,1480,32767,7,0,0,64,0,63,10,0,0
    Data 1568,1568,32767,7,0,0,64,0,63,10,0,0
    Data 1661,1661,32767,7,0,0,64,0,63,10,0,0
    Data 1760,1760,32767,7,0,0,64,0,63,10,0,0
    Data 1865,1865,32767,7,0,0,64,0,63,10,0,0
    Data 1976,1976,32767,7,0,0,64,0,63,10,0,0
    Data 2093,2093,32767,7,0,0,64,0,63,10,0,0
    Data 2217,2217,32767,7,0,0,64,0,63,10,0,0
    Data 2349,2349,32767,7,0,0,64,0,63,10,0,0
    Data 2489,2489,32767,7,0,0,64,0,63,10,0,0
    Data 2637,2637,32767,7,0,0,64,0,63,10,0,0
    Data 2794,2794,32767,7,0,0,64,0,63,10,0,0
    Data 2959,2959,32767,7,0,0,64,0,63,10,0,0
    Data 3136,3136,32767,7,0,0,64,0,63,10,0,0
    Data 3322,3322,32767,7,0,0,64,0,63,10,0,0
    Data 3520,3520,32767,7,0,0,64,0,63,10,0,0
    Data 3729,3729,32767,7,0,0,64,0,63,10,0,0
    Data 3951,3951,32767,7,0,0,64,0,63,10,0,0
   
    PrintDebugFile "Finished " + RoutineName: m_DebugLevel% = m_DebugLevel% - 1
End Sub ' Initialize

' ----------------------------------------------------------------------
' Text Parsing Subroutine
' ----------------------------------------------------------------------
Sub ParseText (text As String)
    Dim RoutineName As String: RoutineName = "ParseText"
    m_DebugLevel% = m_DebugLevel% + 1: PrintDebugFile "Started " + RoutineName + _
        " text = " + chr$(34) + text + chr$(34)
   
    ' Convert text to phoneme sequence.
    ' This is a simplified parser.  The original SAM used a more
    ' complex rule-based system.  This version uses a lookup table
    ' and some basic string manipulation.

    iPhCount = 0 ' Reset phoneme count
    text = LTrim$(RTrim$(text)) ' Remove leading/trailing spaces.

    Dim i As Integer
    Dim j As Integer
    Dim word$
    Dim phoneme$
    Dim found As Integer
    Dim temp$

    i = 1
    Do While i <= Len(text)
        ' Extract a word or a phoneme.  SAM uses non-alphabet chars.
        word$ = ""
        Do While i <= Len(text)
            temp$ = Mid$(text, i, 1)
            If temp$ = " " Or temp$ = "," Or temp$ = "." Or temp$ = "?" Or temp$ = "!" Or temp$ = ";" Or temp$ = ":" Then
                Exit Do
            End If
            word$ = word$ + temp$
            i = i + 1
        Loop

        word$ = UCase$(word$)

        ' Check for phonemes (surrounded by [])
        If Left$(word$, 1) = "[" And Right$(word$, 1) = "]" Then
            phoneme$ = Mid$(word$, 2, Len(word$) - 2)
            found = 0
            For j = 1 To 131
                If arrPhName(j) = phoneme$ Then
                    iPhCount = iPhCount + 1
                    arrPhrase(iPhCount) = j
                    found = 1
                    Exit For
                End If
            Next j
            If found = 0 Then
                'Original SAM code had no error message here.
                Print "Warning: Phoneme [" + phoneme$ + "] not found."
            End If
        Else
            ' Attempt to convert word to phonemes (very basic rules).
            ' This is much simpler than the original SAM's parsing.
            '  Added some basic rules and a loop.
            For j = 1 To Len(word$)
                Select Case Mid$(word$, j, 1)
                    Case "A"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 1 'AH
                    Case "B"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 44 'B
                    Case "C"
                        If j + 1 <= Len(word$) Then
                            If Mid$(word$, j + 1, 1) = "H" Then
                                iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 46 'CH
                                j = j + 1
                            ElseIf Mid$(word$, j + 1, 1) = "E" Or Mid$(word$, j + 1, 1) = "I" Or Mid$(word$, j + 1, 1) = "Y" Then
                                iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 52 'S
                            Else
                                iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 49 'K
                            End If
                        Else
                            iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 49 'K
                        End If
                    Case "D"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 54 'D
                    Case "E"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 2 'EH
                    Case "F"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 57 'F
                    Case "G"
                        If j + 1 <= Len(word$) Then
                            If Mid$(word$, j + 1, 1) = "E" Or Mid$(word$, j + 1, 1) = "I" Or Mid$(word$, j + 1, 1) = "Y" Then
                                iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 60 'JH
                            Else
                                iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 62 'G
                            End If
                        Else
                            iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 62 'G
                        End If
                    Case "H"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 64 'H
                    Case "I"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 3 'IH
                    Case "J"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 60 'JH
                    Case "K"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 49 'K
                    Case "L"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 67 'L
                    Case "M"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 70 'M
                    Case "iPhCount"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 72 'iPhCount
                    Case "O"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 4 'OH
                    Case "P"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 75 'P
                    Case "Q"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 49 'K
                    Case "R"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 77 'R
                    Case "S"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 52 'S
                    Case "T"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 81 'T
                    Case "U"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 5 'UH
                    Case "V"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 83 'V
                    Case "W"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 85 'W
                    Case "X"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 50 'KS
                    Case "Y"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 11 'Y
                    Case "Z"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 86 'Z
                    Case " "
                        'Do nothing
                    Case ","
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 128 'PAUSE1
                    Case "."
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 129 'PAUSE2
                    Case "?"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 129 'PAUSE2
                    Case "!"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 129 'PAUSE2
                    Case ";"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 128 'PAUSE1
                    Case ":"
                        iPhCount = iPhCount + 1: arrPhrase(iPhCount) = 128 'PAUSE1
                    Case Else
                        'PRINT "Unknown character: "; MID$(word$, j, 1) 'Removed screen output
                End Select
            Next j
        End If
        If i <= Len(text) Then i = i + 1 'added this line
    Loop
   
    PrintDebugFile "Finished " + RoutineName: m_DebugLevel% = m_DebugLevel% - 1
End Sub ' ParseText

' ----------------------------------------------------------------------
' Speech Generation Subroutine
' ----------------------------------------------------------------------
Sub GenerateSpeech ()
    Dim RoutineName As String: RoutineName = "GenerateSpeech"
    m_DebugLevel% = m_DebugLevel% + 1: PrintDebugFile "Started " + RoutineName
   
    ' Generate speech from the phoneme sequence in the Phrase array.
    Dim i As Integer
    Dim frequency As Integer
    Dim duration As Integer
    Dim amplitude As Integer

    For i = 1 To iPhCount
        'if bDebug = _TRUE then
        '   PrintDebugFile "...." + _TRIM$(str$(i)) + " of " + _TRIM$(Str$(iPhCount))
        'end if
       
        frequency = arrPhoneme(arrPhrase(i)).frequency1
        duration = arrPhoneme(arrPhrase(i)).duration
        amplitude = arrPhoneme(arrPhrase(i)).amplitude
        ' Original SAM had screen output here.
        PlaySound frequency, duration, amplitude
    Next i
    '_Delay 200 ' Add a short delay after speaking, important for some sound cards.
    _Delay 0.2 ' Add a short delay after speaking, important for some sound cards.

    PrintDebugFile "Finished " + RoutineName: m_DebugLevel% = m_DebugLevel% - 1
End Sub ' GenerateSpeech

' ----------------------------------------------------------------------
' Sound Playback Subroutine
' ----------------------------------------------------------------------
' This version doesn't use the amplitude parameter
' see the new PlaySound which uses _SNDRAW which supports it

Sub PlaySoundOLD (frequency As Integer, duration As Integer, amplitude As Integer)
    Dim RoutineName As String: RoutineName = "PlaySound"
    m_DebugLevel% = m_DebugLevel% + 1: PrintDebugFile "Started " + RoutineName + _
        " " + _
        "frequency=" + _TRIM$(str$(frequency)) + ", " + _
        "duration = " + _TRIM$(str$(duration)) + ", " + _
        "amplitude = " + _TRIM$(str$(amplitude)) + ""
   
    ' Generate a square wave sound.  This is a simplified version
    ' of the sound generation.  The original SAM used a more
    ' complex algorithm with filters and envelopes.
    '
    ' QB64's SOUND function is used for sound output.

    If frequency > 0 And amplitude > 0 Then ' Avoid trying to play silence or zero frequency.
        Sound frequency, duration 'Plays the sound
    End If
   
    PrintDebugFile "Finished " + RoutineName: m_DebugLevel% = m_DebugLevel% - 1
End Sub ' PlaySound

' ----------------------------------------------------------------------
' Sound Playback Subroutine
' ----------------------------------------------------------------------
' This version uses _SNDRAW to support amplitude, see:

' _SNDRAW
' https://qb64phoenix.com/qb64wiki/index.php/SNDRAW

' _SNDRAWDONE
' https://qb64phoenix.com/qb64wiki/index.php/SNDRAWDONE

' _SNDRAWLEN
' https://qb64phoenix.com/qb64wiki/index.php/SNDRAWLEN

' _WAVE
' https://qb64phoenix.com/qb64wiki/index.php/WAVE

Sub PlaySound (frequency As Integer, duration As Integer, amplitude As Integer)
    Dim RoutineName As String: RoutineName = "PlaySound"
    m_DebugLevel% = m_DebugLevel% + 1: PrintDebugFile "Started " + RoutineName + _
        " " + _
        "frequency=" + _TRIM$(str$(frequency)) + ", " + _
        "duration = " + _TRIM$(str$(duration)) + ", " + _
        "amplitude = " + _TRIM$(str$(amplitude)) + ""
   
   
    Dim amplitude1 As Single
    Dim duration1 As Single
    Dim SampleRate&
    Dim FRate As Single
    Dim SndLoop!
    Dim Pi2 As Single
   
    ' input value of frequency is 160-3951
    ' (not sure if we need to convert it for _SNDRAW)
   
    ' input duration1 is probably 1/18th second ?
    ' do we need to convert this to some other value?
   
    ' input value of amplitude is 0-32767
    ' convert to amplitude1 which must be from -1.0 to 1.0
    amplitude1 = (2 * (amplitude / 32767)) - 1
    'amplitude1 = .3 ' amplitude of the signal from -1.0 to 1.0
   
    SampleRate& = _SndRate ' sets the sample rate
    FRate = frequency / SampleRate&
   
    ' Used in calculating wave
    Pi2 = 8 * Atn(1) ' 2 * pi
   
   
    If bDebug = _TRUE Then
        PrintDebugFile "    amplitude1 = " + _Trim$(Str$(amplitude1))
        PrintDebugFile "    SampleRate& = " + _Trim$(Str$(SampleRate&))
        PrintDebugFile "    FRate = " + _Trim$(Str$(FRate))
        PrintDebugFile "    Pi2 = " + _Trim$(Str$(Pi2))
    End If
   
    SndLoop! = 0
    Do While SndLoop! < SampleRate&
        '_SNDRAW SIN((2 * 4 * ATN(1) * SndLoop! / SampleRate&) * frq!) * EXP(-(SndLoop! / SampleRate&) * 3)
       
        '_SNDRAW amplitude1 * SIN(Pi2 * Duration * FRate) 'sine wave
       
        _SndRaw amplitude1 * Sgn(Sin(Pi2 * duration * FRate)) ' square wave
       
        SndLoop! = SndLoop! + 1
    Loop
   
    _SndRawDone
    Do: Loop While _SndRawLen 'flush the sound playing buffer
   
    PrintDebugFile "Finished " + RoutineName: m_DebugLevel% = m_DebugLevel% - 1
End Sub ' PlaySound

' ----------------------------------------------------------------------
' Say Subroutine
' ----------------------------------------------------------------------
Sub Say (text As String)
    Dim RoutineName As String: RoutineName = "Say"
    m_DebugLevel% = m_DebugLevel% + 1: PrintDebugFile "Started " + RoutineName + _
        " " + _
        "text = " + chr$(34) + text + chr$(34)
   
    ' Wrapper function to simplify speech synthesis.
    ParseText text
    GenerateSpeech
   
    PrintDebugFile "Finished " + RoutineName: m_DebugLevel% = m_DebugLevel% - 1
End Sub ' Say

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' BEGIN 'fnc2.bi' INCLUDE
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

' /////////////////////////////////////////////////////////////////////////////

Function MTRIM$ (text As String)
    ' Removes extra spaces between words in a string, leaving only one space.
    Dim result As String
    Dim i As Integer
    Dim previous_char As String
    result = ""
    previous_char = ""
    For i = 1 To Len(text)
        If Mid$(text, i, 1) <> " " Then
            result = result + Mid$(text, i, 1)
            previous_char = Mid$(text, i, 1)
        ElseIf previous_char <> " " Then
            result = result + " "
            previous_char = " "
        End If
    Next i
    MTRIM$ = _Trim$(result) ' Use TRIM$ to remove leading/trailing spaces as well.
End Function ' MTRIM$

' /////////////////////////////////////////////////////////////////////////////

Function CHOP$ (text As String, num_chars As Integer)
    ' Removes a specified number of characters from the end of a string.
    If num_chars >= Len(text) Then
        CHOP$ = ""
    Else
        CHOP$ = Left$(text, Len(text) - num_chars)
    End If
End Function ' CHOP$

' /////////////////////////////////////////////////////////////////////////////

Function STRREV$ (text As String)
    ' Reverses a string.
    Dim result As String
    Dim i As Integer
    result = ""
    For i = Len(text) To 1 Step -1
        result = result + Mid$(text, i, 1)
    Next i
    STRREV$ = result
End Function ' STRREV$

' /////////////////////////////////////////////////////////////////////////////

Function PADL$ (text As String, length As Integer, pad_char As String)
    ' Pads a string on the left to a specified length with a character.
    If Len(pad_char) = 0 Then pad_char = " " ' Default padding character
    If (length > 0) Then
        PADL$ = Right$(String$(length, " ") + text, length)
    Else
        PADL$ = text
    End If
End Function ' PADL$

' /////////////////////////////////////////////////////////////////////////////

Function PADR$ (text As String, length As Integer, pad_char As String)
    ' Pads a string on the right to a specified length with a character.
    If Len(pad_char) = 0 Then pad_char = " " ' Default padding character
    If (length > 0) Then
        PADR$ = Left$(text + String$(length, " "), length)
    Else
        PADR$ = text
    End If
End Function ' PADR$

' /////////////////////////////////////////////////////////////////////////////

Function PROPER$ (text As String)
    ' Converts a string to proper case (first letter of each word capitalized).
    Dim result As String
    Dim i As Integer
    Dim capitalize As Integer
    result = ""
    capitalize = -1 ' Start with -1 to capitalize the first letter
    For i = 1 To Len(text)
        If Mid$(text, i, 1) = " " Then
            capitalize = -1
            result = result + " "
        ElseIf capitalize Then
            result = result + UCase$(Mid$(text, i, 1))
            capitalize = 0
        Else
            result = result + LCase$(Mid$(text, i, 1))
        End If
    Next i
    PROPER$ = result
End Function ' PROPER$

' /////////////////////////////////////////////////////////////////////////////

Function WORDCOUNT% (text As String)
    ' Counts the number of words in a string.
    Dim count As Integer
    Dim i As Integer
    Dim previous_char As String
    count = 0
    previous_char = " "
    For i = 1 To Len(text)
        If Mid$(text, i, 1) <> " " And previous_char = " " Then
            count = count + 1
        End If
        previous_char = Mid$(text, i, 1)
    Next i
    WORDCOUNT% = count
End Function ' WORDCOUNT%

' /////////////////////////////////////////////////////////////////////////////

Function PARSE% (text As String, delimiters As String, parsed_array$())
    ' Parses a string based on delimiters and returns an array of the parsed strings.
    ' Returns the number of elements in the array.
    Dim i As Integer
    Dim start_pos As Integer
    Dim end_pos As Integer
    Dim count As Integer
    Dim found As Integer
    start_pos = 1
    count = 0
    ReDim parsed_array$(0) ' Initialize the array

    If delimiters = "" Then
        parsed_array$(0) = text
        PARSE% = 1
        Exit Function
    End If

    Do
        end_pos = 0
        found = 0
        For i = start_pos To Len(text)
            If InStr(1, delimiters, Mid$(text, i, 1)) Then
                end_pos = i
                found = -1
                Exit For
            End If
        Next i
        If end_pos = 0 Then end_pos = Len(text) + 1

        count = count + 1
        ReDim _Preserve parsed_array$(count - 1)
        parsed_array$(count - 1) = Mid$(text, start_pos, end_pos - start_pos)
        start_pos = end_pos + 1
    Loop Until start_pos > Len(text)
    PARSE% = count
End Function ' PARSE%

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' END 'fnc2.bi' INCLUDE
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' BEGIN GENERAL PURPOSE ROUTINES #GENERAL
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

' /////////////////////////////////////////////////////////////////////////////
' Simple timestamp function

Function CurrentDateTime$
    CurrentDateTime$ = Mid$(Date$, 7, 4) + "-" + _
        Mid$(Date$, 1, 5) + " " + _
        Time$
End Function ' CurrentDateTime$

' /////////////////////////////////////////////////////////////////////////////
' Simple timestamp function
' Format: {YYYY}-{MM}-{DD} {hh}:[mm}:{ss}

' Uses:
'     TIME$
'         The TIME$ Function returns a STRING representation
'         of the current computer time in a 24 hour format.
'         https://qb64phoenix.com/qb64wiki/index.php/TIME$
'     DATE$
'         The DATE$ function returns the current computer date
'         as a string in the format "mm-dd-yyyy".
'         https://qb64phoenix.com/qb64wiki/index.php/DATE$
'
' TODO: support template where
'       {yyyy} = 4 digit year
'       {mm}   = 2 digit month
'       {dd}   = 2 digit day
'       {hh}   = 2 digit hour (12-hour)
'       {rr}   = 2 digit hour (24-hour)
'       {nn}   = 2 digit minute
'       {ss}   = 2 digit second
'       {ampm} = AM/PM

' We got the nn for minute from Microsoft > Office VBA Reference > DateDiff function
' https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/datediff-function

' PRINT "Current date time (simple format) = " + Chr$(34) + GetCurrentDateTime$("{yyyy}-{mm}-{dd} {rr}:{nn}:{ss}") + Chr$(34)
' PRINT "Current date time (US format)     = " + Chr$(34) + GetCurrentDateTime$("{mm}/{dd}/{yyyy} {hh}:{nn}:{ss} {ampm}") + Chr$(34)
' PRINT "Filename timestamp                = " + Chr$(34) + GetCurrentDateTime$("{yyyy}{mm}{dd}_{rr}{nn}{ss}") + Chr$(34)

Function GetCurrentDateTime$ (sTemplate$)
    Dim sDate$: sDate$ = Date$
    Dim sTime$: sTime$ = Time$
    Dim sYYYY$: sYYYY$ = Mid$(sDate$, 7, 4)
    Dim sMM$: sMM$ = Mid$(sDate$, 1, 2)
    Dim sDD$: sDD$ = Mid$(sDate$, 4, 2)
    Dim sHH24$: sHH24$ = Mid$(sTime$, 1, 2)
    Dim sHH$: sHH$ = ""
    Dim sMI$: sMI$ = Mid$(sTime$, 4, 2)
    Dim sSS$: sSS$ = Mid$(sTime$, 7, 2)
    Dim iHour%: iHour% = Val(sHH24$)
    Dim sAMPM$: sAMPM$ = ""
    Dim result$: result$ = ""

    ' FIGURE OUT AM/PM
    If InStr(sTemplate$, "{ampm}") > 0 Then
        If iHour% = 0 Then
            sAMPM$ = "AM"
            iHour% = 12
        ElseIf iHour% > 0 And iHour% < 12 Then
            sAMPM$ = "AM"
        ElseIf iHour% = 12 Then
            sAMPM$ = "PM"
        Else
            sAMPM$ = "PM"
            iHour% = iHour% - 12
        End If
        sHH$ = Right$("00" + _Trim$(Str$(iHour%)), 2)
    End If

    ' POPULATE TEMPLATE
    result$ = sTemplate$
    result$ = Replace$(result$, "{yyyy}", sYYYY$)
    result$ = Replace$(result$, "{mm}", sMM$)
    result$ = Replace$(result$, "{dd}", sDD$)
    result$ = Replace$(result$, "{hh}", sHH$)
    result$ = Replace$(result$, "{rr}", sHH24$)
    result$ = Replace$(result$, "{nn}", sMI$)
    result$ = Replace$(result$, "{ss}", sSS$)
    result$ = Replace$(result$, "{ampm}", sAMPM$)

    ' RETURN RESULT
    GetCurrentDateTime$ = result$
End Function ' GetCurrentDateTime$

' /////////////////////////////////////////////////////////////////////////////
' Split and join strings
' https://www.qb64.org/forum/index.php?topic=1073.0

'Combine all elements of in$() into a single string with delimiter$ separating the elements.

Function join$ (in$(), delimiter$)
    Dim result$
    Dim i As Long
    result$ = in$(LBound(in$))
    For i = LBound(in$) + 1 To UBound(in$)
        result$ = result$ + delimiter$ + in$(i)
    Next i
    join$ = result$
End Function ' join$

' /////////////////////////////////////////////////////////////////////////////
' FROM: String Manipulation
' found at abandoned, outdated and now likely malicious qb64 dot net website
' http://www.qb64.[net]/forum/index_topic_5964-0/
'
'SUMMARY:
'   Purpose:  A library of custom functions that transform strings.
'   Author:   Dustinian Camburides (dustinian@gmail.com)
'   Platform: QB64 (www.qb64.org)
'   Revision: 1.6
'   Updated:  5/28/2012
'SUMMARY:
'[Replace$] replaces all instances of the [Find] sub-string with the [Add] sub-string within the [Text] string.
'INPUT:
'Text: The input string; the text that's being manipulated.
'Find: The specified sub-string; the string sought within the [Text] string.
'Add: The sub-string that's being added to the [Text] string.

Function Replace$ (Text1 As String, Find1 As String, Add1 As String)
    ' VARIABLES:
    Dim Text2 As String
    Dim Find2 As String
    Dim Add2 As String
    Dim lngLocation As Long ' The address of the [Find] substring within the [Text] string.
    Dim strBefore As String ' The characters before the string to be replaced.
    Dim strAfter As String ' The characters after the string to be replaced.
    ' INITIALIZE:
    ' MAKE COPIESSO THE ORIGINAL IS NOT MODIFIED (LIKE ByVal IN VBA)
    Text2 = Text1
    Find2 = Find1
    Add2 = Add1
    lngLocation = InStr(1, Text2, Find2)
    ' PROCESSING:
    ' While [Find2] appears in [Text2]...
    While lngLocation
        ' Extract all Text2 before the [Find2] substring:
        strBefore = Left$(Text2, lngLocation - 1)
        ' Extract all text after the [Find2] substring:
        strAfter = Right$(Text2, ((Len(Text2) - (lngLocation + Len(Find2) - 1))))
        ' Return the substring:
        Text2 = strBefore + Add2 + strAfter
        ' Locate the next instance of [Find2]:
        lngLocation = InStr(1, Text2, Find2)
        ' Next instance of [Find2]...
    Wend
    ' OUTPUT:
    Replace$ = Text2
End Function ' Replace$

' /////////////////////////////////////////////////////////////////////////////
' Split and join strings
' https://www.qb64.org/forum/index.php?topic=1073.0
'
' FROM luke, QB64 Developer
' Date: February 15, 2019, 04:11:07 AM
'
' Given a string of words separated by spaces (or any other character),
' splits it into an array of the words. I've no doubt many people have
' written a version of this over the years and no doubt there's a million
' ways to do it, but I thought I'd put mine here so we have at least one
' version. There's also a join function that does the opposite
' array -> single string.
'
' Code is hopefully reasonably self explanatory with comments and a little demo.
' Note, this is akin to Python/JavaScript split/join, PHP explode/implode.

' Split in$ into pieces, chopping at every occurrence of delimiter$. Multiple consecutive occurrences
' of delimiter$ are treated as a single instance. The chopped pieces are stored in result$().
'
' delimiter$ must be one character long.
' result$() must have been REDIMmed previously.

' Modified to handle multi-character delimiters

Sub split (in$, delimiter$, result$())
    Dim start As Integer
    Dim finish As Integer
    Dim iDelimLen As Integer
    ReDim result$(-1)

    iDelimLen = Len(delimiter$)

    start = 1
    Do
        'While Mid$(in$, start, 1) = delimiter$
        While Mid$(in$, start, iDelimLen) = delimiter$
            'start = start + 1
            start = start + iDelimLen
            If start > Len(in$) Then
                Exit Sub
            End If
        Wend
        finish = InStr(start, in$, delimiter$)
        If finish = 0 Then
            finish = Len(in$) + 1
        End If

        ReDim _Preserve result$(0 To UBound(result$) + 1)

        result$(UBound(result$)) = Mid$(in$, start, finish - start)
        start = finish + 1
    Loop While start <= Len(in$)
End Sub ' split

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' EMD GENERAL PURPOSE ROUTINES #GENERAL
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' BEGIN FILE FUNCTIONS
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
'WORKS IN QB64PE BUT NOT QB64:
'' /////////////////////////////////////////////////////////////////////////////
'' QB64 Phoenix Edition › QB64 Rising › Code and Stuff › Help Me!
'' Using shell to delete a file
'' https://qb64phoenix.com/forum/showthread.php?tid=2618&pid=24683#pid24683
'' a740g
'' #5
'' 04-24-2024, 06:05 AM
''
'' There are no commands to directly make copies or backup of files.
'' But you could write one with a few lines of code like:
''
'' Copies src to dst
'' Set overwite to _TRUE if dst should be overwritten if present
'Sub CopyFile (src As String, dst As String, overwrite As _Byte)
'    If _FileExists(src) Then
'        If Not _FileExists(dst) Or (_FileExists(dst) And overwrite) Then
'            _WriteFile dst, _ReadFile$(src)
'        End If
'    End If
'End Sub ' CopyFile

' /////////////////////////////////////////////////////////////////////////////
' QB64 Phoenix Edition › QB64 Rising › Code and Stuff › Help Me!
' Using shell to delete a file
' https://qb64phoenix.com/forum/showthread.php?tid=2618

Sub DeleteFile (sFile As String)
    If _FileExists(sFile) Then
        'Shell "DELETE " + sFile
        'Shell "del " + sFile
        Kill sFile
    End If
End Sub ' DeleteFile

' /////////////////////////////////////////////////////////////////////////////

Function FileExt$ (sFile As String)
    Dim iPos As Integer
    iPos = _InStrRev(sFile, ".")
    If iPos > 0 Then
        If Len(sFile) > 1 Then
            If iPos > 1 Then
                FileExt$ = Right$(sFile, Len(sFile) - iPos)
            Else
                ' dot is first character, return everything after it
                FileExt$ = Right$(sFile, Len(sFile) - 1)
            End If
        Else
            ' file only has one character, the dot, the file extension is blank
            FileExt$ = ""
        End If
    Else
        ' no dot found, the file extension is blank
        FileExt$ = ""
    End If
End Function ' FileExt$

' /////////////////////////////////////////////////////////////////////////////

Function NameOnly$ (sFile As String, sSlash As String)
    Dim iPos As Integer
    'sFile = Replace$(sFile, "/", "\")

    iPos = _InStrRev(sFile, sSlash)
    If iPos > 0 Then
        If Len(sFile) > 1 Then
            If iPos > 1 Then
                NameOnly$ = Right$(sFile, Len(sFile) - iPos)
            Else
                ' slash is first character, return everything after it
                NameOnly$ = Right$(sFile, Len(sFile) - 1)
            End If
        Else
            ' file only has one character, the slash, name is blank
            NameOnly$ = ""
        End If
    Else
        ' slash not found, return the entire thing
        NameOnly$ = sFile
    End If
End Function ' NameOnly$

' /////////////////////////////////////////////////////////////////////////////

Function NoExt$ (sFile As String)
    Dim iPos As Integer
    iPos = _InStrRev(sFile, ".")
    If iPos > 0 Then
        If Len(sFile) > 1 Then
            If iPos > 1 Then
                NoExt$ = Left$(sFile, iPos - 1)
            Else
                ' dot is first character, removing it returns blank!
                ' our version will just return the name unchanged
                ' but you can return blank if you prefer
                NoExt$ = sFile
            End If
        Else
            ' file only has one character, the dot, removing it returns blank!
            ' our version will just return the name unchanged
            ' but you can return blank if you prefer
            NoExt$ = sFile
        End If
    Else
        ' no dot found
        ' return the name unchanged
        NoExt$ = sFile
    End If
End Function ' NoExt$

' /////////////////////////////////////////////////////////////////////////////

Function PathOnly$ (sFile As String, sSlash As String)
    Dim iPos As Integer
    'sFile = Replace$(sFile, "/", "\")

    iPos = _InStrRev(sFile, sSlash)
    If iPos > 0 Then
        If Len(sFile) > 1 Then
            If iPos > 1 Then
                PathOnly$ = Left$(sFile, iPos)
            Else
                ' slash is first character, so not much of a path, return blank
                PathOnly$ = ""
            End If
        Else
            ' file only has one character, the slash, name is blank
            PathOnly$ = ""
        End If
    Else
        ' slash not found, so not a path, return blank
        PathOnly$ = ""
    End If
End Function ' PathOnly$

' /////////////////////////////////////////////////////////////////////////////
' Writes sText to file sFileName.
' If bAppend=_TRUE appends to file, else overwrites it.

' Returns blank if successful else returns error message.

' Example:
' ProgramPath$ = Left$(Command$(0), _InStrRev(Command$(0), "\"))
' ProgramName$: m_ProgramName$ = Mid$(Command$(0), _InStrRev(Command$(0), "\") + 1)
' sFileName = ProgramPath$ + ProgramName$ + ".OUT.txt"
' sText = "This is a test." + chr$(13) + "Here is line 2." + chr$(13) + "End."
' sError = PrintFile$(sFileName, sText, _FALSE)

Function PrintFile$ (sFileName As String, sText As String, bAppend As Integer)
    Dim sError As String: sError = ""

    If (bAppend = _TRUE) Then
        If _FileExists(sFileName) Then
            Open sFileName For Append As #1 ' opens an existing file for appending
        Else
            sError = "Error in PrintFile$ : File not found. Cannot append."
        End If
    Else
        Open sFileName For Output As #1 ' opens and clears an existing file or creates new empty file
    End If

    If Len(sError) = 0 Then
        ' NOTE: WRITE places text in quotes in the file
        'WRITE #1, x, y, z$
        'WRITE #1, sText

        ' PRINT does not put text inside quotes
        Print #1, sText

        Close #1
    End If

    PrintFile$ = sError
End Function ' PrintFile$

' /////////////////////////////////////////////////////////////////////////////
' Fastest way is always to just read the whole life at once and then parse it.

Function ReadTextFile$ (sFileName As String, sDefault As String)
    Dim x$
    If _FileExists(sFileName) Then
        Open sFileName For Binary As #1
        x$ = Space$(LOF(1))
        Get #1, 1, x$
        Close #1
        ReadTextFile$ = x$
    Else
        ReadTextFile$ = sDefault
    End If
End Function ' ReadTextFile$

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' END FILE FUNCTIONS
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' BEGIN DEBUGGING ROUTINES #DEBUG
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

' /////////////////////////////////////////////////////////////////////////////

Sub DebugLog (sText As String)
    If cDebugEnabled = _TRUE Then
        Dim sTime As String
        Dim sResult As String
        ReDim arrLines(0) As String
        Dim iLoop As Integer
        Dim sNextLine As String

        If _FileExists(m_sDebugFile) = _FALSE Then
            sResult = PrintFile$(m_sDebugFile, "", _FALSE)
        End If

        If Len(sResult) = 0 Then
            sTime = GetCurrentDateTime$("{mm}/{dd}/{yyyy} {hh}:{nn}:{ss} {ampm}")
            split sText, Chr$(13), arrLines()
            For iLoop = LBound(arrLines) To UBound(arrLines)
                sNextLine = sTime + " " + arrLines(iLoop)
                sResult = PrintFile$(m_sDebugFile, sNextLine, _TRUE)
            Next iLoop
        End If
    End If
End Sub ' DebugLog

' /////////////////////////////////////////////////////////////////////////////

Sub DebugLog1 (sText As String)
    If cDebugEnabled = _TRUE Then
        Dim sResult As String
        If _FileExists(m_sDebugFile) Then
            sResult = PrintFile$(m_sDebugFile, sText, _TRUE)
        Else
            sResult = PrintFile$(m_sDebugFile, sText, _FALSE)
        End If
    End If
End Sub ' DebugLog

' /////////////////////////////////////////////////////////////////////////////
' Writes sText to a debug file in the EXE folder.
' Debug file is named the same thing as the program EXE name with ".txt" at the end.
' For example the program "C:\QB64\MyProgram.BAS" running as
' "C:\QB64\MyProgram.EXE" would have an output file "C:\QB64\MyProgram.EXE.txt".
' If the file doesn't exist, it is created, otherwise it is appended to.

Sub PrintDebugFile (sText As String)
    Dim sFileName As String
    Dim sError As String
    Dim sOut As String
   
    If bDebug = _TRUE Then
        sFileName = m_ProgramPath$ + m_ProgramName$ + ".txt"
        sError = ""
        If _FileExists(sFileName) = _FALSE Then
            sOut = ""
            sOut = sOut + "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + Chr$(13) + Chr$(10)
            sOut = sOut + "PROGRAM : " + m_ProgramName$ + Chr$(13) + Chr$(10)
            sOut = sOut + "RUN DATE: " + CurrentDateTime$ + Chr$(13) + Chr$(10)
            sOut = sOut + "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + Chr$(13) + Chr$(10)
            sError = PrintFile$(sFileName, sOut, _FALSE)
        End If
        If Len(sError) = 0 Then
            sError = PrintFile$(sFileName, String$(m_DebugLevel%, Chr$(9)) + sText, _TRUE)
        End If
        If Len(sError) <> 0 Then
            Print CurrentDateTime$ + " PrintDebugFile FAILED: " + sError
        End If
    End If
End Sub ' PrintDebugFile

' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' END DEBUGGING ROUTINES @DEBUG
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

' ################################################################################################################################################################
' #REFERENCE

' =============================================================================
' SOME USEFUL STUFF FOR REFERENCE:

' Type Name               Type suffix symbol   Minimum value                  Maximum value                Size in Bytes
' ---------------------   ------------------   ----------------------------   --------------------------   -------------
' _BIT                    `                    -1                             0                            1/8
' _BIT * n                `n                   -128                           127                          n/8
' _UNSIGNED _BIT          ~`                   0                              1                            1/8
' _BYTE                   %%                   -128                           127                          1
' _UNSIGNED _BYTE         ~%%                  0                              255                          1
' INTEGER                 %                    -32,768                        32,767                       2
' _UNSIGNED INTEGER       ~%                   0                              65,535                       2
' LONG                    &                    -2,147,483,648                 2,147,483,647                4
' _UNSIGNED LONG          ~&                   0                              4,294,967,295                4
' _INTEGER64              &&                   -9,223,372,036,854,775,808     9,223,372,036,854,775,807    8
' _UNSIGNED _INTEGER64    ~&&                  0                              18,446,744,073,709,551,615   8
' SINGLE                  ! or none            -2.802597E-45                  +3.402823E+38                4
' DOUBLE                  #                    -4.490656458412465E-324        +1.797693134862310E+308      8
' _FLOAT                  ##                   -1.18E-4932                    +1.18E+4932                  32(10 used)
' _OFFSET                 %&                   -9,223,372,036,854,775,808     9,223,372,036,854,775,807    Use LEN
' _UNSIGNED _OFFSET       ~%&                  0                              18,446,744,073,709,551,615   Use LEN
' _MEM                    none                 combined memory variable type  N/A                          Use LEN

' div: int1% = num1% \ den1%
' mod: rem1% = num1% MOD den1%

' @END

(original post)


RE: SAM Speech Synthesis ported to native QB64PE (v0.15) - SMcNeill - 04-01-2025

(04-01-2025, 12:13 AM)madscijr Wrote: Any input would be welcome... 

Umm... That's a lot of code to produce a little static!

(You didn't say it needed to be *useful* input!!)


RE: SAM Speech Synthesis ported to native QB64PE (v0.15) - madscijr - 04-01-2025

(04-01-2025, 12:18 AM)SMcNeill Wrote:
(04-01-2025, 12:13 AM)madscijr Wrote: Any input would be welcome... 

Umm... That's a lot of code to produce a little static!

(You didn't say it needed to be *useful* input!!)
I think if you remove the routines not being used, it'll shrink. 
The heart of it is the data for the sounds that map to phonemes. 
It looks at a word and breaks it down into phoneme and looks up the sound parameters associated with that portion in the array.  
I think the play sound routine just needs to be fixed, and the data values just need to be converted to the right range to work with it...


RE: SAM Speech Synthesis ported to native QB64PE (v0.15) - madscijr - 04-01-2025

Here is just the core code that makes the different sounds, but I don't hear anything.... are the values out of range, is _SNDRAW being used wrong, or both? 

Code: (Select All)
Const PI = 3.141592653589793
Const TWOPI = 2 * PI
Const Pi2 = 8 * Atn(1)

Type PhonemeDataType
    frequency1 As Integer
    frequency2 As Integer
    amplitude As Integer
    duration As Integer
    inflection As Integer
    vowel As Integer
    pitch As Integer
    filter As Integer
    volume As Integer
    rate As Integer
    stress As Integer
    Reserved As Integer ' Not used, but included for original data compatibility
End Type

Dim Shared arrPhoneme(1 To 131) As PhonemeDataType ' Array to hold phoneme data.  Dimensioned for the number of phonemes in the DATA statements.
Dim Shared arrPhName(1 To 131) As String ' Array of phoneme names, for lookup.
Dim iLoop As Integer

Restore PhonemeNameData
For iLoop = 1 To 131
    Read arrPhName(iLoop)
Next iLoop

Restore PhonemeSoundData
For iLoop = 1 To 131
    Read arrPhoneme(iLoop).frequency1
    Read arrPhoneme(iLoop).frequency2
    Read arrPhoneme(iLoop).amplitude
    Read arrPhoneme(iLoop).duration
    Read arrPhoneme(iLoop).inflection
    Read arrPhoneme(iLoop).vowel
    Read arrPhoneme(iLoop).pitch
    Read arrPhoneme(iLoop).filter
    Read arrPhoneme(iLoop).volume
    Read arrPhoneme(iLoop).rate
    Read arrPhoneme(iLoop).stress
    Read arrPhoneme(iLoop).Reserved
Next iLoop

PhonemeNameData:
' Phoneme names corresponding to the PhonemeSoundData
Data "AH","AO","AA","AE","EH","ER","IH","EY","IY","AY","UH","UW"
Data "UX","OH","OY","AX","AXR","AW","AWH","OW","P","B","T","D"
Data "K","G","CH","JH","S","Z","SH","ZH","F","V","TH","DH","M"
Data "N","NG","L","R","W","Y","H","PAUSE","BEEP","BREATH","UH"
Data "AH","IH","EH","OH","UW","P","T","K","F","TH","S","SH","H"
Data "M","N","L","R","W","Y","B","D","G","V","DH","Z","ZH","CH"
Data "JH","NG","BREATH","UH","AH","IH","EH","OH","UW","P","T"
Data "K","F","TH","S","SH","H","M","N","L","R","W","Y","B","D"
Data "G","V","DH","Z","ZH","CH","JH","NG","PAUSE1","PAUSE2","PUNC"

PhonemeSoundData:
' Data frequency1, frequency2, amplitude, duration, inflection, vowel, pitch, filter, volume, rate, stress, Reserved
Data 160,160,32767,7,0,1,64,0,63,10,0,0
Data 260,260,32767,7,0,1,64,0,63,10,0,0
Data 294,294,32767,7,0,1,64,0,63,10,0,0
Data 247,247,32767,7,0,1,64,0,63,10,0,0
Data 220,220,32767,7,0,1,64,0,63,10,0,0
Data 208,208,32767,7,0,1,64,0,63,10,0,0
Data 311,311,32767,7,0,1,64,0,63,10,0,0
Data 277,277,32767,7,0,1,64,0,63,10,0,0
Data 330,330,32767,7,0,1,64,0,63,10,0,0
Data 311,311,32767,7,0,1,64,0,63,10,0,0
Data 370,370,32767,7,0,1,64,0,63,10,0,0
Data 392,392,32767,9,0,0,64,0,63,10,0,0
Data 440,440,32767,9,0,0,64,0,63,10,0,0
Data 494,494,32767,9,0,0,64,0,63,10,0,0
Data 523,523,32767,9,0,0,64,0,63,10,0,0
Data 587,587,32767,9,0,0,64,0,63,10,0,0
Data 659,659,32767,9,0,0,64,0,63,10,0,0
Data 698,698,32767,9,0,0,64,0,63,10,0,0
Data 784,784,32767,9,0,0,64,0,63,10,0,0
Data 880,880,32767,9,0,0,64,0,63,10,0,0
Data 988,988,32767,9,0,0,64,0,63,10,0,0
Data 1047,1047,32767,9,0,0,64,0,63,10,0,0
Data 1175,1175,32767,9,0,0,64,0,63,10,0,0
Data 1319,1319,32767,9,0,0,64,0,63,10,0,0
Data 1397,1397,32767,9,0,0,64,0,63,10,0,0
Data 1568,1568,32767,9,0,0,64,0,63,10,0,0
Data 1760,1760,32767,9,0,0,64,0,63,10,0,0
Data 1865,1865,32767,9,0,0,64,0,63,10,0,0
Data 2093,2093,32767,9,0,0,64,0,63,10,0,0
Data 2349,2349,32767,9,0,0,64,0,63,10,0,0
Data 2489,2489,32767,9,0,0,64,0,63,10,0,0
Data 160,160,32767,5,0,0,64,0,63,10,0,0
Data 160,160,32767,5,0,0,64,0,63,10,0,0
Data 160,160,32767,5,0,0,64,0,63,10,0,0
Data 160,160,32767,5,0,0,64,0,63,10,0,0
Data 349,349,32767,7,0,0,64,0,63,10,0,0
Data 175,175,32767,7,0,0,64,0,63,10,0,0
Data 196,196,32767,7,0,0,64,0,63,10,0,0
Data 233,233,32767,7,0,0,64,0,63,10,0,0
Data 277,277,32767,7,0,0,64,0,63,10,0,0
Data 311,311,32767,7,0,0,64,0,63,10,0,0
Data 349,349,32767,7,0,0,64,0,63,10,0,0
Data 415,415,32767,7,0,0,64,0,63,10,0,0
Data 466,466,32767,7,0,0,64,0,63,10,0,0
Data 523,523,32767,7,0,0,64,0,63,10,0,0
Data 587,587,32767,7,0,0,64,0,63,10,0,0
Data 622,622,32767,7,0,0,64,0,63,10,0,0
Data 698,698,32767,7,0,0,64,0,63,10,0,0
Data 784,784,32767,7,0,0,64,0,63,10,0,0
Data 830,830,32767,7,0,0,64,0,63,10,0,0
Data 932,932,32767,7,0,0,64,0,63,10,0,0
Data 1047,1047,32767,7,0,0,64,0,63,10,0,0
Data 1109,1109,32767,7,0,0,64,0,63,10,0,0
Data 1245,1245,32767,7,0,0,64,0,63,10,0,0
Data 1397,1397,32767,7,0,0,64,0,63,10,0,0
Data 1480,1480,32767,7,0,0,64,0,63,10,0,0
Data 1661,1661,32767,7,0,0,64,0,63,10,0,0
Data 1865,1865,32767,7,0,0,64,0,63,10,0,0
Data 1976,1976,32767,7,0,0,64,0,63,10,0,0
Data 2217,2217,32767,7,0,0,64,0,63,10,0,0
Data 2489,2489,32767,7,0,0,64,0,63,10,0,0
Data 2637,2637,32767,7,0,0,64,0,63,10,0,0
Data 2959,2959,32767,7,0,0,64,0,63,10,0,0
Data 3322,3322,32767,7,0,0,64,0,63,10,0,0
Data 3520,3520,32767,7,0,0,64,0,63,10,0,0
Data 3951,3951,32767,7,0,0,64,0,63,10,0,0
Data 160,160,32767,5,0,0,64,0,63,10,0,0
Data 247,247,32767,5,0,0,64,0,63,10,0,0
Data 294,294,32767,5,0,0,64,0,63,10,0,0
Data 220,220,32767,5,0,0,64,0,63,10,0,0
Data 311,311,32767,5,0,0,64,0,63,10,0,0
Data 370,370,32767,5,0,0,64,0,63,10,0,0
Data 440,440,32767,5,0,0,64,0,63,10,0,0
Data 494,494,32767,5,0,0,64,0,63,10,0,0
Data 587,587,32767,5,0,0,64,0,63,10,0,0
Data 659,659,32767,5,0,0,64,0,63,10,0,0
Data 784,784,32767,5,0,0,64,0,63,10,0,0
Data 880,880,32767,5,0,0,64,0,63,10,0,0
Data 1047,1047,32767,5,0,0,64,0,63,10,0,0
Data 1175,1175,32767,5,0,0,64,0,63,10,0,0
Data 1397,1397,32767,5,0,0,64,0,63,10,0,0
Data 1568,1568,32767,5,0,0,64,0,63,10,0,0
Data 1865,1865,32767,5,0,0,64,0,63,10,0,0
Data 2093,2093,32767,5,0,0,64,0,63,10,0,0
Data 2489,2489,32767,5,0,0,64,0,63,10,0,0
Data 2959,2959,32767,5,0,0,64,0,63,10,0,0
Data 160,160,20000,8,0,0,64,0,63,10,0,0
Data 160,160,20000,8,0,0,64,0,63,10,0,0
Data 261,261,20000,8,0,0,64,0,63,10,0,0
Data 174,174,20000,8,0,0,64,0,63,10,0,0
Data 196,196,20000,8,0,0,64,0,63,10,0,0
Data 233,233,20000,8,0,0,64,0,63,10,0,0
Data 277,277,20000,8,0,0,64,0,63,10,0,0
Data 311,311,20000,8,0,0,64,0,63,10,0,0
Data 349,349,20000,8,0,0,64,0,63,10,0,0
Data 415,415,20000,8,0,0,64,0,63,10,0,0
Data 466,466,20000,8,0,0,64,0,63,10,0,0
Data 523,523,20000,8,0,0,64,0,63,10,0,0
Data 587,587,20000,8,0,0,64,0,63,10,0,0
Data 622,622,20000,8,0,0,64,0,63,10,0,0
Data 698,698,20000,8,0,0,64,0,63,10,0,0
Data 784,784,20000,8,0,0,64,0,63,10,0,0
Data 830,830,20000,8,0,0,64,0,63,10,0,0
Data 932,932,20000,8,0,0,64,0,63,10,0,0
Data 1047,1047,20000,8,0,0,64,0,63,10,0,0
Data 1109,1109,20000,8,0,0,64,0,63,10,0,0
Data 1245,1245,20000,8,0,0,64,0,63,10,0,0
Data 1397,1397,20000,8,0,0,64,0,63,10,0,0
Data 1480,1480,20000,8,0,0,64,0,63,10,0,0
Data 1661,1661,20000,8,0,0,64,0,63,10,0,0
Data 1865,1865,20000,8,0,0,64,0,63,10,0,0
Data 1976,1976,20000,8,0,0,64,0,63,10,0,0
Data 2217,2217,20000,8,0,0,64,0,63,10,0,0
Data 2489,2489,20000,8,0,0,64,0,63,10,0,0
Data 2637,2637,20000,8,0,0,64,0,63,10,0,0
Data 2959,2959,20000,8,0,0,64,0,63,10,0,0
Data 3322,3322,20000,8,0,0,64,0,63,10,0,0
Data 3520,3520,20000,8,0,0,64,0,63,10,0,0
Data 3951,3951,20000,8,0,0,64,0,63,10,0,0
Data 160,160,20000,5,0,0,64,0,63,10,0,0
Data 247,247,20000,5,0,0,64,0,63,10,0,0
Data 294,294,20000,5,0,0,64,0,63,10,0,0
Data 220,220,20000,5,0,0,64,0,63,10,0,0
Data 311,311,20000,5,0,0,64,0,63,10,0,0
Data 370,370,20000,5,0,0,64,0,63,10,0,0
Data 440,440,20000,5,0,0,64,0,63,10,0,0
Data 494,494,20000,5,0,0,64,0,63,10,0,0
Data 587,587,20000,5,0,0,64,0,63,10,0,0
Data 659,659,20000,5,0,0,64,0,63,10,0,0
Data 784,784,20000,5,0,0,64,0,63,10,0,0
Data 880,880,20000,5,0,0,64,0,63,10,0,0
Data 1047,1047,20000,5,0,0,64,0,63,10,0,0
Data 1175,1175,20000,5,0,0,64,0,63,10,0,0
Data 1397,1397,20000,5,0,0,64,0,63,10,0,0
Data 1568,1568,20000,5,0,0,64,0,63,10,0,0
Data 1865,1865,20000,5,0,0,64,0,63,10,0,0
Data 2093,2093,20000,5,0,0,64,0,63,10,0,0
Data 2489,2489,20000,5,0,0,64,0,63,10,0,0
Data 2959,2959,20000,5,0,0,64,0,63,10,0,0
Data 160,160,32767,7,0,0,64,0,63,10,0,0
Data 170,170,32767,7,0,0,64,0,63,10,0,0
Data 160,160,32767,7,0,0,64,0,63,10,0,0
Data 170,170,32767,7,0,0,64,0,63,10,0,0
Data 181,181,32767,7,0,0,64,0,63,10,0,0
Data 192,192,32767,7,0,0,64,0,63,10,0,0
Data 204,204,32767,7,0,0,64,0,63,10,0,0
Data 216,216,32767,7,0,0,64,0,63,10,0,0
Data 230,230,32767,7,0,0,64,0,63,10,0,0
Data 244,244,32767,7,0,0,64,0,63,10,0,0
Data 259,259,32767,7,0,0,64,0,63,10,0,0
Data 275,275,32767,7,0,0,64,0,63,10,0,0
Data 292,292,32767,7,0,0,64,0,63,10,0,0
Data 310,310,32767,7,0,0,64,0,63,10,0,0
Data 329,329,32767,7,0,0,64,0,63,10,0,0
Data 349,349,32767,7,0,0,64,0,63,10,0,0
Data 370,370,32767,7,0,0,64,0,63,10,0,0
Data 392,392,32767,7,0,0,64,0,63,10,0,0
Data 415,415,32767,7,0,0,64,0,63,10,0,0
Data 440,440,32767,7,0,0,64,0,63,10,0,0
Data 466,466,32767,7,0,0,64,0,63,10,0,0
Data 494,494,32767,7,0,0,64,0,63,10,0,0
Data 523,523,32767,7,0,0,64,0,63,10,0,0
Data 554,554,32767,7,0,0,64,0,63,10,0,0
Data 587,587,32767,7,0,0,64,0,63,10,0,0
Data 622,622,32767,7,0,0,64,0,63,10,0,0
Data 659,659,32767,7,0,0,64,0,63,10,0,0
Data 698,698,32767,7,0,0,64,0,63,10,0,0
Data 740,740,32767,7,0,0,64,0,63,10,0,0
Data 784,784,32767,7,0,0,64,0,63,10,0,0
Data 830,830,32767,7,0,0,64,0,63,10,0,0
Data 880,880,32767,7,0,0,64,0,63,10,0,0
Data 932,932,32767,7,0,0,64,0,63,10,0,0
Data 988,988,32767,7,0,0,64,0,63,10,0,0
Data 1047,1047,32767,7,0,0,64,0,63,10,0,0
Data 1109,1109,32767,7,0,0,64,0,63,10,0,0
Data 1175,1175,32767,7,0,0,64,0,63,10,0,0
Data 1245,1245,32767,7,0,0,64,0,63,10,0,0
Data 1319,1319,32767,7,0,0,64,0,63,10,0,0
Data 1397,1397,32767,7,0,0,64,0,63,10,0,0
Data 1480,1480,32767,7,0,0,64,0,63,10,0,0
Data 1568,1568,32767,7,0,0,64,0,63,10,0,0
Data 1661,1661,32767,7,0,0,64,0,63,10,0,0
Data 1760,1760,32767,7,0,0,64,0,63,10,0,0
Data 1865,1865,32767,7,0,0,64,0,63,10,0,0
Data 1976,1976,32767,7,0,0,64,0,63,10,0,0
Data 2093,2093,32767,7,0,0,64,0,63,10,0,0
Data 2217,2217,32767,7,0,0,64,0,63,10,0,0
Data 2349,2349,32767,7,0,0,64,0,63,10,0,0
Data 2489,2489,32767,7,0,0,64,0,63,10,0,0
Data 2637,2637,32767,7,0,0,64,0,63,10,0,0
Data 2794,2794,32767,7,0,0,64,0,63,10,0,0
Data 2959,2959,32767,7,0,0,64,0,63,10,0,0
Data 3136,3136,32767,7,0,0,64,0,63,10,0,0
Data 3322,3322,32767,7,0,0,64,0,63,10,0,0
Data 3520,3520,32767,7,0,0,64,0,63,10,0,0
Data 3729,3729,32767,7,0,0,64,0,63,10,0,0
Data 3951,3951,32767,7,0,0,64,0,63,10,0,0

Dim frequency As Integer
Dim duration As Integer
Dim amplitude As Integer
For iLoop = 1 To 131
    frequency = arrPhoneme(iLoop).frequency1
    duration = arrPhoneme(iLoop).duration
    amplitude = arrPhoneme(iLoop).amplitude

    Print _Trim$(Str$(iLoop)) + ". " + arrPhName(iLoop) + " : " + _
        "frequency=" + _TRIM$(str$(frequency)) + ", " + _
        "duraton=" + _TRIM$(str$(duration)) + ", " + _
        "amplitude=" + _TRIM$(str$(amplitude))
   
    PlaySound frequency, duration, amplitude
Next iLoop

_Delay 0.2 ' Add a short delay after speaking, important for some sound cards.

End

Sub PlaySound (frequency As Integer, duration As Integer, amplitude As Integer)
    Dim amplitude1 As Single
    Dim duration1 As Single
    Dim SampleRate&
    Dim FRate As Single
    Dim SndLoop!

    ' input value of frequency is 160-3951
    ' (not sure if we need to convert it for _SNDRAW)

    ' input duration1 is probably 1/18th second ?
    ' do we need to convert this to some other value?

    ' input value of amplitude is 0-32767
    ' convert to amplitude1 which must be from -1.0 to 1.0
    amplitude1 = (2 * (amplitude / 32767)) - 1
    'amplitude1 = .3 ' amplitude of the signal from -1.0 to 1.0

    SampleRate& = _SndRate ' sets the sample rate
    FRate = frequency / SampleRate&

    SndLoop! = 0
    Do While SndLoop! < SampleRate&
        '_SNDRAW SIN((2 * 4 * ATN(1) * SndLoop! / SampleRate&) * frq!) * EXP(-(SndLoop! / SampleRate&) * 3)
        '_SNDRAW amplitude1 * SIN(Pi2 * Duration * FRate) 'sine wave
        _SndRaw amplitude1 * Sgn(Sin(Pi2 * duration * FRate)) ' square wave
        SndLoop! = SndLoop! + 1
    Loop

    _SndRawDone
    Do: Loop While _SndRawLen 'flush the sound playing buffer

End Sub ' PlaySound



RE: SAM Speech Synthesis ported to native QB64PE (v0.15) - madscijr - 04-01-2025

This version prints out the parameters as it attempts to paly each sound...

Code: (Select All)
Const PI = 3.141592653589793
Const TWOPI = 2 * PI
Const Pi2 = 8 * Atn(1)

Type PhonemeDataType
    frequency1 As Integer
    frequency2 As Integer
    amplitude As Integer
    duration As Integer
    inflection As Integer
    vowel As Integer
    pitch As Integer
    filter As Integer
    volume As Integer
    rate As Integer
    stress As Integer
    Reserved As Integer ' Not used, but included for original data compatibility
End Type

Dim Shared arrPhoneme(1 To 131) As PhonemeDataType ' Array to hold phoneme data.  Dimensioned for the number of phonemes in the DATA statements.
Dim Shared arrPhName(1 To 131) As String ' Array of phoneme names, for lookup.
Dim iLoop As Integer

Restore PhonemeNameData
For iLoop = 1 To 131
    Read arrPhName(iLoop)
Next iLoop

Restore PhonemeSoundData
For iLoop = 1 To 131
    Read arrPhoneme(iLoop).frequency1
    Read arrPhoneme(iLoop).frequency2
    Read arrPhoneme(iLoop).amplitude
    Read arrPhoneme(iLoop).duration
    Read arrPhoneme(iLoop).inflection
    Read arrPhoneme(iLoop).vowel
    Read arrPhoneme(iLoop).pitch
    Read arrPhoneme(iLoop).filter
    Read arrPhoneme(iLoop).volume
    Read arrPhoneme(iLoop).rate
    Read arrPhoneme(iLoop).stress
    Read arrPhoneme(iLoop).Reserved
Next iLoop

PhonemeNameData:
' Phoneme names corresponding to the PhonemeSoundData
Data "AH","AO","AA","AE","EH","ER","IH","EY","IY","AY","UH","UW"
Data "UX","OH","OY","AX","AXR","AW","AWH","OW","P","B","T","D"
Data "K","G","CH","JH","S","Z","SH","ZH","F","V","TH","DH","M"
Data "N","NG","L","R","W","Y","H","PAUSE","BEEP","BREATH","UH"
Data "AH","IH","EH","OH","UW","P","T","K","F","TH","S","SH","H"
Data "M","N","L","R","W","Y","B","D","G","V","DH","Z","ZH","CH"
Data "JH","NG","BREATH","UH","AH","IH","EH","OH","UW","P","T"
Data "K","F","TH","S","SH","H","M","N","L","R","W","Y","B","D"
Data "G","V","DH","Z","ZH","CH","JH","NG","PAUSE1","PAUSE2","PUNC"

PhonemeSoundData:
' Data frequency1, frequency2, amplitude, duration, inflection, vowel, pitch, filter, volume, rate, stress, Reserved
Data 160,160,32767,7,0,1,64,0,63,10,0,0
Data 260,260,32767,7,0,1,64,0,63,10,0,0
Data 294,294,32767,7,0,1,64,0,63,10,0,0
Data 247,247,32767,7,0,1,64,0,63,10,0,0
Data 220,220,32767,7,0,1,64,0,63,10,0,0
Data 208,208,32767,7,0,1,64,0,63,10,0,0
Data 311,311,32767,7,0,1,64,0,63,10,0,0
Data 277,277,32767,7,0,1,64,0,63,10,0,0
Data 330,330,32767,7,0,1,64,0,63,10,0,0
Data 311,311,32767,7,0,1,64,0,63,10,0,0
Data 370,370,32767,7,0,1,64,0,63,10,0,0
Data 392,392,32767,9,0,0,64,0,63,10,0,0
Data 440,440,32767,9,0,0,64,0,63,10,0,0
Data 494,494,32767,9,0,0,64,0,63,10,0,0
Data 523,523,32767,9,0,0,64,0,63,10,0,0
Data 587,587,32767,9,0,0,64,0,63,10,0,0
Data 659,659,32767,9,0,0,64,0,63,10,0,0
Data 698,698,32767,9,0,0,64,0,63,10,0,0
Data 784,784,32767,9,0,0,64,0,63,10,0,0
Data 880,880,32767,9,0,0,64,0,63,10,0,0
Data 988,988,32767,9,0,0,64,0,63,10,0,0
Data 1047,1047,32767,9,0,0,64,0,63,10,0,0
Data 1175,1175,32767,9,0,0,64,0,63,10,0,0
Data 1319,1319,32767,9,0,0,64,0,63,10,0,0
Data 1397,1397,32767,9,0,0,64,0,63,10,0,0
Data 1568,1568,32767,9,0,0,64,0,63,10,0,0
Data 1760,1760,32767,9,0,0,64,0,63,10,0,0
Data 1865,1865,32767,9,0,0,64,0,63,10,0,0
Data 2093,2093,32767,9,0,0,64,0,63,10,0,0
Data 2349,2349,32767,9,0,0,64,0,63,10,0,0
Data 2489,2489,32767,9,0,0,64,0,63,10,0,0
Data 160,160,32767,5,0,0,64,0,63,10,0,0
Data 160,160,32767,5,0,0,64,0,63,10,0,0
Data 160,160,32767,5,0,0,64,0,63,10,0,0
Data 160,160,32767,5,0,0,64,0,63,10,0,0
Data 349,349,32767,7,0,0,64,0,63,10,0,0
Data 175,175,32767,7,0,0,64,0,63,10,0,0
Data 196,196,32767,7,0,0,64,0,63,10,0,0
Data 233,233,32767,7,0,0,64,0,63,10,0,0
Data 277,277,32767,7,0,0,64,0,63,10,0,0
Data 311,311,32767,7,0,0,64,0,63,10,0,0
Data 349,349,32767,7,0,0,64,0,63,10,0,0
Data 415,415,32767,7,0,0,64,0,63,10,0,0
Data 466,466,32767,7,0,0,64,0,63,10,0,0
Data 523,523,32767,7,0,0,64,0,63,10,0,0
Data 587,587,32767,7,0,0,64,0,63,10,0,0
Data 622,622,32767,7,0,0,64,0,63,10,0,0
Data 698,698,32767,7,0,0,64,0,63,10,0,0
Data 784,784,32767,7,0,0,64,0,63,10,0,0
Data 830,830,32767,7,0,0,64,0,63,10,0,0
Data 932,932,32767,7,0,0,64,0,63,10,0,0
Data 1047,1047,32767,7,0,0,64,0,63,10,0,0
Data 1109,1109,32767,7,0,0,64,0,63,10,0,0
Data 1245,1245,32767,7,0,0,64,0,63,10,0,0
Data 1397,1397,32767,7,0,0,64,0,63,10,0,0
Data 1480,1480,32767,7,0,0,64,0,63,10,0,0
Data 1661,1661,32767,7,0,0,64,0,63,10,0,0
Data 1865,1865,32767,7,0,0,64,0,63,10,0,0
Data 1976,1976,32767,7,0,0,64,0,63,10,0,0
Data 2217,2217,32767,7,0,0,64,0,63,10,0,0
Data 2489,2489,32767,7,0,0,64,0,63,10,0,0
Data 2637,2637,32767,7,0,0,64,0,63,10,0,0
Data 2959,2959,32767,7,0,0,64,0,63,10,0,0
Data 3322,3322,32767,7,0,0,64,0,63,10,0,0
Data 3520,3520,32767,7,0,0,64,0,63,10,0,0
Data 3951,3951,32767,7,0,0,64,0,63,10,0,0
Data 160,160,32767,5,0,0,64,0,63,10,0,0
Data 247,247,32767,5,0,0,64,0,63,10,0,0
Data 294,294,32767,5,0,0,64,0,63,10,0,0
Data 220,220,32767,5,0,0,64,0,63,10,0,0
Data 311,311,32767,5,0,0,64,0,63,10,0,0
Data 370,370,32767,5,0,0,64,0,63,10,0,0
Data 440,440,32767,5,0,0,64,0,63,10,0,0
Data 494,494,32767,5,0,0,64,0,63,10,0,0
Data 587,587,32767,5,0,0,64,0,63,10,0,0
Data 659,659,32767,5,0,0,64,0,63,10,0,0
Data 784,784,32767,5,0,0,64,0,63,10,0,0
Data 880,880,32767,5,0,0,64,0,63,10,0,0
Data 1047,1047,32767,5,0,0,64,0,63,10,0,0
Data 1175,1175,32767,5,0,0,64,0,63,10,0,0
Data 1397,1397,32767,5,0,0,64,0,63,10,0,0
Data 1568,1568,32767,5,0,0,64,0,63,10,0,0
Data 1865,1865,32767,5,0,0,64,0,63,10,0,0
Data 2093,2093,32767,5,0,0,64,0,63,10,0,0
Data 2489,2489,32767,5,0,0,64,0,63,10,0,0
Data 2959,2959,32767,5,0,0,64,0,63,10,0,0
Data 160,160,20000,8,0,0,64,0,63,10,0,0
Data 160,160,20000,8,0,0,64,0,63,10,0,0
Data 261,261,20000,8,0,0,64,0,63,10,0,0
Data 174,174,20000,8,0,0,64,0,63,10,0,0
Data 196,196,20000,8,0,0,64,0,63,10,0,0
Data 233,233,20000,8,0,0,64,0,63,10,0,0
Data 277,277,20000,8,0,0,64,0,63,10,0,0
Data 311,311,20000,8,0,0,64,0,63,10,0,0
Data 349,349,20000,8,0,0,64,0,63,10,0,0
Data 415,415,20000,8,0,0,64,0,63,10,0,0
Data 466,466,20000,8,0,0,64,0,63,10,0,0
Data 523,523,20000,8,0,0,64,0,63,10,0,0
Data 587,587,20000,8,0,0,64,0,63,10,0,0
Data 622,622,20000,8,0,0,64,0,63,10,0,0
Data 698,698,20000,8,0,0,64,0,63,10,0,0
Data 784,784,20000,8,0,0,64,0,63,10,0,0
Data 830,830,20000,8,0,0,64,0,63,10,0,0
Data 932,932,20000,8,0,0,64,0,63,10,0,0
Data 1047,1047,20000,8,0,0,64,0,63,10,0,0
Data 1109,1109,20000,8,0,0,64,0,63,10,0,0
Data 1245,1245,20000,8,0,0,64,0,63,10,0,0
Data 1397,1397,20000,8,0,0,64,0,63,10,0,0
Data 1480,1480,20000,8,0,0,64,0,63,10,0,0
Data 1661,1661,20000,8,0,0,64,0,63,10,0,0
Data 1865,1865,20000,8,0,0,64,0,63,10,0,0
Data 1976,1976,20000,8,0,0,64,0,63,10,0,0
Data 2217,2217,20000,8,0,0,64,0,63,10,0,0
Data 2489,2489,20000,8,0,0,64,0,63,10,0,0
Data 2637,2637,20000,8,0,0,64,0,63,10,0,0
Data 2959,2959,20000,8,0,0,64,0,63,10,0,0
Data 3322,3322,20000,8,0,0,64,0,63,10,0,0
Data 3520,3520,20000,8,0,0,64,0,63,10,0,0
Data 3951,3951,20000,8,0,0,64,0,63,10,0,0
Data 160,160,20000,5,0,0,64,0,63,10,0,0
Data 247,247,20000,5,0,0,64,0,63,10,0,0
Data 294,294,20000,5,0,0,64,0,63,10,0,0
Data 220,220,20000,5,0,0,64,0,63,10,0,0
Data 311,311,20000,5,0,0,64,0,63,10,0,0
Data 370,370,20000,5,0,0,64,0,63,10,0,0
Data 440,440,20000,5,0,0,64,0,63,10,0,0
Data 494,494,20000,5,0,0,64,0,63,10,0,0
Data 587,587,20000,5,0,0,64,0,63,10,0,0
Data 659,659,20000,5,0,0,64,0,63,10,0,0
Data 784,784,20000,5,0,0,64,0,63,10,0,0
Data 880,880,20000,5,0,0,64,0,63,10,0,0
Data 1047,1047,20000,5,0,0,64,0,63,10,0,0
Data 1175,1175,20000,5,0,0,64,0,63,10,0,0
Data 1397,1397,20000,5,0,0,64,0,63,10,0,0
Data 1568,1568,20000,5,0,0,64,0,63,10,0,0
Data 1865,1865,20000,5,0,0,64,0,63,10,0,0
Data 2093,2093,20000,5,0,0,64,0,63,10,0,0
Data 2489,2489,20000,5,0,0,64,0,63,10,0,0
Data 2959,2959,20000,5,0,0,64,0,63,10,0,0
Data 160,160,32767,7,0,0,64,0,63,10,0,0
Data 170,170,32767,7,0,0,64,0,63,10,0,0
Data 160,160,32767,7,0,0,64,0,63,10,0,0
Data 170,170,32767,7,0,0,64,0,63,10,0,0
Data 181,181,32767,7,0,0,64,0,63,10,0,0
Data 192,192,32767,7,0,0,64,0,63,10,0,0
Data 204,204,32767,7,0,0,64,0,63,10,0,0
Data 216,216,32767,7,0,0,64,0,63,10,0,0
Data 230,230,32767,7,0,0,64,0,63,10,0,0
Data 244,244,32767,7,0,0,64,0,63,10,0,0
Data 259,259,32767,7,0,0,64,0,63,10,0,0
Data 275,275,32767,7,0,0,64,0,63,10,0,0
Data 292,292,32767,7,0,0,64,0,63,10,0,0
Data 310,310,32767,7,0,0,64,0,63,10,0,0
Data 329,329,32767,7,0,0,64,0,63,10,0,0
Data 349,349,32767,7,0,0,64,0,63,10,0,0
Data 370,370,32767,7,0,0,64,0,63,10,0,0
Data 392,392,32767,7,0,0,64,0,63,10,0,0
Data 415,415,32767,7,0,0,64,0,63,10,0,0
Data 440,440,32767,7,0,0,64,0,63,10,0,0
Data 466,466,32767,7,0,0,64,0,63,10,0,0
Data 494,494,32767,7,0,0,64,0,63,10,0,0
Data 523,523,32767,7,0,0,64,0,63,10,0,0
Data 554,554,32767,7,0,0,64,0,63,10,0,0
Data 587,587,32767,7,0,0,64,0,63,10,0,0
Data 622,622,32767,7,0,0,64,0,63,10,0,0
Data 659,659,32767,7,0,0,64,0,63,10,0,0
Data 698,698,32767,7,0,0,64,0,63,10,0,0
Data 740,740,32767,7,0,0,64,0,63,10,0,0
Data 784,784,32767,7,0,0,64,0,63,10,0,0
Data 830,830,32767,7,0,0,64,0,63,10,0,0
Data 880,880,32767,7,0,0,64,0,63,10,0,0
Data 932,932,32767,7,0,0,64,0,63,10,0,0
Data 988,988,32767,7,0,0,64,0,63,10,0,0
Data 1047,1047,32767,7,0,0,64,0,63,10,0,0
Data 1109,1109,32767,7,0,0,64,0,63,10,0,0
Data 1175,1175,32767,7,0,0,64,0,63,10,0,0
Data 1245,1245,32767,7,0,0,64,0,63,10,0,0
Data 1319,1319,32767,7,0,0,64,0,63,10,0,0
Data 1397,1397,32767,7,0,0,64,0,63,10,0,0
Data 1480,1480,32767,7,0,0,64,0,63,10,0,0
Data 1568,1568,32767,7,0,0,64,0,63,10,0,0
Data 1661,1661,32767,7,0,0,64,0,63,10,0,0
Data 1760,1760,32767,7,0,0,64,0,63,10,0,0
Data 1865,1865,32767,7,0,0,64,0,63,10,0,0
Data 1976,1976,32767,7,0,0,64,0,63,10,0,0
Data 2093,2093,32767,7,0,0,64,0,63,10,0,0
Data 2217,2217,32767,7,0,0,64,0,63,10,0,0
Data 2349,2349,32767,7,0,0,64,0,63,10,0,0
Data 2489,2489,32767,7,0,0,64,0,63,10,0,0
Data 2637,2637,32767,7,0,0,64,0,63,10,0,0
Data 2794,2794,32767,7,0,0,64,0,63,10,0,0
Data 2959,2959,32767,7,0,0,64,0,63,10,0,0
Data 3136,3136,32767,7,0,0,64,0,63,10,0,0
Data 3322,3322,32767,7,0,0,64,0,63,10,0,0
Data 3520,3520,32767,7,0,0,64,0,63,10,0,0
Data 3729,3729,32767,7,0,0,64,0,63,10,0,0
Data 3951,3951,32767,7,0,0,64,0,63,10,0,0

Dim frequency As Integer
Dim duration As Integer
Dim amplitude As Integer
For iLoop = 1 To 131
    frequency = arrPhoneme(iLoop).frequency1
    duration = arrPhoneme(iLoop).duration
    amplitude = arrPhoneme(iLoop).amplitude

    Print _Trim$(Str$(iLoop)) + ". " + arrPhName(iLoop) + " : " + _
        "frequency=" + _TRIM$(str$(frequency)) + ", " + _
        "duraton=" + _TRIM$(str$(duration)) + ", " + _
        "amplitude=" + _TRIM$(str$(amplitude))
   
    PlaySound frequency, duration, amplitude
Next iLoop

_Delay 0.2 ' Add a short delay after speaking, important for some sound cards.

End

Sub PlaySound (frequency As Integer, duration As Integer, amplitude As Integer)
    Dim amplitude1 As Single
    Dim duration1 As Single
    Dim SampleRate&
    Dim FRate As Single
    Dim SndLoop!

    ' input value of frequency is 160-3951
    ' (not sure if we need to convert it for _SNDRAW)

    ' input duration1 is probably 1/18th second ?
    ' do we need to convert this to some other value?

    ' input value of amplitude is 0-32767
    ' convert to amplitude1 which must be from -1.0 to 1.0
    amplitude1 = (2 * (amplitude / 32767)) - 1
    'amplitude1 = .3 ' amplitude of the signal from -1.0 to 1.0

    SampleRate& = _SndRate ' sets the sample rate
    FRate = frequency / SampleRate&
   
    Print "    PlaySound values: "
    Print "        amplitude1=" + _Trim$(Str$(amplitude1))
    Print "        SampleRate&=" + _Trim$(Str$(SampleRate&))
    Print "        FRate=" + _Trim$(Str$(FRate))
    Print "        Pi2=" + _Trim$(Str$(Pi2))
   
    SndLoop! = 0
    Do While SndLoop! < SampleRate&
        '_SNDRAW SIN((2 * 4 * ATN(1) * SndLoop! / SampleRate&) * frq!) * EXP(-(SndLoop! / SampleRate&) * 3)
        '_SNDRAW amplitude1 * SIN(Pi2 * Duration * FRate) 'sine wave
        _SndRaw amplitude1 * Sgn(Sin(Pi2 * duration * FRate)) ' square wave
        SndLoop! = SndLoop! + 1
    Loop

    _SndRawDone
    Do: Loop While _SndRawLen 'flush the sound playing buffer

End Sub ' PlaySound



RE: SAM Speech Synthesis ported to native QB64PE (v0.15) - madscijr - 04-01-2025

(04-01-2025, 01:38 AM)madscijr Wrote: This version prints out the parameters as it attempts to play each sound...

Code: (Select All)
Const PI = 3.141592653589793
Const TWOPI = 2 * PI
Const Pi2 = 8 * Atn(1)

Type PhonemeDataType
    frequency1 As Integer
    frequency2 As Integer
    amplitude As Integer
    duration As Integer
    inflection As Integer
    vowel As Integer
    pitch As Integer
    filter As Integer
    volume As Integer
    rate As Integer
    stress As Integer
    Reserved As Integer ' Not used, but included for original data compatibility
End Type

Dim Shared arrPhoneme(1 To 131) As PhonemeDataType ' Array to hold phoneme data.  Dimensioned for the number of phonemes in the DATA statements.
Dim Shared arrPhName(1 To 131) As String ' Array of phoneme names, for lookup.
Dim iLoop As Integer

Restore PhonemeNameData
For iLoop = 1 To 131
    Read arrPhName(iLoop)
Next iLoop

Restore PhonemeSoundData
For iLoop = 1 To 131
    Read arrPhoneme(iLoop).frequency1
    Read arrPhoneme(iLoop).frequency2
    Read arrPhoneme(iLoop).amplitude
    Read arrPhoneme(iLoop).duration
    Read arrPhoneme(iLoop).inflection
    Read arrPhoneme(iLoop).vowel
    Read arrPhoneme(iLoop).pitch
    Read arrPhoneme(iLoop).filter
    Read arrPhoneme(iLoop).volume
    Read arrPhoneme(iLoop).rate
    Read arrPhoneme(iLoop).stress
    Read arrPhoneme(iLoop).Reserved
Next iLoop

PhonemeNameData:
' Phoneme names corresponding to the PhonemeSoundData
Data "AH","AO","AA","AE","EH","ER","IH","EY","IY","AY","UH","UW"
Data "UX","OH","OY","AX","AXR","AW","AWH","OW","P","B","T","D"
Data "K","G","CH","JH","S","Z","SH","ZH","F","V","TH","DH","M"
Data "N","NG","L","R","W","Y","H","PAUSE","BEEP","BREATH","UH"
Data "AH","IH","EH","OH","UW","P","T","K","F","TH","S","SH","H"
Data "M","N","L","R","W","Y","B","D","G","V","DH","Z","ZH","CH"
Data "JH","NG","BREATH","UH","AH","IH","EH","OH","UW","P","T"
Data "K","F","TH","S","SH","H","M","N","L","R","W","Y","B","D"
Data "G","V","DH","Z","ZH","CH","JH","NG","PAUSE1","PAUSE2","PUNC"

PhonemeSoundData:
' Data frequency1, frequency2, amplitude, duration, inflection, vowel, pitch, filter, volume, rate, stress, Reserved
Data 160,160,32767,7,0,1,64,0,63,10,0,0
Data 260,260,32767,7,0,1,64,0,63,10,0,0
Data 294,294,32767,7,0,1,64,0,63,10,0,0
Data 247,247,32767,7,0,1,64,0,63,10,0,0
Data 220,220,32767,7,0,1,64,0,63,10,0,0
Data 208,208,32767,7,0,1,64,0,63,10,0,0
Data 311,311,32767,7,0,1,64,0,63,10,0,0
Data 277,277,32767,7,0,1,64,0,63,10,0,0
Data 330,330,32767,7,0,1,64,0,63,10,0,0
Data 311,311,32767,7,0,1,64,0,63,10,0,0
Data 370,370,32767,7,0,1,64,0,63,10,0,0
Data 392,392,32767,9,0,0,64,0,63,10,0,0
Data 440,440,32767,9,0,0,64,0,63,10,0,0
Data 494,494,32767,9,0,0,64,0,63,10,0,0
Data 523,523,32767,9,0,0,64,0,63,10,0,0
Data 587,587,32767,9,0,0,64,0,63,10,0,0
Data 659,659,32767,9,0,0,64,0,63,10,0,0
Data 698,698,32767,9,0,0,64,0,63,10,0,0
Data 784,784,32767,9,0,0,64,0,63,10,0,0
Data 880,880,32767,9,0,0,64,0,63,10,0,0
Data 988,988,32767,9,0,0,64,0,63,10,0,0
Data 1047,1047,32767,9,0,0,64,0,63,10,0,0
Data 1175,1175,32767,9,0,0,64,0,63,10,0,0
Data 1319,1319,32767,9,0,0,64,0,63,10,0,0
Data 1397,1397,32767,9,0,0,64,0,63,10,0,0
Data 1568,1568,32767,9,0,0,64,0,63,10,0,0
Data 1760,1760,32767,9,0,0,64,0,63,10,0,0
Data 1865,1865,32767,9,0,0,64,0,63,10,0,0
Data 2093,2093,32767,9,0,0,64,0,63,10,0,0
Data 2349,2349,32767,9,0,0,64,0,63,10,0,0
Data 2489,2489,32767,9,0,0,64,0,63,10,0,0
Data 160,160,32767,5,0,0,64,0,63,10,0,0
Data 160,160,32767,5,0,0,64,0,63,10,0,0
Data 160,160,32767,5,0,0,64,0,63,10,0,0
Data 160,160,32767,5,0,0,64,0,63,10,0,0
Data 349,349,32767,7,0,0,64,0,63,10,0,0
Data 175,175,32767,7,0,0,64,0,63,10,0,0
Data 196,196,32767,7,0,0,64,0,63,10,0,0
Data 233,233,32767,7,0,0,64,0,63,10,0,0
Data 277,277,32767,7,0,0,64,0,63,10,0,0
Data 311,311,32767,7,0,0,64,0,63,10,0,0
Data 349,349,32767,7,0,0,64,0,63,10,0,0
Data 415,415,32767,7,0,0,64,0,63,10,0,0
Data 466,466,32767,7,0,0,64,0,63,10,0,0
Data 523,523,32767,7,0,0,64,0,63,10,0,0
Data 587,587,32767,7,0,0,64,0,63,10,0,0
Data 622,622,32767,7,0,0,64,0,63,10,0,0
Data 698,698,32767,7,0,0,64,0,63,10,0,0
Data 784,784,32767,7,0,0,64,0,63,10,0,0
Data 830,830,32767,7,0,0,64,0,63,10,0,0
Data 932,932,32767,7,0,0,64,0,63,10,0,0
Data 1047,1047,32767,7,0,0,64,0,63,10,0,0
Data 1109,1109,32767,7,0,0,64,0,63,10,0,0
Data 1245,1245,32767,7,0,0,64,0,63,10,0,0
Data 1397,1397,32767,7,0,0,64,0,63,10,0,0
Data 1480,1480,32767,7,0,0,64,0,63,10,0,0
Data 1661,1661,32767,7,0,0,64,0,63,10,0,0
Data 1865,1865,32767,7,0,0,64,0,63,10,0,0
Data 1976,1976,32767,7,0,0,64,0,63,10,0,0
Data 2217,2217,32767,7,0,0,64,0,63,10,0,0
Data 2489,2489,32767,7,0,0,64,0,63,10,0,0
Data 2637,2637,32767,7,0,0,64,0,63,10,0,0
Data 2959,2959,32767,7,0,0,64,0,63,10,0,0
Data 3322,3322,32767,7,0,0,64,0,63,10,0,0
Data 3520,3520,32767,7,0,0,64,0,63,10,0,0
Data 3951,3951,32767,7,0,0,64,0,63,10,0,0
Data 160,160,32767,5,0,0,64,0,63,10,0,0
Data 247,247,32767,5,0,0,64,0,63,10,0,0
Data 294,294,32767,5,0,0,64,0,63,10,0,0
Data 220,220,32767,5,0,0,64,0,63,10,0,0
Data 311,311,32767,5,0,0,64,0,63,10,0,0
Data 370,370,32767,5,0,0,64,0,63,10,0,0
Data 440,440,32767,5,0,0,64,0,63,10,0,0
Data 494,494,32767,5,0,0,64,0,63,10,0,0
Data 587,587,32767,5,0,0,64,0,63,10,0,0
Data 659,659,32767,5,0,0,64,0,63,10,0,0
Data 784,784,32767,5,0,0,64,0,63,10,0,0
Data 880,880,32767,5,0,0,64,0,63,10,0,0
Data 1047,1047,32767,5,0,0,64,0,63,10,0,0
Data 1175,1175,32767,5,0,0,64,0,63,10,0,0
Data 1397,1397,32767,5,0,0,64,0,63,10,0,0
Data 1568,1568,32767,5,0,0,64,0,63,10,0,0
Data 1865,1865,32767,5,0,0,64,0,63,10,0,0
Data 2093,2093,32767,5,0,0,64,0,63,10,0,0
Data 2489,2489,32767,5,0,0,64,0,63,10,0,0
Data 2959,2959,32767,5,0,0,64,0,63,10,0,0
Data 160,160,20000,8,0,0,64,0,63,10,0,0
Data 160,160,20000,8,0,0,64,0,63,10,0,0
Data 261,261,20000,8,0,0,64,0,63,10,0,0
Data 174,174,20000,8,0,0,64,0,63,10,0,0
Data 196,196,20000,8,0,0,64,0,63,10,0,0
Data 233,233,20000,8,0,0,64,0,63,10,0,0
Data 277,277,20000,8,0,0,64,0,63,10,0,0
Data 311,311,20000,8,0,0,64,0,63,10,0,0
Data 349,349,20000,8,0,0,64,0,63,10,0,0
Data 415,415,20000,8,0,0,64,0,63,10,0,0
Data 466,466,20000,8,0,0,64,0,63,10,0,0
Data 523,523,20000,8,0,0,64,0,63,10,0,0
Data 587,587,20000,8,0,0,64,0,63,10,0,0
Data 622,622,20000,8,0,0,64,0,63,10,0,0
Data 698,698,20000,8,0,0,64,0,63,10,0,0
Data 784,784,20000,8,0,0,64,0,63,10,0,0
Data 830,830,20000,8,0,0,64,0,63,10,0,0
Data 932,932,20000,8,0,0,64,0,63,10,0,0
Data 1047,1047,20000,8,0,0,64,0,63,10,0,0
Data 1109,1109,20000,8,0,0,64,0,63,10,0,0
Data 1245,1245,20000,8,0,0,64,0,63,10,0,0
Data 1397,1397,20000,8,0,0,64,0,63,10,0,0
Data 1480,1480,20000,8,0,0,64,0,63,10,0,0
Data 1661,1661,20000,8,0,0,64,0,63,10,0,0
Data 1865,1865,20000,8,0,0,64,0,63,10,0,0
Data 1976,1976,20000,8,0,0,64,0,63,10,0,0
Data 2217,2217,20000,8,0,0,64,0,63,10,0,0
Data 2489,2489,20000,8,0,0,64,0,63,10,0,0
Data 2637,2637,20000,8,0,0,64,0,63,10,0,0
Data 2959,2959,20000,8,0,0,64,0,63,10,0,0
Data 3322,3322,20000,8,0,0,64,0,63,10,0,0
Data 3520,3520,20000,8,0,0,64,0,63,10,0,0
Data 3951,3951,20000,8,0,0,64,0,63,10,0,0
Data 160,160,20000,5,0,0,64,0,63,10,0,0
Data 247,247,20000,5,0,0,64,0,63,10,0,0
Data 294,294,20000,5,0,0,64,0,63,10,0,0
Data 220,220,20000,5,0,0,64,0,63,10,0,0
Data 311,311,20000,5,0,0,64,0,63,10,0,0
Data 370,370,20000,5,0,0,64,0,63,10,0,0
Data 440,440,20000,5,0,0,64,0,63,10,0,0
Data 494,494,20000,5,0,0,64,0,63,10,0,0
Data 587,587,20000,5,0,0,64,0,63,10,0,0
Data 659,659,20000,5,0,0,64,0,63,10,0,0
Data 784,784,20000,5,0,0,64,0,63,10,0,0
Data 880,880,20000,5,0,0,64,0,63,10,0,0
Data 1047,1047,20000,5,0,0,64,0,63,10,0,0
Data 1175,1175,20000,5,0,0,64,0,63,10,0,0
Data 1397,1397,20000,5,0,0,64,0,63,10,0,0
Data 1568,1568,20000,5,0,0,64,0,63,10,0,0
Data 1865,1865,20000,5,0,0,64,0,63,10,0,0
Data 2093,2093,20000,5,0,0,64,0,63,10,0,0
Data 2489,2489,20000,5,0,0,64,0,63,10,0,0
Data 2959,2959,20000,5,0,0,64,0,63,10,0,0
Data 160,160,32767,7,0,0,64,0,63,10,0,0
Data 170,170,32767,7,0,0,64,0,63,10,0,0
Data 160,160,32767,7,0,0,64,0,63,10,0,0
Data 170,170,32767,7,0,0,64,0,63,10,0,0
Data 181,181,32767,7,0,0,64,0,63,10,0,0
Data 192,192,32767,7,0,0,64,0,63,10,0,0
Data 204,204,32767,7,0,0,64,0,63,10,0,0
Data 216,216,32767,7,0,0,64,0,63,10,0,0
Data 230,230,32767,7,0,0,64,0,63,10,0,0
Data 244,244,32767,7,0,0,64,0,63,10,0,0
Data 259,259,32767,7,0,0,64,0,63,10,0,0
Data 275,275,32767,7,0,0,64,0,63,10,0,0
Data 292,292,32767,7,0,0,64,0,63,10,0,0
Data 310,310,32767,7,0,0,64,0,63,10,0,0
Data 329,329,32767,7,0,0,64,0,63,10,0,0
Data 349,349,32767,7,0,0,64,0,63,10,0,0
Data 370,370,32767,7,0,0,64,0,63,10,0,0
Data 392,392,32767,7,0,0,64,0,63,10,0,0
Data 415,415,32767,7,0,0,64,0,63,10,0,0
Data 440,440,32767,7,0,0,64,0,63,10,0,0
Data 466,466,32767,7,0,0,64,0,63,10,0,0
Data 494,494,32767,7,0,0,64,0,63,10,0,0
Data 523,523,32767,7,0,0,64,0,63,10,0,0
Data 554,554,32767,7,0,0,64,0,63,10,0,0
Data 587,587,32767,7,0,0,64,0,63,10,0,0
Data 622,622,32767,7,0,0,64,0,63,10,0,0
Data 659,659,32767,7,0,0,64,0,63,10,0,0
Data 698,698,32767,7,0,0,64,0,63,10,0,0
Data 740,740,32767,7,0,0,64,0,63,10,0,0
Data 784,784,32767,7,0,0,64,0,63,10,0,0
Data 830,830,32767,7,0,0,64,0,63,10,0,0
Data 880,880,32767,7,0,0,64,0,63,10,0,0
Data 932,932,32767,7,0,0,64,0,63,10,0,0
Data 988,988,32767,7,0,0,64,0,63,10,0,0
Data 1047,1047,32767,7,0,0,64,0,63,10,0,0
Data 1109,1109,32767,7,0,0,64,0,63,10,0,0
Data 1175,1175,32767,7,0,0,64,0,63,10,0,0
Data 1245,1245,32767,7,0,0,64,0,63,10,0,0
Data 1319,1319,32767,7,0,0,64,0,63,10,0,0
Data 1397,1397,32767,7,0,0,64,0,63,10,0,0
Data 1480,1480,32767,7,0,0,64,0,63,10,0,0
Data 1568,1568,32767,7,0,0,64,0,63,10,0,0
Data 1661,1661,32767,7,0,0,64,0,63,10,0,0
Data 1760,1760,32767,7,0,0,64,0,63,10,0,0
Data 1865,1865,32767,7,0,0,64,0,63,10,0,0
Data 1976,1976,32767,7,0,0,64,0,63,10,0,0
Data 2093,2093,32767,7,0,0,64,0,63,10,0,0
Data 2217,2217,32767,7,0,0,64,0,63,10,0,0
Data 2349,2349,32767,7,0,0,64,0,63,10,0,0
Data 2489,2489,32767,7,0,0,64,0,63,10,0,0
Data 2637,2637,32767,7,0,0,64,0,63,10,0,0
Data 2794,2794,32767,7,0,0,64,0,63,10,0,0
Data 2959,2959,32767,7,0,0,64,0,63,10,0,0
Data 3136,3136,32767,7,0,0,64,0,63,10,0,0
Data 3322,3322,32767,7,0,0,64,0,63,10,0,0
Data 3520,3520,32767,7,0,0,64,0,63,10,0,0
Data 3729,3729,32767,7,0,0,64,0,63,10,0,0
Data 3951,3951,32767,7,0,0,64,0,63,10,0,0

Dim frequency As Integer
Dim duration As Integer
Dim amplitude As Integer
For iLoop = 1 To 131
    frequency = arrPhoneme(iLoop).frequency1
    duration = arrPhoneme(iLoop).duration
    amplitude = arrPhoneme(iLoop).amplitude

    Print _Trim$(Str$(iLoop)) + ". " + arrPhName(iLoop) + " : " + _
        "frequency=" + _TRIM$(str$(frequency)) + ", " + _
        "duraton=" + _TRIM$(str$(duration)) + ", " + _
        "amplitude=" + _TRIM$(str$(amplitude))
   
    PlaySound frequency, duration, amplitude
Next iLoop

_Delay 0.2 ' Add a short delay after speaking, important for some sound cards.

End

Sub PlaySound (frequency As Integer, duration As Integer, amplitude As Integer)
    Dim amplitude1 As Single
    Dim duration1 As Single
    Dim SampleRate&
    Dim FRate As Single
    Dim SndLoop!

    ' input value of frequency is 160-3951
    ' (not sure if we need to convert it for _SNDRAW)

    ' input duration1 is probably 1/18th second ?
    ' do we need to convert this to some other value?

    ' input value of amplitude is 0-32767
    ' convert to amplitude1 which must be from -1.0 to 1.0
    amplitude1 = (2 * (amplitude / 32767)) - 1
    'amplitude1 = .3 ' amplitude of the signal from -1.0 to 1.0

    SampleRate& = _SndRate ' sets the sample rate
    FRate = frequency / SampleRate&
   
    Print "    PlaySound values: "
    Print "        amplitude1=" + _Trim$(Str$(amplitude1))
    Print "        SampleRate&=" + _Trim$(Str$(SampleRate&))
    Print "        FRate=" + _Trim$(Str$(FRate))
    Print "        Pi2=" + _Trim$(Str$(Pi2))
   
    SndLoop! = 0
    Do While SndLoop! < SampleRate&
        '_SNDRAW SIN((2 * 4 * ATN(1) * SndLoop! / SampleRate&) * frq!) * EXP(-(SndLoop! / SampleRate&) * 3)
        '_SNDRAW amplitude1 * SIN(Pi2 * Duration * FRate) 'sine wave
        _SndRaw amplitude1 * Sgn(Sin(Pi2 * duration * FRate)) ' square wave
        SndLoop! = SndLoop! + 1
    Loop

    _SndRawDone
    Do: Loop While _SndRawLen 'flush the sound playing buffer

End Sub ' PlaySound