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.

239 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;
}
/// 检查是否正在播放
bool get isPlaying => true;
/// 检查是否正在合成(简化版,直接返回true)
bool isActuallySynthesizing() {
return true;
}
}