fix: fix pause when auto play
This commit is contained in:
parent
a299dfdb7b
commit
156063c3a0
@ -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>
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
30
server.py
30
server.py
@ -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({
|
||||
|
||||
@ -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
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user