<template>
|
<div class="speech-demo">
|
<!-- 基础演示区域 -->
|
<section class="demo-section">
|
<h3>基础演示</h3>
|
<textarea
|
v-model="basicText"
|
placeholder="请输入要转换的文本"
|
></textarea>
|
<button @click="handleBasicSpeech">开始播放</button>
|
</section>
|
|
<!-- 分段演示区域 -->
|
<section class="demo-section">
|
<h3>分段播放演示</h3>
|
<div class="segment-container">
|
<div v-for="(text, index) in mockTexts" :key="index" class="segment">
|
<span>{{ text }}</span>
|
</div>
|
</div>
|
<button @click="handleSegmentSpeech">分段播放</button>
|
</section>
|
|
<!-- 高级功能演示区域 -->
|
<section class="demo-section">
|
<h3>高级功能演示</h3>
|
<div class="controls">
|
<button @click="handleTogglePlay">{{ isPaused ? '继续' : '暂停' }}</button>
|
<button @click="handleReset">重置</button>
|
</div>
|
<div class="status">
|
<p>当前状态: {{ status }}</p>
|
<p>播放进度: {{ progress }}%</p>
|
</div>
|
</section>
|
|
<!-- 事件日志区域 -->
|
<section class="demo-section">
|
<h3>事件日志</h3>
|
<div class="log-container">
|
<div v-for="(log, index) in eventLogs" :key="index" class="log-item">
|
{{ log }}
|
</div>
|
</div>
|
</section>
|
|
<!-- 添加状态管理演示区域 -->
|
<section class="demo-section">
|
<h3>状态管理演示</h3>
|
<div class="state-info">
|
<div class="state-item">
|
<span>播放状态:</span>
|
<span :class="stateClass">{{ stateText }}</span>
|
</div>
|
<div class="state-item">
|
<span>队列长度:</span>
|
<span>{{ queueLength }}</span>
|
</div>
|
<div class="state-item">
|
<span>总进度:</span>
|
<div class="progress-bar">
|
<div class="progress-fill" :style="{ width: `${totalProgress}%` }"></div>
|
<span>{{ totalProgress }}%</span>
|
</div>
|
</div>
|
</div>
|
<div class="state-controls">
|
<button @click="checkState">检查状态</button>
|
<button @click="resetState">重置状态</button>
|
</div>
|
</section>
|
</div>
|
</template>
|
|
<script setup lang="ts">
|
import { ref, onMounted, onBeforeUnmount, computed } from 'vue';
|
import SpeechSynthesisUtil, { EventType } from 'uniapp-text-to-speech';
|
|
// 响应式状态
|
const basicText = ref('你好,这是一个基础示例。');
|
const mockTexts = ref(['你好,', '我是', '人工智能助手,', '很高兴认识你!']);
|
const status = ref('就绪');
|
const progress = ref(0);
|
const isPaused = ref(false);
|
const eventLogs = ref<string[]>([]);
|
|
// 添加状态管理相关的响应式状态
|
const stateText = ref('就绪');
|
const queueLength = ref(0);
|
const totalProgress = ref(0);
|
const isError = ref(false);
|
|
// 添加状态类的计算属性
|
const stateClass = computed(() => {
|
return {
|
'state-ready': stateText.value === '就绪',
|
'state-playing': stateText.value === '播放中',
|
'state-paused': stateText.value === '已暂停',
|
'state-error': isError.value
|
};
|
});
|
|
// 初始化语音工具
|
const tts = new SpeechSynthesisUtil({
|
API_KEY: 'your_api_key', // 替换为你的API密钥
|
GroupId: 'your_group_id', // 替换为你的组ID
|
modelConfig: {
|
model: 'speech-01-240228',
|
voice_setting: {
|
voice_id: "female-yujie",
|
speed: 1,
|
vol: 1
|
}
|
}
|
});
|
|
// 添加日志
|
const addLog = (message: string) => {
|
eventLogs.value.unshift(`${new Date().toLocaleTimeString()}: ${message}`);
|
if (eventLogs.value.length > 10) {
|
eventLogs.value.pop();
|
}
|
};
|
|
// 设置事件监听
|
const setupEventListeners = () => {
|
// 监听合成开始
|
tts.on(EventType.SYNTHESIS_START, ({ text }) => {
|
addLog(`开始合成文本: ${text}`);
|
});
|
|
// 监听播放开始
|
tts.on(EventType.AUDIO_PLAY, ({ currentText, remainingCount }) => {
|
addLog(`正在播放: ${currentText}, 剩余: ${remainingCount}`);
|
status.value = '播放中';
|
});
|
|
// 监听播放结束
|
tts.on(EventType.AUDIO_END, ({ finishedText }) => {
|
addLog(`播放完成: ${finishedText}`);
|
status.value = '就绪';
|
progress.value = 100;
|
});
|
|
// 监听错误
|
tts.on(EventType.ERROR, ({ error }) => {
|
addLog(`错误: ${error.message}`);
|
status.value = '错误';
|
});
|
|
// 监听暂停
|
tts.on(EventType.PAUSE, () => {
|
addLog('播放已暂停');
|
status.value = '已暂停';
|
isPaused.value = true;
|
});
|
|
// 监听恢复
|
tts.on(EventType.RESUME, () => {
|
addLog('播放已恢复');
|
status.value = '播放中';
|
isPaused.value = false;
|
});
|
|
// 添加状态变化监听
|
tts.on(EventType.STATE_CHANGE, ({ newState }) => {
|
stateText.value = newState.isPlaying ? '播放中' :
|
newState.isPaused ? '已暂停' :
|
newState.isError ? '错误' : '就绪';
|
isError.value = newState.isError;
|
});
|
|
// 添加进度监听
|
tts.on(EventType.PROGRESS, ({ currentIndex, totalChunks }) => {
|
totalProgress.value = Math.round((currentIndex / totalChunks) * 100);
|
});
|
|
// 添加队列变化监听
|
tts.on(EventType.QUEUE_CHANGE, ({ length }) => {
|
queueLength.value = length;
|
addLog(`队列长度更新: ${length}`);
|
});
|
};
|
|
// 基础播放示例
|
const handleBasicSpeech = async () => {
|
try {
|
await tts.textToSpeech(basicText.value);
|
} catch (error) {
|
addLog(`播放失败: ${error.message}`);
|
}
|
};
|
|
// 分段播放示例
|
const handleSegmentSpeech = async () => {
|
try {
|
// 直接使用 textToSpeech 处理每个分段
|
for (const text of mockTexts.value) {
|
await tts.textToSpeech(text);
|
}
|
} catch (error) {
|
addLog(`分段播放失败: ${error.message}`);
|
}
|
};
|
|
// 切换播放/暂停
|
const handleTogglePlay = () => {
|
tts.togglePlay();
|
};
|
|
// 重置播放
|
const handleReset = () => {
|
tts.reset();
|
status.value = '就绪';
|
progress.value = 0;
|
isPaused.value = false;
|
addLog('已重置所有状态');
|
};
|
|
// 添加状态检查方法
|
const checkState = () => {
|
const currentState = tts.getState();
|
stateText.value = currentState.isPlaying ? '播放中' :
|
currentState.isPaused ? '已暂停' :
|
currentState.isError ? '错误' : '就绪';
|
isError.value = currentState.isError;
|
|
// 获取队列信息
|
const queueInfo = tts.getQueueInfo();
|
queueLength.value = queueInfo.length;
|
|
addLog(`当前状态: ${stateText.value}, 队列长度: ${queueLength.value}`);
|
};
|
|
// 添加状态重置方法
|
const resetState = () => {
|
tts.reset();
|
stateText.value = '就绪';
|
queueLength.value = 0;
|
totalProgress.value = 0;
|
isError.value = false;
|
addLog('状态已重置');
|
};
|
|
// 生命周期钩子
|
onMounted(() => {
|
setupEventListeners();
|
});
|
|
onBeforeUnmount(() => {
|
tts.reset();
|
});
|
</script>
|
|
<style scoped>
|
.speech-demo {
|
padding: 20px;
|
max-width: 800px;
|
margin: 0 auto;
|
}
|
|
.demo-section {
|
margin-bottom: 30px;
|
padding: 20px;
|
border: 1px solid #eee;
|
border-radius: 8px;
|
}
|
|
h3 {
|
margin-top: 0;
|
margin-bottom: 15px;
|
color: #333;
|
}
|
|
textarea {
|
width: 100%;
|
height: 100px;
|
padding: 10px;
|
margin-bottom: 10px;
|
border: 1px solid #ddd;
|
border-radius: 4px;
|
resize: vertical;
|
}
|
|
button {
|
padding: 8px 16px;
|
margin-right: 10px;
|
border: none;
|
border-radius: 4px;
|
background-color: #4CAF50;
|
color: white;
|
cursor: pointer;
|
}
|
|
button:disabled {
|
background-color: #cccccc;
|
}
|
|
.segment-container {
|
margin-bottom: 15px;
|
}
|
|
.segment {
|
display: inline-block;
|
padding: 5px 10px;
|
margin: 5px;
|
background-color: #f5f5f5;
|
border-radius: 4px;
|
}
|
|
.controls {
|
margin-bottom: 15px;
|
}
|
|
.status {
|
padding: 10px;
|
background-color: #f9f9f9;
|
border-radius: 4px;
|
}
|
|
.log-container {
|
height: 200px;
|
overflow-y: auto;
|
padding: 10px;
|
background-color: #f5f5f5;
|
border-radius: 4px;
|
}
|
|
.log-item {
|
padding: 5px;
|
border-bottom: 1px solid #eee;
|
font-family: monospace;
|
}
|
|
/* 添加状态管理相关样式 */
|
.state-info {
|
margin-bottom: 15px;
|
}
|
|
.state-item {
|
display: flex;
|
align-items: center;
|
margin-bottom: 10px;
|
}
|
|
.state-item span:first-child {
|
width: 100px;
|
color: #666;
|
}
|
|
.state-controls {
|
display: flex;
|
gap: 10px;
|
}
|
|
.state-ready { color: #2196F3; }
|
.state-playing { color: #4CAF50; }
|
.state-paused { color: #FF9800; }
|
.state-error { color: #F44336; }
|
|
.progress-bar {
|
flex: 1;
|
height: 20px;
|
background-color: #eee;
|
border-radius: 10px;
|
overflow: hidden;
|
position: relative;
|
margin-left: 10px;
|
}
|
|
.progress-fill {
|
height: 100%;
|
background-color: #4CAF50;
|
transition: width 0.3s ease;
|
}
|
|
.progress-bar span {
|
position: absolute;
|
left: 50%;
|
top: 50%;
|
transform: translate(-50%, -50%);
|
color: #fff;
|
text-shadow: 0 0 2px rgba(0,0,0,0.5);
|
}
|
</style>
|