Flutter文本转语音插件openjtalk的使用

Flutter文本转语音插件openjtalk的使用

要求

  • ffi ^2.0.1

什么是Open JTalk?

Open JTalk 是一个日语文本转语音系统。 该软件根据修改后的BSD许可发布。

动态库

如果你需要使用库,请参考以下站点。

  • jtalk.dll (Windows)
  • libjtalk.so (Android, Linux, Fuchsia)
  • libjtalk.dylib (iOS, macOS)

作者

yamahara


完整示例Demo

import 'package:openjtalk/openjtalk.dart';

Future<void> main() async {
  // 初始化Open JTalk实例
  var talk = await openjtalkInitialize(
    'mei_normal', // 声音类型
    'assets/dic_utf_8', // 字典路径
    'assets/voice' // 声音文件路径
  );

  // 打印一些信息
  print(talk.getVoice()); // 获取当前使用的声库
  print(talk.getSamplingFrequency()); // 获取采样频率
  print(talk.getSpeed()); // 获取发音速度
  print(talk.getVoiceDir()); // 获取声音目录
  print(talk.getVoicePath()); // 获取声音文件路径
  print(talk.getVoiceName()); // 获取声音名称

  // 异步播放语音
  talk.speakAsync('闻到吗?');

  // 等待语音播放完毕
  while (talk.isSpeaking() != 0) {}

  // 清理资源
  openjtalkClear(talk);
}

更多关于Flutter文本转语音插件openjtalk的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter文本转语音插件openjtalk的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter应用中使用openjtalk进行文本转语音(Text-to-Speech, TTS)的示例代码。需要注意的是,openjtalk本身是一个C++库,因此在Flutter中直接使用它需要通过一些平台通道(如MethodChannel)或者通过封装成原生模块来进行调用。

由于直接在Flutter中调用C++库比较复杂,这里提供一个通过MethodChannel调用原生Android和iOS代码来实现文本转语音的示例。由于openjtalk在Android和iOS上的集成方式有所不同,这里将分别展示。

1. 设置Flutter项目

首先,创建一个新的Flutter项目:

flutter create flutter_openjtalk_example
cd flutter_openjtalk_example

2. 在Android上集成openjtalk

a. 添加openjtalk到Android项目

  1. 下载openjtalk的Android库或者编译后的二进制文件,并将其放置在android/app/src/main/jniLibs/目录下。

  2. 修改android/app/build.gradle文件,添加对ndk的支持(如果尚未添加):

android {
    ...
    defaultConfig {
        ...
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}
  1. android/app/目录下创建一个CMakeLists.txt文件,配置openjtalk的编译选项(这里假设你已经有了预编译的库,所以不需要从源代码编译):
cmake_minimum_required(VERSION 3.4.1)

add_library( # Sets the name of the library.
             openjtalk

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             IMPORTED )

set_target_properties(openjtalk PROPERTIES IMPORTED_LOCATION
            ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopenjtalk.so)

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

target_link_libraries( # Specifies the target library.
                       openjtalk

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

b. 创建MethodChannel调用openjtalk

android/app/src/main/kotlin/com/example/flutter_openjtalk_example/(或者对应的Java目录)下创建一个新的Kotlin/Java类,例如OpenJTalkPlugin.kt

package com.example.flutter_openjtalk_example

import android.content.Context
import android.os.AsyncTask
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import java.io.File

class OpenJTalkPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
    private var context: Context? = null
    private var channel: MethodChannel? = null

    override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPluginBinding) {
        context = flutterPluginBinding.applicationContext
        channel = MethodChannel(flutterPluginBinding.binaryMessenger, "openjtalk_channel")
        channel?.setMethodCallHandler(this)
    }

    override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
        if (call.method == "speak") {
            val text = call.argument<String>("text") ?: ""
            SpeakTask(text, result).execute()
        } else {
            result.notImplemented()
        }
    }

    override fun onDetachedFromEngine(@NonNull binding: FlutterPluginBinding) {
        channel?.setMethodCallHandler(null)
        channel = null
        context = null
    }

    override fun onAttachedToActivity(binding: ActivityPluginBinding) {}

    override fun onDetachedFromActivityForConfigChanges() {}

    override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {}

    override fun onDetachedFromActivity() {}

    private inner class SpeakTask(private val text: String, private val result: Result) : AsyncTask<Void, Void, Void>() {
        override fun doInBackground(vararg params: Void?): Void? {
            // Here you would call the openjtalk binary or library method to speak the text
            // For simplicity, let's just simulate it
            // In a real implementation, you would execute the openjtalk command or call the appropriate Java method if wrapped in a Java library
            val command = arrayOf("/path/to/openjtalk_binary", "-x", "/path/to/dic", text)
            // Execute the command (this is just an example, you need to actually execute it and handle the process)
            // val process = Runtime.getRuntime().exec(command)
            // process.waitFor()
            return null
        }

        override fun onPostExecute(result: Void?) {
            super.onPostExecute(result)
            // Return success or error based on the execution result
            this@OpenJTalkPlugin.result.success("Text spoken")
        }
    }
}

注意:上述代码中的/path/to/openjtalk_binary/path/to/dic需要替换为实际的openjtalk二进制文件路径和字典文件路径。

c. 注册插件

android/app/src/main/kotlin/com/example/flutter_openjtalk_example/MainActivity.kt中注册插件(如果使用的是Kotlin):

package com.example.flutter_openjtalk_example

import io.flutter.embedding.android.FlutterActivity

class MainActivity: FlutterActivity() {
}

或者在MainActivity.java中(如果使用的是Java):

package com.example.flutter_openjtalk_example;

import io.flutter.embedding.android.FlutterActivity;

public class MainActivity extends FlutterActivity {
}

并在Application类中注册插件(可选,但推荐):

package com.example.flutter_openjtalk_example

import io.flutter.embedding.android.FlutterApplication

class MyApplication: FlutterApplication() {
    override fun onCreate() {
        super.onCreate()
        OpenJTalkPlugin().registerWith(registrarFor("com.example.flutter_openjtalk_example/OpenJTalkPlugin"))
    }
}

别忘了在AndroidManifest.xml中指定Application类:

<application
    android:name=".MyApplication"
    ... >
    ...
</application>

3. 在iOS上集成openjtalk

由于openjtalk在iOS上通常需要通过Objective-C/C++桥接来实现,这里提供一个简化的示例,说明如何在iOS原生代码中调用openjtalk,然后通过MethodChannel暴露给Flutter。

a. 添加openjtalk到iOS项目

  1. 下载并编译openjtalk的iOS库,或者找到预编译的库文件。

  2. 将库文件添加到ios/Runner/项目中的Frameworks, Libraries, and Embedded Content

  3. ios/Runner/Runner-Bridging-Header.h中添加对openjtalk头文件的引用(如果使用的是Swift和Objective-C混合开发)。

b. 创建MethodChannel调用openjtalk

ios/Runner/目录下创建一个新的Objective-C类,例如OpenJTalkPlugin.m

#import <Flutter/Flutter.h>
#import "OpenJTalkPlugin.h"

[@implementation](/user/implementation) OpenJTalkPlugin

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
    FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"openjtalk_channel" binaryMessenger:[registrar messenger]];
    OpenJTalkPlugin* instance = [[OpenJTalkPlugin alloc] initWithChannel:channel];
    [channel setMethodCallHandler:[instance methodCallHandler]];
}

- (instancetype)initWithChannel:(FlutterMethodChannel*)channel {
    self = [super init];
    _channel = channel;
    return self;
}

- (FlutterMethodCallHandler)method
回到顶部