You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

603 lines
15 KiB

import 'dart:async';
import 'package:flutter/services.dart';
/// ASR 事件类型
enum AsrEventType {
/// 会话开始
sessionStarted,
/// 会话结束
sessionStopped,
/// 正在识别(中间结果)
recognizing,
/// 识别结果(最终结果)
result,
/// 音量变化
volumeChanged,
/// 错误
error
}
/// TTS 工作模式
enum TtsWorkMode {
/// 在线合成
online,
/// 离线合成
offline,
/// 同时在线离线
both,
/// 先在线再离线(网络不好时自动切换)
alternate,
/// 文件模式
file
}
/// TTS 文本类型
enum TtsTextType {
/// 纯文本
plain,
/// SSML格式
ssml
}
/// 协议类型
enum ProtocolType {
/// 默认协议
defaultProtocol,
/// Seed协议(用于大模型)
seed
}
/// TTS 播放进度事件
class TtsProgressEvent {
/// 播放进度 0.0-1.0
final double progress;
/// 请求ID
final String reqId;
const TtsProgressEvent({
required this.progress,
required this.reqId,
});
@override
String toString() {
return 'TtsProgressEvent{progress: $progress, reqId: $reqId}';
}
}
/// ASR 事件
class AsrEvent {
/// 事件类型
final AsrEventType type;
/// 识别文本(仅在recognizing和result类型时有效)
final String? text;
/// 识别语言(仅在recognizing和result类型时有效)
final String? language;
/// 音量值(仅在volumeChanged类型时有效)
final int? volume;
/// 错误信息(仅在error类型时有效)
final String? errorMessage;
const AsrEvent({
required this.type,
this.text,
this.language,
this.volume,
this.errorMessage,
});
factory AsrEvent.fromMap(Map<dynamic, dynamic> map) {
final typeStr = map['type'] as String;
AsrEventType type;
switch (typeStr) {
case 'sessionStarted':
type = AsrEventType.sessionStarted;
break;
case 'sessionStopped':
type = AsrEventType.sessionStopped;
break;
case 'recognizing':
type = AsrEventType.recognizing;
break;
case 'result':
type = AsrEventType.result;
break;
case 'volumeChanged':
type = AsrEventType.volumeChanged;
break;
case 'error':
type = AsrEventType.error;
break;
default:
throw ArgumentError('未知的事件类型: $typeStr');
}
return AsrEvent(
type: type,
text: map['text'] as String?,
language: map['language'] as String?,
volume: map['volume'] as int?,
errorMessage: map['message'] as String?,
);
}
@override
String toString() {
return 'AsrEvent{type: $type, text: $text, language: $language, volume: $volume, errorMessage: $errorMessage}';
}
}
/// 火山引擎语音服务插件
class VolcanoSpeech {
static final VolcanoSpeechAsr asr = VolcanoSpeechAsr._();
static final VolcanoSpeechTts tts = VolcanoSpeechTts._();
/// 释放资源
static Future<void> dispose() async {
await asr.dispose();
await tts.dispose();
}
}
/// 火山引擎语音识别服务
class VolcanoSpeechAsr {
static const MethodChannel _channel = MethodChannel('volcano_speech/asr');
static const EventChannel _eventChannel = EventChannel('volcano_speech/asr_events');
/// ASR事件流控制器
static final StreamController<AsrEvent> _eventStreamController = StreamController<AsrEvent>.broadcast();
/// ASR事件流
Stream<AsrEvent> get events => _eventStreamController.stream;
/// 是否已初始化事件监听
bool _eventListenerInitialized = false;
VolcanoSpeechAsr._() {
_initEventListener();
}
/// 初始化ASR事件监听
void _initEventListener() {
if (_eventListenerInitialized) return;
_eventChannel.receiveBroadcastStream().listen((dynamic event) {
if (event is Map) {
_eventStreamController.add(AsrEvent.fromMap(event));
}
});
_eventListenerInitialized = true;
}
/// 获取平台版本信息
Future<String?> getPlatformVersion() async {
return await _channel.invokeMethod('getPlatformVersion');
}
/// 初始化语音识别引擎
///
/// [appId] 火山引擎AppID
/// [apiKey] 火山引擎ApiKey
/// [supportedLanguages] 支持的语言列表
/// [useBigModel] 是否使用大模型识别
/// [resourceId] 大模型资源ID(仅当useBigModel为true时有效)
Future<bool> initialize({
required String appId,
required String apiKey,
List<String> supportedLanguages = const ['zh-CN'],
bool useBigModel = false,
String resourceId = '',
}) async {
return await _channel.invokeMethod('initialize', {
'appId': appId,
'apiKey': apiKey,
'supportedLanguages': supportedLanguages,
'useBigModel': useBigModel,
'resourceId': resourceId,
}) ?? false;
}
/// 设置识别语言
///
/// [language] 语言代码,例如 zh-CN、en-US
Future<bool> setLanguage(String language) async {
return await _channel.invokeMethod('setLanguage', {
'language': language,
}) ?? false;
}
/// 设置热词
///
/// [hotWords] 热词JSON字符串,例如 {"hotwords":[{"word":"快速入门","scale":2.0}]}
Future<bool> setHotWords(String hotWords) async {
return await _channel.invokeMethod('setHotWords', {
'hotWords': hotWords,
}) ?? false;
}
/// 设置ASR请求参数
///
/// [params] 请求参数JSON字符串
Future<bool> setRequestParams(String params) async {
return await _channel.invokeMethod('setRequestParams', {
'params': params,
}) ?? false;
}
/// 启用语音停顿、分句、分词信息输出
///
/// [enable] 是否启用
Future<bool> setShowUtterances(bool enable) async {
return await _channel.invokeMethod('setShowUtterances', {
'enable': enable,
}) ?? false;
}
/// 一次性识别(直到说话结束)
///
/// 返回识别结果文本和语言
Future<Map<String, String>> recognizeOnce() async {
final result = await _channel.invokeMethod('recognizeOnce');
return {
'text': result['text'] ?? '',
'language': result['language'] ?? 'zh-CN',
};
}
/// 开始连续识别
///
/// 通过[events]流监听识别结果
Future<bool> startContinuousRecognition() async {
return await _channel.invokeMethod('startContinuousRecognition') ?? false;
}
/// 停止连续识别
Future<bool> stopContinuousRecognition() async {
return await _channel.invokeMethod('stopContinuousRecognition') ?? false;
}
/// 检查是否正在连续识别
Future<bool> isContinuousRecognitionActive() async {
return await _channel.invokeMethod('isContinuousRecognitionActive') ?? false;
}
/// 开始长按识别(按下开始,抬起结束)
///
/// 通过[events]流监听识别结果
Future<bool> startListening() async {
return await _channel.invokeMethod('startListening') ?? false;
}
/// 停止长按识别
Future<bool> stopListening() async {
return await _channel.invokeMethod('stopListening') ?? false;
}
/// 释放ASR资源
Future<bool> dispose() async {
return await _channel.invokeMethod('dispose') ?? false;
}
}
/// 火山引擎语音合成服务
class VolcanoSpeechTts {
static const MethodChannel _channel = MethodChannel('volcano_speech/tts');
static const EventChannel _eventChannel = EventChannel('volcano_speech/tts_events');
/// TTS进度事件流控制器
static final StreamController<TtsProgressEvent> _progressStreamController = StreamController<TtsProgressEvent>.broadcast();
/// TTS进度事件流
Stream<TtsProgressEvent> get progressEvents => _progressStreamController.stream;
/// 是否已初始化事件监听
bool _eventListenerInitialized = false;
VolcanoSpeechTts._() {
_initEventListener();
}
/// 初始化TTS事件监听
void _initEventListener() {
if (_eventListenerInitialized) return;
_eventChannel.receiveBroadcastStream().listen((dynamic event) {
if (event is Map) {
final progress = event['progress'] as double?;
final reqId = event['reqId'] as String?;
if (progress != null && reqId != null) {
_progressStreamController.add(TtsProgressEvent(
progress: progress,
reqId: reqId,
));
}
}
});
_eventListenerInitialized = true;
}
/// 获取平台版本信息
Future<String?> getPlatformVersion() async {
return await _channel.invokeMethod('getPlatformVersion');
}
/// 启用大模型TTS
///
/// [enable] 是否启用大模型TTS
/// [resourceId] 资源ID
Future<bool> enableBigModelTts({
required bool enable,
String resourceId = '',
}) async {
return await _channel.invokeMethod('enableBigModelTts', {
'enable': enable,
'resourceId': resourceId,
}) ?? false;
}
/// 初始化语音合成引擎
///
/// [appId] 火山引擎AppID
/// [apiKey] 火山引擎ApiKey
Future<bool> initialize({
required String appId,
required String apiKey,
}) async {
return await _channel.invokeMethod('initialize', {
'appId': appId,
'apiKey': apiKey,
}) ?? false;
}
/// 设置音色
///
/// [voiceName] 音色名称
/// [voiceType] 音色类型,默认 qingxin
Future<bool> setVoice({
required String voiceName,
String voiceType = 'qingxin',
}) async {
return await _channel.invokeMethod('setVoice', {
'voiceName': voiceName,
'voiceType': voiceType,
}) ?? false;
}
/// 设置离线音色
///
/// [voiceName] 音色名称
/// [voiceType] 音色类型,默认 qingxin
Future<bool> setOfflineVoice({
required String voiceName,
String voiceType = 'qingxin',
}) async {
return await _channel.invokeMethod('setOfflineVoice', {
'voiceName': voiceName,
'voiceType': voiceType,
}) ?? false;
}
/// 设置大模型声音ID
///
/// [voiceId] 声音ID
Future<bool> setBigModelVoiceId(String voiceId) async {
return await _channel.invokeMethod('setBigModelVoiceId', {
'voiceId': voiceId,
}) ?? false;
}
/// 设置大模型请求参数
///
/// [params] 请求参数,JSON字符串
Future<bool> setBigModelRequestParams(String params) async {
return await _channel.invokeMethod('setBigModelRequestParams', {
'params': params,
}) ?? false;
}
/// 设置工作模式
///
/// [mode] 工作模式
Future<bool> setWorkMode(TtsWorkMode mode) async {
String modeStr;
switch (mode) {
case TtsWorkMode.online:
modeStr = 'online';
break;
case TtsWorkMode.offline:
modeStr = 'offline';
break;
case TtsWorkMode.both:
modeStr = 'both';
break;
case TtsWorkMode.alternate:
modeStr = 'alternate';
break;
case TtsWorkMode.file:
modeStr = 'file';
break;
}
return await _channel.invokeMethod('setWorkMode', {
'mode': modeStr,
}) ?? false;
}
/// 设置文本类型
///
/// [type] 文本类型,plain或ssml
Future<bool> setTextType(TtsTextType type) async {
String typeStr;
switch (type) {
case TtsTextType.plain:
typeStr = 'plain';
break;
case TtsTextType.ssml:
typeStr = 'ssml';
break;
}
return await _channel.invokeMethod('setTextType', {
'type': typeStr,
}) ?? false;
}
/// 设置是否启用缓存
///
/// [enable] 是否启用
Future<bool> setEnableCache(bool enable) async {
return await _channel.invokeMethod('setEnableCache', {
'enable': enable,
}) ?? false;
}
/// 设置情感
///
/// [emotion] 情感,例如 neutral、happy、angry、sad等
Future<bool> setEmotion(String emotion) async {
return await _channel.invokeMethod('setEmotion', {
'emotion': emotion,
}) ?? false;
}
/// 设置是否启用情感预测
///
/// [enable] 是否启用
Future<bool> setEnableEmotionPredict(bool enable) async {
return await _channel.invokeMethod('setEnableEmotionPredict', {
'enable': enable,
}) ?? false;
}
/// 设置是否启用声音克隆
///
/// [enable] 是否启用
/// [backendCluster] 后端集群
Future<bool> setEnableVoiceClone({
required bool enable,
String backendCluster = '',
}) async {
return await _channel.invokeMethod('setEnableVoiceClone', {
'enable': enable,
'backendCluster': backendCluster,
}) ?? false;
}
/// 设置是否启用回声消除
///
/// [enable] 是否启用
Future<bool> setEnableAEC(bool enable) async {
return await _channel.invokeMethod('setEnableAEC', {
'enable': enable,
}) ?? false;
}
/// 下载离线资源
///
/// [voiceTypes] 音色类型列表
/// [languages] 语言列表
Future<bool> downloadOfflineResource({
List<String> voiceTypes = const ['qingxin'],
List<String> languages = const ['zh-CN'],
}) async {
return await _channel.invokeMethod('downloadOfflineResource', {
'voiceTypes': voiceTypes,
'languages': languages,
}) ?? false;
}
/// 设置语音参数
///
/// [rate] 语速 -500~500
/// [volume] 音量 0~100
/// [pitch] 音调 -500~500
/// [silenceDuration] 静音时长,毫秒
Future<bool> setSpeechParams({
int rate = 0,
int volume = 100,
int pitch = 0,
int silenceDuration = 0,
}) async {
return await _channel.invokeMethod('setSpeechParams', {
'rate': rate,
'volume': volume,
'pitch': pitch,
'silenceDuration': silenceDuration,
}) ?? false;
}
/// 设置音频输出类型
///
/// [outputType] 输出类型,speaker(扬声器),earpiece(听筒),auto(自动)
Future<bool> setAudioOutputType(String outputType) async {
return await _channel.invokeMethod('setAudioOutputType', {
'outputType': outputType,
}) ?? false;
}
/// 合成并播放文本
///
/// [text] 待合成的文本
Future<bool> speakText(String text) async {
return await _channel.invokeMethod('speakText', {
'text': text,
}) ?? false;
}
/// 使用大模型合成并播放文本
///
/// [text] 待合成的文本
/// [voiceId] 声音ID
/// [params] 额外参数,JSON字符串
Future<bool> speakWithBigModel({
required String text,
required String voiceId,
String params = '',
}) async {
return await _channel.invokeMethod('speakWithBigModel', {
'text': text,
'voiceId': voiceId,
'params': params,
}) ?? false;
}
/// 暂停播放
Future<bool> pausePlayback() async {
return await _channel.invokeMethod('pausePlayback') ?? false;
}
/// 恢复播放
Future<bool> resumePlayback() async {
return await _channel.invokeMethod('resumePlayback') ?? false;
}
/// 停止播放
Future<bool> stopSpeaking() async {
return await _channel.invokeMethod('stopSpeaking') ?? false;
}
/// 释放TTS资源
Future<bool> dispose() async {
return await _channel.invokeMethod('dispose') ?? false;
}
}