Android Question How to call java Interface?

watesoft

Active Member
Licensed User
Longtime User
In a Java class, there is an interface for TTS callback. How can I use this interface with Javaobject method?

OfflineTtsCallback.java:
package com.k2fsa.sherpa.onnx;

@FunctionalInterface
public interface OfflineTtsCallback {
    Integer invoke(float[] samples);
}

In B4A, I use the following method, but always encounter errors.

CreateCallback:
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_Invoke(samples() As Float) As Int
    Try
        If stopped = False Then
            PlayPCM(samples)
            Return 1
        Else
            If audioTrack.IsInitialized Then
                audioTrack.RunMethod("stop", Null)
            End If
            Return 0
        End If
    Catch
        Log("Error in OnSamples: " & LastException)
        Return 0
    End Try
End Sub
 
Last edited:

teddybear

Well-Known Member
Licensed User
The event sub signature is wrong. it should look like this
B4X:
Sub ttsCallback_Event(MethodName As String,Args() As Object) As Object
 
Upvote 0

watesoft

Active Member
Licensed User
Longtime User
The event sub signature is wrong. it should look like this
B4X:
Sub ttsCallback_Event(MethodName As String,Args() As Object) As Object
Thank you. I made some modifications according to your suggestions.,but it not work. Actually, I don't know Java, and I'm not familiar with B4XJavaObject either. I have already tried many methods through AI before., and the following errors always occur during operation.
java.lang.RuntimeException: Method: generateWithCallback not matched.

B4X:
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
 
Upvote 0

teddybear

Well-Known Member
Licensed User
You need to understand how java works. at least these java codes you would like to call can work on java env
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Method: generateWithCallback not matched.
Usually means that one of the parameters you are passing is the wrong type. Check the documentation and make sure you are passing the correct variable types.

In your case, it looks like you are passing (String, Int, Double, OfflineTtsCallback)
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
I couldn't find javadoc, but looking at this page it looks like the third parameter should be a float, so you would have to do:

B4X:
        Dim callback As JavaObject = CreateCallback
        Dim Val as Float = 1
        tts.RunMethod("generateWithCallback", Array("这是一个测试语音", 0, Val,callback))

Hope it helps.
 
Upvote 0

watesoft

Active Member
Licensed User
Longtime User
I couldn't find javadoc, but looking at this page it looks like the third parameter should be a float, so you would have to do:

B4X:
        Dim callback As JavaObject = CreateCallback
        Dim Val as Float = 1
        tts.RunMethod("generateWithCallback", Array("这是一个测试语音", 0, Val,callback))

Hope it helps.
Thank you very much. Although I still can't hear the playback sound, you have helped me solve a problem that has been bothering me for a long time. The error of "generateWithCallback not matched" no longer appears.
 
Upvote 0
Top