mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-10-09 23:53:28 -04:00
Rockbox Utility currently exposes SAPI5 voice speed but not the SAPI voice volume. The encoder volume setting is applied after synthesis, so it cannot prevent clipping or distortion already present in the generated wave file. This patch adds a Volume control to the SAPI5 TTS configuration. The value is passed to the standard SpVoice.Volume property before synthesis. Change-Id: Ifa43e55f716f98150e94c86e5e6a6c8bce0ca852
352 lines
12 KiB
Text
352 lines
12 KiB
Text
'***************************************************************************
|
|
' __________ __ ___.
|
|
' Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
' Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
' Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
' Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
' \/ \/ \/ \/ \/
|
|
'
|
|
' Copyright (C) 2007 Steve Bavin, Jens Arnold, Mesar Hameed
|
|
'
|
|
' All files in this archive are subject to the GNU General Public License.
|
|
' See the file COPYING in the source tree root for full license agreement.
|
|
'
|
|
' This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
' KIND, either express or implied.
|
|
'
|
|
'***************************************************************************
|
|
|
|
Option Explicit
|
|
|
|
Const SSFMCreateForWrite = 3
|
|
|
|
' Audio formats for SAPI5 filestream object
|
|
Const SPSF_8kHz16BitMono = 6
|
|
Const SPSF_11kHz16BitMono = 10
|
|
Const SPSF_12kHz16BitMono = 14
|
|
Const SPSF_16kHz16BitMono = 18
|
|
Const SPSF_22kHz16BitMono = 22
|
|
Const SPSF_24kHz16BitMono = 26
|
|
Const SPSF_32kHz16BitMono = 30
|
|
Const SPSF_44kHz16BitMono = 34
|
|
Const SPSF_48kHz16BitMono = 38
|
|
|
|
Const STDIN = 0
|
|
Const STDOUT = 1
|
|
Const STDERR = 2
|
|
|
|
Dim oShell, oArgs, oEnv
|
|
Dim oFSO, oStdIn, oStdOut
|
|
Dim bVerbose, bList
|
|
Dim bMSSP
|
|
Dim sLanguage, sVoice, sSpeed, sVolume, sName, sVendor
|
|
|
|
Dim oSpVoice, oSpFS ' SAPI5 voice and filestream
|
|
Dim oVoice ' for traversing the list of voices
|
|
Dim nLangID, sSelectString
|
|
|
|
Dim aLine, aData ' used in command reading
|
|
Dim nError, sError ' error returned to the controlling process
|
|
|
|
On Error Resume Next
|
|
|
|
Set oFSO = CreateObject("Scripting.FileSystemObject")
|
|
Set oStdIn = oFSO.GetStandardStream(STDIN, true)
|
|
Set oStdOut = oFSO.GetStandardStream(STDOUT, true)
|
|
|
|
Set oShell = CreateObject("WScript.Shell")
|
|
Set oEnv = oShell.Environment("Process")
|
|
bVerbose = (oEnv("V") <> "")
|
|
|
|
Set oArgs = WScript.Arguments.Named
|
|
bMSSP = oArgs.Exists("mssp")
|
|
bList = oArgs.Exists("listvoices")
|
|
sLanguage = oArgs.Item("language")
|
|
sVoice = oArgs.Item("voice")
|
|
sSpeed = oArgs.Item("speed")
|
|
sVolume = oArgs.Item("volume")
|
|
|
|
' Create SAPI5 object
|
|
If bMSSP Then
|
|
Set oSpVoice = CreateObject("speech.SpVoice")
|
|
Else
|
|
Set oSpVoice = CreateObject("SAPI.SpVoice")
|
|
End If
|
|
If Err.Number <> 0 Then
|
|
WScript.StdErr.WriteLine "Error " & Err.Number _
|
|
& " - could not get SpVoice object." _
|
|
& " SAPI 5 not installed?"
|
|
WScript.Quit 1
|
|
End If
|
|
|
|
If bList Then
|
|
' Just list available voices for the selected language
|
|
For Each nLangID in LangIDs(sLanguage)
|
|
sSelectString = "Language=" & Hex(nLangID)
|
|
For Each oVoice in oSpVoice.GetVoices(sSelectString)
|
|
WScript.StdErr.Write oVoice.GetAttribute("Name") & ";"
|
|
Next
|
|
Next
|
|
WScript.StdErr.WriteLine
|
|
WScript.Quit 0
|
|
End If
|
|
|
|
' Select matching voice
|
|
For Each nLangID in LangIDs(sLanguage)
|
|
sSelectString = "Language=" & Hex(nLangID)
|
|
If sVoice <> "" Then
|
|
sSelectString = sSelectString & ";Name=" & sVoice
|
|
End If
|
|
Set oSpVoice.Voice = oSpVoice.GetVoices(sSelectString).Item(0)
|
|
If Err.Number = 0 Then
|
|
sName = oSpVoice.Voice.GetAttribute("Name")
|
|
If bVerbose Then
|
|
WScript.StdErr.WriteLine "Using " & sName & " for " & sSelectString
|
|
End If
|
|
Exit For
|
|
Else
|
|
sSelectString = ""
|
|
Err.Clear
|
|
End If
|
|
Next
|
|
If sSelectString = "" Then
|
|
WScript.StdErr.WriteLine "Error - found no matching voice for " _
|
|
& sLanguage & ", " & sVoice
|
|
WScript.Quit 1
|
|
End If
|
|
|
|
' Speed selection
|
|
If sSpeed <> "" Then oSpVoice.Rate = sSpeed
|
|
' Volume selection
|
|
If sVolume <> "" Then oSpVoice.Volume = sVolume
|
|
' Get vendor information, protect from missing attribute
|
|
sVendor = oSpVoice.Voice.GetAttribute("Vendor")
|
|
If Err.Number <> 0 Then
|
|
Err.Clear
|
|
sVendor = "(unknown)"
|
|
' Some L&H engines don't set the vendor attribute - check the name
|
|
If Len(sName) > 3 And Left(sName, 3) = "LH " Then
|
|
sVendor = "L&H"
|
|
End If
|
|
End If
|
|
|
|
' Filestream object for output
|
|
Set oSpFS = CreateObject("SAPI.SpFileStream")
|
|
oSpFS.Format.Type = AudioFormat(sVendor)
|
|
|
|
Do
|
|
aLine = Split(oStdIn.ReadLine, vbTab, 2)
|
|
If Err.Number <> 0 Then
|
|
WScript.StdErr.WriteLine "Error " & Err.Number & ": " & Err.Description
|
|
WScript.Quit 1
|
|
End If
|
|
Select Case aLine(0) ' command
|
|
Case "QUERY"
|
|
Select Case aLine(1)
|
|
Case "VENDOR"
|
|
oStdOut.WriteLine sVendor
|
|
End Select
|
|
Case "SPEAK"
|
|
aData = Split(aLine(1), vbTab, 2)
|
|
If bVerbose Then WScript.StdErr.WriteLine "Saying " & aData(1) _
|
|
& " in " & aData(0)
|
|
Err.Clear
|
|
oSpFS.Open aData(0), SSFMCreateForWrite, false
|
|
nError = Err.Number
|
|
sError = Err.Description
|
|
If nError = 0 Then
|
|
Set oSpVoice.AudioOutputStream = oSpFS
|
|
oSpVoice.Speak aData(1)
|
|
nError = Err.Number
|
|
sError = Err.Description
|
|
oSpFS.Close
|
|
If nError = 0 And Err.Number <> 0 Then
|
|
nError = Err.Number
|
|
sError = Err.Description
|
|
End If
|
|
End If
|
|
If nError <> 0 Then
|
|
oStdOut.WriteLine "ERROR" & vbTab & nError & ": " & sError
|
|
Err.Clear
|
|
ElseIf Not oFSO.FileExists(aData(0)) Then
|
|
oStdOut.WriteLine "ERROR" & vbTab & "SAPI reported success but created no wave file"
|
|
End If
|
|
Case "EXEC"
|
|
If bVerbose Then WScript.StdErr.WriteLine "> " & aLine(1)
|
|
oShell.Run aLine(1), 0, true
|
|
If Err.Number <> 0 Then
|
|
If Not bVerbose Then
|
|
WScript.StdErr.Write "> " & aLine(1) & ": "
|
|
End If
|
|
If Err.Number = &H80070002 Then ' Actually file not found
|
|
WScript.StdErr.WriteLine "command not found"
|
|
Else
|
|
WScript.StdErr.WriteLine "error " & Err.Number & ":" _
|
|
& Err.Description
|
|
End If
|
|
WScript.Quit 2
|
|
End If
|
|
Case "SYNC"
|
|
If bVerbose Then WScript.StdErr.WriteLine "Syncing"
|
|
oStdOut.WriteLine aLine(1) ' Just echo what was passed
|
|
Case "QUIT"
|
|
If bVerbose Then WScript.StdErr.WriteLine "Quitting"
|
|
WScript.Quit 0
|
|
End Select
|
|
Loop
|
|
|
|
' Subroutines
|
|
' -----------
|
|
|
|
' SAPI5 output format selection based on engine
|
|
Function AudioFormat(ByRef sVendor)
|
|
Select Case sVendor
|
|
Case "Microsoft"
|
|
AudioFormat = SPSF_22kHz16BitMono
|
|
Case "AT&T Labs"
|
|
AudioFormat = SPSF_32kHz16BitMono
|
|
Case "Loquendo"
|
|
AudioFormat = SPSF_16kHz16BitMono
|
|
Case "ScanSoft, Inc"
|
|
AudioFormat = SPSF_22kHz16BitMono
|
|
Case "Voiceware"
|
|
AudioFormat = SPSF_16kHz16BitMono
|
|
Case Else
|
|
AudioFormat = SPSF_22kHz16BitMono
|
|
WScript.StdErr.WriteLine "Warning - unknown vendor """ & sVendor _
|
|
& """ - using default wave format"
|
|
End Select
|
|
End Function
|
|
|
|
' Language mapping rockbox->windows
|
|
Function LangIDs(ByRef sLanguage)
|
|
Dim aIDs
|
|
|
|
Select Case sLanguage
|
|
Case "afrikaans"
|
|
LangIDs = Array(&h436)
|
|
Case "arabic"
|
|
LangIDs = Array( &h401, &h801, &hc01, &h1001, &h1401, &h1801, _
|
|
&h1c01, &h2001, &h2401, &h2801, &h2c01, &h3001, _
|
|
&h3401, &h3801, &h3c01, &h4001)
|
|
' Saudi Arabia, Iraq, Egypt, Libya, Algeria, Morocco, Tunisia,
|
|
' Oman, Yemen, Syria, Jordan, Lebanon, Kuwait, U.A.E., Bahrain,
|
|
' Qatar
|
|
Case "basque"
|
|
LangIDs = Array(&h42d)
|
|
Case "bulgarian"
|
|
LangIDs = Array(&h402)
|
|
Case "catala"
|
|
LangIDs = Array(&h403)
|
|
Case "chinese-simp"
|
|
LangIDs = Array(&h804) ' PRC
|
|
Case "chinese-trad"
|
|
LangIDs = Array(&h404) ' Taiwan. Perhaps also Hong Kong, Singapore, Macau?
|
|
Case "czech"
|
|
LangIDs = Array(&h405)
|
|
Case "dansk"
|
|
LangIDs = Array(&h406)
|
|
Case "deutsch"
|
|
LangIDs = Array(&h407, &hc07, &h1007, &h1407)
|
|
' Standard, Austrian, Luxembourg, Liechtenstein (Swiss -> wallisertitsch)
|
|
Case "eesti"
|
|
LangIDs = Array(&h425)
|
|
Case "english-us"
|
|
LangIDs = Array( &h409, &h809, &hc09, &h1009, &h1409, &h1809, _
|
|
&h1c09, &h2009, &h2409, &h2809, &h2c09, &h3009, _
|
|
&h3409)
|
|
' American, British, Australian, Canadian, New Zealand, Ireland,
|
|
' South Africa, Jamaika, Caribbean, Belize, Trinidad, Zimbabwe,
|
|
' Philippines
|
|
Case "english"
|
|
LangIDs = Array( &h809, &h409, &hc09, &h1009, &h1409, &h1809, _
|
|
&h1c09, &h2009, &h2409, &h2809, &h2c09, &h3009, _
|
|
&h3409)
|
|
' British, American, Australian, Canadian, New Zealand, Ireland,
|
|
' South Africa, Jamaika, Caribbean, Belize, Trinidad, Zimbabwe,
|
|
' Philippines
|
|
Case "espanol"
|
|
LangIDs = Array( &h40a, &hc0a, &h80a, &h100a, &h140a, &h180a, _
|
|
&h1c0a, &h200a, &h240a, &h280a, &h2c0a, &h300a, _
|
|
&h340a, &h380a, &h3c0a, &h400a, &h440a, &h480a, _
|
|
&h4c0a, &h500a)
|
|
' trad. sort., mordern sort., Mexican, Guatemala, Costa Rica,
|
|
' Panama, Dominican Republic, Venezuela, Colombia, Peru, Argentina,
|
|
' Ecuador, Chile, Uruguay, Paraguay, Bolivia, El Salvador,
|
|
' Honduras, Nicaragua, Puerto Rico
|
|
Case "esperanto"
|
|
WScript.StdErr.WriteLine "Error: no esperanto support in Windows"
|
|
WScript.Quit 1
|
|
Case "finnish"
|
|
LangIDs = Array(&h40b)
|
|
Case "francais"
|
|
LangIDs = Array(&h40c, &hc0c, &h100c, &h140c, &h180c)
|
|
' Standard, Canadian, Swiss, Luxembourg, Monaco (Belgian -> walon)
|
|
Case "galego"
|
|
LangIDs = Array(&h456)
|
|
Case "greek"
|
|
LangIDs = Array(&h408)
|
|
Case "hebrew"
|
|
LangIDs = Array(&h40d)
|
|
Case "hindi"
|
|
LangIDs = Array(&h439)
|
|
Case "hrvatski"
|
|
LangIDs = Array(&h41a, &h101a) ' Croatia, Bosnia and Herzegovina
|
|
Case "islenska"
|
|
LangIDs = Array(&h40f)
|
|
Case "italiano"
|
|
LangIDs = Array(&h410, &h810) ' Standard, Swiss
|
|
Case "japanese"
|
|
LangIDs = Array(&h411)
|
|
Case "korean"
|
|
LangIDs = Array(&h412)
|
|
Case "latviesu"
|
|
LangIDs = Array(&h426)
|
|
Case "lietuviu"
|
|
LangIDs = Array(&h427)
|
|
Case "magyar"
|
|
LangIDs = Array(&h40e)
|
|
Case "moldoveneste"
|
|
LangIDs = Array(&h818)
|
|
Case "nederlands"
|
|
LangIDs = Array(&h413, &h813) ' Standard, Belgian
|
|
Case "norsk-nynorsk"
|
|
LangIDs = Array(&h814)
|
|
Case "norsk"
|
|
LangIDs = Array(&h414) ' Bokmal
|
|
Case "polski"
|
|
LangIDs = Array(&h415)
|
|
Case "portugues-brasileiro"
|
|
LangIDs = Array(&h416)
|
|
Case "portugues"
|
|
LangIDs = Array(&h816)
|
|
Case "romaneste"
|
|
LangIDs = Array(&h418)
|
|
Case "russian"
|
|
LangIDs = Array(&h419)
|
|
Case "slovak"
|
|
LangIDs = Array(&h41B)
|
|
Case "slovenscina"
|
|
LangIDs = Array(&h424)
|
|
Case "srpski"
|
|
LangIDs = Array(&hc1a) ' Cyrillic
|
|
Case "svenska"
|
|
LangIDs = Array(&h41d, &h81d) ' Standard, Finland
|
|
Case "tagalog"
|
|
LangIDs = Array(&h464) ' Filipino, might not be 100% correct
|
|
Case "thai"
|
|
LangIDs = Array(&h41e)
|
|
Case "turkce"
|
|
LangIDs = Array(&h41f)
|
|
Case "ukrainian"
|
|
LangIDs = Array(&h422)
|
|
Case "vietnamese"
|
|
LangIDs = Array(&h42a)
|
|
Case "wallisertitsch"
|
|
LangIDs = Array(&h807) ' Swiss German
|
|
Case "walon"
|
|
LangIDs = Array(&h80c) ' Belgian French
|
|
End Select
|
|
End Function
|
|
|
|
|