Sub Process_Globals
'These global variables will be declared once when the application starts.
'These variables can be accessed from all modules.
Private xui As XUI
Private tts As JavaObject
Private isInitialized As Boolean = False
End Sub
Sub Globals
Dim audioTrack As JavaObject
Dim stopped As Boolean
End Sub
Sub Activity_Create(FirstTime As Boolean)
Activity.LoadLayout("Layout")
If FirstTime Then
InitializeTTS
End If
End Sub
Sub InitializeTTS
Try
Dim matchaConfigBuilder As JavaObject
matchaConfigBuilder.InitializeNewInstance("com.k2fsa.sherpa.onnx.OfflineTtsMatchaModelConfig$Builder", Null)
Dim modelDir As String = File.DirInternal & "/matcha-icefall-zh-baker"
matchaConfigBuilder.RunMethod("setAcousticModel", Array(modelDir & "/model-steps-3.onnx"))
matchaConfigBuilder.RunMethod("setVocoder", Array(modelDir & "/hifigan_v2.onnx"))
matchaConfigBuilder.RunMethod("setLexicon", Array(modelDir & "/lexicon.txt"))
matchaConfigBuilder.RunMethod("setTokens", Array(modelDir & "/tokens.txt"))
'matchaConfigBuilder.RunMethod("setDataDir", Array(""))
matchaConfigBuilder.RunMethod("setDictDir", Array(modelDir & "/dict"))
'matchaConfigBuilder.RunMethod("setNoiseScale", Array(0.667f))
'matchaConfigBuilder.RunMethod("setLengthScale", Array(1.0f))
Dim matchaConfig As JavaObject = matchaConfigBuilder.RunMethod("build", Null)
Dim modelConfigBuilder As JavaObject
modelConfigBuilder.InitializeNewInstance("com.k2fsa.sherpa.onnx.OfflineTtsModelConfig$Builder", Null)
modelConfigBuilder.RunMethod("setMatcha", Array(matchaConfig))
modelConfigBuilder.RunMethod("setNumThreads", Array(2))
'modelConfigBuilder.RunMethod("setProvider", Array("cpu"))
modelConfigBuilder.RunMethod("setDebug", Array(True))
Dim modelConfig As JavaObject = modelConfigBuilder.RunMethod("build", Null)
Dim ttsConfigBuilder As JavaObject
ttsConfigBuilder.InitializeNewInstance("com.k2fsa.sherpa.onnx.OfflineTtsConfig$Builder", Null)
ttsConfigBuilder.RunMethod("setModel", Array(modelConfig))
ttsConfigBuilder.RunMethod("setRuleFsts", Array(modelDir & "/phone.fst," & modelDir & "/date.fst," & modelDir & "/number.fst")) ' 规则FST文件
'ttsConfigBuilder.RunMethod("setRuleFars", Array(""))
'ttsConfigBuilder.RunMethod("setMaxNumSentences", Array(1))
'ttsConfigBuilder.RunMethod("setSilenceScale", Array(0.2f))
Dim ttsConfig As JavaObject = ttsConfigBuilder.RunMethod("build", Null)
tts.InitializeNewInstance("com.k2fsa.sherpa.onnx.OfflineTts", Array(ttsConfig))
isInitialized = True
Log("TTS初始化成功")
Catch
Log("TTS初始化错误: " & LastException)
End Try
End Sub
Sub CreateCallback As Object
Dim callback As JavaObject
callback.InitializeStatic("com.k2fsa.sherpa.onnx.OfflineTtsCallback")
Return callback.CreateEvent("com.k2fsa.sherpa.onnx.OfflineTtsCallback", "ttsCallback", Null)
End Sub
Sub ttsCallback_Event(MethodName As String, Args() As Object) As Object
Try
If MethodName = "invoke" Then
Dim samples() As Float = Args(0)
If stopped = False Then
PlayPCM(samples)
Return 1
Else
If audioTrack.IsInitialized Then
audioTrack.RunMethod("stop", Null)
End If
Return 0
End If
End If
Catch
Log("Error in ttsCallback_Event: " & LastException)
Return 0
End Try
Return Null
End Sub
Sub Button1_Click
Try
Dim sampleRate As Int = tts.RunMethod("getSampleRate", Null)
If audioTrack.IsInitialized = False Then
InitializeAudioTrack(sampleRate)
Else
audioTrack.RunMethod("stop", Null)
audioTrack.RunMethod("release", Null)
InitializeAudioTrack(sampleRate)
End If
Dim callback As JavaObject = CreateCallback
tts.RunMethod("generateWithCallback", Array("这是一个测试语音", 0, 1.0,callback))
Catch
Log("生成语音错误: " & LastException)
End Try
End Sub
Sub PlayPCM(samples() As Float)
Try
If audioTrack.IsInitialized = False Then
InitializeAudioTrack(22050)
Dim shortArray(samples.Length) As Short
For i = 0 To samples.Length - 1
Dim clampedSample As Float = Max(Min(samples(i), 1.0), -1.0)
shortArray(i) = clampedSample * 32767
Next
Dim pcmData(shortArray.Length * 2) As Byte
For i = 0 To shortArray.Length - 1
pcmData(i * 2) = Bit.And(shortArray(i), 0xFF)
pcmData(i * 2 + 1) = Bit.And(Bit.ShiftRight(shortArray(i), 8), 0xFF)
Next
' Write audio data
audioTrack.RunMethod("write", Array(pcmData, 0, pcmData.Length))
If 3<>audioTrack.RunMethod("getPlayState", Null) Then
audioTrack.RunMethod("play", Null)
End If
Catch
Log("Error in PlayPCMChunk: " & LastException)
End Try
End Sub
Sub InitializeAudioTrack(sampleRate As Int)
Try
Dim audioAttributesBuilder As JavaObject
audioAttributesBuilder.InitializeNewInstance("android.media.AudioAttributes$Builder", Null)
audioAttributesBuilder.RunMethod("setUsage", Array(1)) ' USAGE_MEDIA
audioAttributesBuilder.RunMethod("setContentType", Array(2)) ' CONTENT_TYPE_MUSIC
Dim audioAttributes As JavaObject = audioAttributesBuilder.RunMethod("build", Null)
' Create AudioFormat
Dim audioFormatBuilder As JavaObject
audioFormatBuilder.InitializeNewInstance("android.media.AudioFormat$Builder", Null)
audioFormatBuilder.RunMethod("setSampleRate", Array(sampleRate))
audioFormatBuilder.RunMethod("setChannelMask", Array(4)) ' CHANNEL_OUT_MONO
audioFormatBuilder.RunMethod("setEncoding", Array(2)) ' ENCODING_PCM_16BIT
Dim audioFormat As JavaObject = audioFormatBuilder.RunMethod("build", Null)
' Calculate buffer size using AudioTrack class
Dim audioTrackClass As JavaObject
audioTrackClass.InitializeStatic("android.media.AudioTrack")
Dim minBufferSize As Int = audioTrackClass.RunMethod("getMinBufferSize", Array(sampleRate, 4, 2))
If minBufferSize = -1 Or minBufferSize = -2 Then
Log("Invalid buffer size")
Return
End If
' Initialize AudioTrack
audioTrack.InitializeNewInstance("android.media.AudioTrack", Array( _
audioAttributes, _ ' AudioAttributes
audioFormat, _ ' AudioFormat
minBufferSize, _ ' Buffer size
1, _ ' MODE_STATIC
0 _ ' Session ID (0 for default)
))
' Check if AudioTrack is initialized
Dim state As Int = audioTrack.RunMethod("getState", Null)
If state <> 1 Then ' STATE_INITIALIZED
Log("AudioTrack initialization failed")
Return
End If
Catch
Log("Error in InitializeAudioTrack: " & LastException)
End Try
End Sub