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.
241 lines
6.1 KiB
241 lines
6.1 KiB
import 'dart:async';
|
|
import 'dart:io';
|
|
import 'dart:collection';
|
|
import 'package:get/get.dart';
|
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
|
import 'package:flutter/services.dart';
|
|
|
|
/// 火山语音服务异常
|
|
class VolcanoTtsException implements Exception {
|
|
final String message;
|
|
final dynamic originalError;
|
|
|
|
VolcanoTtsException(this.message, [this.originalError]);
|
|
|
|
@override
|
|
String toString() => originalError != null
|
|
? '$message (原始错误: $originalError)'
|
|
: message;
|
|
}
|
|
|
|
/// 火山语音合成服务 (极简版)
|
|
///
|
|
/// 直接使用原生端 VolcanoTtsHelper 的功能,不处理状态回调
|
|
class VolcanoTtsService extends GetxService {
|
|
// 平台通道
|
|
static const MethodChannel _channel = MethodChannel('com.example.deep_voice/volcano_tts');
|
|
|
|
// 配置参数
|
|
final String _appId;
|
|
final String _token;
|
|
final String _cluster;
|
|
final String _voiceType;
|
|
|
|
// 初始化状态
|
|
bool _isInitialized = false;
|
|
bool _isDisposed = false;
|
|
|
|
// 服务启用状态
|
|
final isEnabled = true.obs; // 默认启用
|
|
|
|
// 音频质量参数
|
|
final double _defaultVolume = 1.0;
|
|
final double _defaultSpeed = 1.0;
|
|
|
|
VolcanoTtsService() :
|
|
_appId = dotenv.env['VOLCANO_TTS_APP_ID'] ?? '',
|
|
_token = dotenv.env['VOLCANO_TTS_APP_TOKEN'] ?? '',
|
|
_cluster = dotenv.env['VOLCANO_TTS_CLUSTER'] ?? '',
|
|
_voiceType = dotenv.env['VOLCANO_TTS_VOICE_TYPE'] ?? '' {
|
|
if (_appId.isEmpty || _token.isEmpty || _cluster.isEmpty) {
|
|
throw VolcanoTtsException('火山语音配置信息不完整,请检查环境变量 VOLCANO_TTS_APP_ID, VOLCANO_TTS_APP_TOKEN 和 VOLCANO_TTS_CLUSTER');
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<void> onInit() async {
|
|
super.onInit();
|
|
try {
|
|
await _initializeTtsEngine();
|
|
} catch (e, stackTrace) {
|
|
print('初始化TTS服务失败: $e');
|
|
print('Stack trace: $stackTrace');
|
|
}
|
|
}
|
|
|
|
/// 初始化TTS引擎
|
|
Future<void> _initializeTtsEngine() async {
|
|
try {
|
|
print('开始初始化火山语音TTS引擎');
|
|
|
|
final result = await _channel.invokeMethod<bool>('initialize', {
|
|
'appId': _appId,
|
|
'token': _token,
|
|
'cluster': _cluster,
|
|
});
|
|
|
|
_isInitialized = result ?? false;
|
|
print('火山语音TTS引擎初始化${_isInitialized ? '成功' : '失败'}');
|
|
|
|
if (!_isInitialized) {
|
|
throw VolcanoTtsException('TTS引擎初始化失败');
|
|
}
|
|
|
|
// 设置默认音量和速度
|
|
await setVolume(_defaultVolume);
|
|
await setSpeed(_defaultSpeed);
|
|
} catch (e) {
|
|
print('初始化火山语音TTS引擎失败: $e');
|
|
_isInitialized = false;
|
|
throw VolcanoTtsException('初始化失败', e);
|
|
}
|
|
}
|
|
|
|
/// 合成并播放文本
|
|
///
|
|
/// [text] 要合成的文本
|
|
/// [voiceType] 可选的语音类型,如果提供,将覆盖默认语音类型
|
|
///
|
|
/// 返回一个Future,表示是否成功开始播放
|
|
Future<bool> speak(String text, {String? voiceType}) async {
|
|
if (text.trim().isEmpty) return false;
|
|
if (!isEnabled.value) return false;
|
|
if (!_isInitialized) {
|
|
print('TTS引擎尚未初始化');
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
print('开始播放文本: "${text.length > 20 ? text.substring(0, 20) + '...' : text}"');
|
|
|
|
// 如果提供了语音类型,记录日志
|
|
if (voiceType != null && voiceType != _voiceType) {
|
|
print('使用临时语音类型: $voiceType (默认: $_voiceType)');
|
|
}
|
|
|
|
// 直接调用原生方法合成并播放语音
|
|
final success = await _channel.invokeMethod<bool>('speakText', {
|
|
'text': text,
|
|
'voiceType': voiceType ?? _voiceType,
|
|
});
|
|
|
|
return success ?? false;
|
|
} catch (e) {
|
|
print('语音合成失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 停止播放
|
|
Future<bool> stop() async {
|
|
if (!_isInitialized) {
|
|
print('TTS引擎尚未初始化');
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
print('停止播放');
|
|
|
|
// 调用原生方法停止播放
|
|
final success = await _channel.invokeMethod<bool>('stopSpeaking');
|
|
|
|
print('已停止播放');
|
|
return success ?? false;
|
|
} catch (e) {
|
|
print('停止播放失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 切换TTS启用状态
|
|
void toggleEnabled() {
|
|
isEnabled.toggle();
|
|
if (!isEnabled.value) {
|
|
stop();
|
|
}
|
|
}
|
|
|
|
/// 设置播放速度
|
|
Future<bool> setSpeed(double speed) async {
|
|
if (!_isInitialized) {
|
|
print('TTS引擎尚未初始化');
|
|
return false;
|
|
}
|
|
|
|
if (speed < 0.5 || speed > 2.0) {
|
|
throw VolcanoTtsException('播放速度必须在0.5到2.0之间');
|
|
}
|
|
|
|
try {
|
|
// 设置原生播放速度
|
|
final success = await _channel.invokeMethod<bool>('setSpeed', {
|
|
'speed': speed,
|
|
});
|
|
|
|
return success ?? false;
|
|
} catch (e) {
|
|
print('设置播放速度失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 设置音量
|
|
Future<bool> setVolume(double volume) async {
|
|
if (!_isInitialized) {
|
|
print('TTS引擎尚未初始化');
|
|
return false;
|
|
}
|
|
|
|
if (volume < 0.0 || volume > 1.0) {
|
|
throw VolcanoTtsException('音量必须在0.0到1.0之间');
|
|
}
|
|
|
|
try {
|
|
// 设置原生音量
|
|
final success = await _channel.invokeMethod<bool>('setVolume', {
|
|
'volume': volume,
|
|
});
|
|
|
|
return success ?? false;
|
|
} catch (e) {
|
|
print('设置音量失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 清理资源
|
|
Future<void> _cleanupResources() async {
|
|
_isDisposed = true;
|
|
await stop();
|
|
|
|
// 释放原生资源
|
|
try {
|
|
await _channel.invokeMethod('dispose');
|
|
} catch (e) {
|
|
print('释放原生TTS资源失败: $e');
|
|
}
|
|
}
|
|
|
|
@override
|
|
void onClose() async {
|
|
print('关闭TTS服务');
|
|
await _cleanupResources();
|
|
super.onClose();
|
|
}
|
|
|
|
/// 获取当前语音类型
|
|
String getCurrentVoiceType() {
|
|
return _voiceType;
|
|
}
|
|
|
|
/// 检查是否正在播放(简化版,直接返回true)
|
|
bool isActuallyPlaying() {
|
|
return true;
|
|
}
|
|
|
|
/// 检查是否正在合成(简化版,直接返回true)
|
|
bool isActuallySynthesizing() {
|
|
return true;
|
|
}
|
|
|
|
}
|