fix: fix pause when auto play

This commit is contained in:
冯琪 2025-03-18 20:54:51 +08:00
parent a299dfdb7b
commit 156063c3a0
4 changed files with 42 additions and 36 deletions

View File

@ -67,10 +67,10 @@
<div class="voice-selector">
<label for="voice-selector">选择语音:</label>
<select id="voice-selector" class="form-select form-select-sm">
<option value="zf_xiaole">Heart</option>
<option value="zf_xiaoxiao">小小</option>
<option value="zf_xiaoyun">小妮</option>
<option value="zf_xiaogang">小贝</option>
<option value="zf_xiaole">Heart</option>
</select>
</div>

View File

@ -27,7 +27,7 @@ export class AITeacherApp {
// 音频相关
this.audioPlayer = null;
this.currentAudioBase64 = null;
this.selectedVoice = 'zf_xiaoxiao';
this.selectedVoice = 'af_heart';
this.speechSpeed = 1.0;
this.sentences = [];
this.audioSegments = [];
@ -148,16 +148,17 @@ export class AITeacherApp {
this.renderPage(this.pageNumPending);
this.pageNumPending = null;
}
// 清空讲解区域和停止音频播放
document.getElementById('explanation-text').textContent = '点击"生成讲解"按钮获取AI讲解';
this.stopAudio();
document.getElementById('play-btn').disabled = true;
});
});
document.getElementById('page-num').value = num;
}
renderExplain(){
document.getElementById('explanation-text').textContent = '点击"生成讲解"按钮获取AI讲解';
this.stopAudio();
document.getElementById('play-btn').disabled = true;
}
queueRenderPage(num) {
if (this.pageRendering) {
@ -172,6 +173,7 @@ export class AITeacherApp {
return;
}
this.pageNum--;
this.renderExplain();
this.queueRenderPage(this.pageNum);
}
@ -186,9 +188,9 @@ export class AITeacherApp {
this.pageNum++;
document.getElementById('page-num').textContent = this.pageNum;
this.queueRenderPage(this.pageNum);
// 自动获取新页面的讲解
this.renderExplain();
this.onExplain();
console.log("auto load next page")
}
onPageNumChange(e) {
@ -196,6 +198,7 @@ export class AITeacherApp {
if (newPageNum > 0 && newPageNum <= this.pdfDoc.numPages) {
this.pageNum = newPageNum;
this.queueRenderPage(this.pageNum);
this.renderExplain();
}
}
@ -264,7 +267,7 @@ export class AITeacherApp {
speed: speed
})
});
console.log("trying to get explain");
if (!response.ok) {
throw new Error('服务器响应错误');
}
@ -428,7 +431,8 @@ export class AITeacherApp {
}
// 播放音频
this.audioPlayer.play();
// this.audioPlayer.play();
this.playAudioWithRetry(this.audioPlayer);
this.isPlaying = true;
// 更新播放按钮状态
@ -439,17 +443,12 @@ export class AITeacherApp {
// 监听音频播放结束事件
this.audioPlayer.onended = () => {
// 结束口型同步
if (this.live2dController && this.live2dController.initialized) {
this.live2dController.setMouthOpenY(0);
}
// 播放下一段
this.currentSegmentIndex++;
this.playCurrentSegment();
};
// 启动口型同步动画
this.frequencyData = new Uint8Array(this.analyzerNode.frequencyBinCount);
this.lipSyncAnimationId = requestAnimationFrame(this.updateLipSync.bind(this));
} catch (error) {
@ -458,6 +457,17 @@ export class AITeacherApp {
this.finishPlayback();
}
}
async playAudioWithRetry(audioPlayer) {
while (true) {
try {
await audioPlayer.play();
break;
} catch (error) {
console.error('播放音频失败将在100ms后重试:', error);
await new Promise(resolve => setTimeout(resolve, 100));
}
}
}
pauseAudio() {
if (this.audioPlayer && !this.audioPlayer.paused) {
@ -668,10 +678,6 @@ export class AITeacherApp {
// 自动跳转到下一页(如果启用了自动翻页)
if (this.autoNavigate && this.pageNum < this.pdfDoc.numPages) {
// 延迟1.5秒后跳转,给用户一个短暂的停顿时间
// setTimeout(() => {
// this.onNextPage();
// }, 1500);
this.onNextPage();
}

View File

@ -152,7 +152,7 @@ async def fetch_audio_data(session, pair_data, voice, speed):
await asyncio.sleep(0.5)
continue
async def text_to_speech(text, voice="zf_xiaoxiao", speed=1.5):
async def text_to_speech(text, voice="af_heart", speed=1.5):
"""异步将文本转换为语音,返回每两句话的音频数据和时间戳"""
try:
start_time = time.time()
@ -250,7 +250,7 @@ async def explain():
async def tts():
data = await request.json
text = data.get('text', '')
voice = data.get('voice', 'zf_xiaoxiao')
voice = data.get('voice', 'af_heart')
speed = data.get('speed', 1.0)
if not text:
@ -315,7 +315,7 @@ async def explain_with_audio():
data = await request.json
text = data.get('text', '')
page_num = data.get('page', None)
voice = data.get('voice', 'zf_xiaoxiao')
voice = data.get('voice', 'af_heart')
speed = data.get('speed', 1.0)
# 这里多线程执行, 用于提前加载缓存的讲解
asyncio.create_task(generate_cache_explanation(page_num,voice,speed))
@ -397,7 +397,7 @@ async def load_pdf():
pdfpages = page_count
# 使用默认的声音和速度预加载讲解
voice = 'zf_xiaoxiao'
voice = 'af_heart'
speed = 1.0
start_time = time.time()
asyncio.create_task(generate_cache_explanation(0,voice,speed))
@ -418,18 +418,18 @@ async def load_pdf():
def get_voices():
"""获取可用的TTS声音列表"""
voices = [
{"id": "zf_xiaoxiao", "name": "小小", "gender": "female", "lang": "zh"},
{"id": "zf_xiaoni", "name": "小妮", "gender": "female", "lang": "zh"},
{"id": "zf_xiaoyi", "name": "小怡", "gender": "female", "lang": "zh"},
{"id": "zf_xiaobei", "name": "小贝", "gender": "female", "lang": "zh"},
{"id": "zm_yunxi", "name": "云熙", "gender": "male", "lang": "zh"},
{"id": "zm_yunyang", "name": "云扬", "gender": "male", "lang": "zh"},
{"id": "zm_yunxia", "name": "云夏", "gender": "male", "lang": "zh"},
{"id": "zm_yunjian", "name": "云健", "gender": "male", "lang": "zh"},
# {"id": "zf_xiaoxiao", "name": "小小", "gender": "female", "lang": "zh"},
# {"id": "zf_xiaoni", "name": "小妮", "gender": "female", "lang": "zh"},
# {"id": "zf_xiaoyi", "name": "小怡", "gender": "female", "lang": "zh"},
# {"id": "zf_xiaobei", "name": "小贝", "gender": "female", "lang": "zh"},
# {"id": "zm_yunxi", "name": "云熙", "gender": "male", "lang": "zh"},
# {"id": "zm_yunyang", "name": "云扬", "gender": "male", "lang": "zh"},
# {"id": "zm_yunxia", "name": "云夏", "gender": "male", "lang": "zh"},
# {"id": "zm_yunjian", "name": "云健", "gender": "male", "lang": "zh"},
{"id": "af_heart", "name": "Heart", "gender": "female", "lang": "en"},
{"id": "af_bella", "name": "Bella", "gender": "female", "lang": "en"},
{"id": "am_michael", "name": "Michael", "gender": "male", "lang": "en"},
{"id": "am_puck", "name": "Puck", "gender": "male", "lang": "en"}
# {"id": "af_bella", "name": "Bella", "gender": "female", "lang": "en"},
# {"id": "am_michael", "name": "Michael", "gender": "male", "lang": "en"},
# {"id": "am_puck", "name": "Puck", "gender": "male", "lang": "en"}
]
return jsonify({

View File

@ -1,7 +1,7 @@
{
"OPENAI_API_KEY":"your_openai_api_key_here",
"VOICERSS_API_KEY":"your_voicerss_api_key_here",
"TTS_BASE_URL":"http://server.feng-arch.cn:52861",
"TTS_BASE_URL":"http://server.feng-arch.cn:52862",
"websocket_port": 6006,
"port": 6007
}