fix : 切换播放,暂停播放bug
This commit is contained in:
@@ -37,6 +37,7 @@ export const useAudioSpeak = (config = {}) => {
|
||||
let firstSegmentHeader = null
|
||||
let lastPlayedIndex = -1
|
||||
let currentPlayingIndex = -1 // 当前正在播放的片段索引
|
||||
let isInterrupted = false // 是否被中断
|
||||
|
||||
// 按标点分割文本
|
||||
const splitTextByPunctuation = (text) => {
|
||||
@@ -167,7 +168,7 @@ export const useAudioSpeak = (config = {}) => {
|
||||
|
||||
// 初始化音频上下文
|
||||
const initAudioContext = () => {
|
||||
if (!audioContext) {
|
||||
if (!audioContext || audioContext.state === 'closed') {
|
||||
audioContext = new (window.AudioContext || window.webkitAudioContext)()
|
||||
console.log('音频上下文已初始化')
|
||||
}
|
||||
@@ -178,6 +179,7 @@ export const useAudioSpeak = (config = {}) => {
|
||||
const decodeAndPlayBlob = async (audioBlob, segmentIndex) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (isCancelled || !audioContext) {
|
||||
console.log('播放已取消或音频上下文不存在,跳过播放')
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
@@ -193,6 +195,13 @@ export const useAudioSpeak = (config = {}) => {
|
||||
const arrayBuffer = e.target.result
|
||||
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer)
|
||||
|
||||
// 如果在此期间被取消,直接返回
|
||||
if (isCancelled) {
|
||||
console.log('播放过程中被取消')
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
|
||||
audioSource = audioContext.createBufferSource()
|
||||
audioSource.buffer = audioBuffer
|
||||
audioSource.connect(audioContext.destination)
|
||||
@@ -212,6 +221,12 @@ export const useAudioSpeak = (config = {}) => {
|
||||
}
|
||||
|
||||
console.log(`▶️开始播放第${segmentIndex + 1}个片段`)
|
||||
|
||||
// 如果音频上下文被暂停,先恢复
|
||||
if (audioContext.state === 'suspended') {
|
||||
await audioContext.resume()
|
||||
}
|
||||
|
||||
audioSource.start(0)
|
||||
|
||||
} catch (error) {
|
||||
@@ -295,7 +310,7 @@ export const useAudioSpeak = (config = {}) => {
|
||||
isPlayingQueue = true
|
||||
|
||||
try {
|
||||
while (audioQueue.length > 0 && !isCancelled) {
|
||||
while (audioQueue.length > 0 && !isCancelled && !isInterrupted) {
|
||||
const audioItem = audioQueue[0]
|
||||
currentSegmentIndex.value = audioItem.segmentIndex
|
||||
|
||||
@@ -303,6 +318,11 @@ export const useAudioSpeak = (config = {}) => {
|
||||
console.log(`准备播放第${audioItem.segmentIndex + 1}个片段: "${audioItem.text}"`)
|
||||
await decodeAndPlayBlob(audioItem.blob, audioItem.segmentIndex)
|
||||
|
||||
if (isCancelled || isInterrupted) {
|
||||
console.log('播放被中断,退出播放队列')
|
||||
break
|
||||
}
|
||||
|
||||
// 播放完成后移除
|
||||
audioQueue.shift()
|
||||
console.log(`片段${audioItem.segmentIndex + 1}播放完成,队列剩余: ${audioQueue.length}`)
|
||||
@@ -329,6 +349,12 @@ export const useAudioSpeak = (config = {}) => {
|
||||
|
||||
// 向队列添加音频
|
||||
const addToQueue = (audioItem) => {
|
||||
// 如果被取消或中断,不添加到队列
|
||||
if (isCancelled || isInterrupted) {
|
||||
console.log('播放已中断,不添加到队列')
|
||||
return
|
||||
}
|
||||
|
||||
// 按segmentIndex插入到正确位置
|
||||
let insertIndex = 0
|
||||
for (let i = audioQueue.length - 1; i >= 0; i--) {
|
||||
@@ -471,19 +497,23 @@ export const useAudioSpeak = (config = {}) => {
|
||||
|
||||
// 清理资源
|
||||
const cleanup = () => {
|
||||
console.log('开始清理资源')
|
||||
isCancelled = true
|
||||
isInterrupted = true
|
||||
|
||||
if (audioSource) {
|
||||
try {
|
||||
audioSource.stop()
|
||||
console.log('音频源已停止')
|
||||
} catch (e) {
|
||||
// 忽略错误
|
||||
console.warn('停止音频源失败:', e)
|
||||
}
|
||||
audioSource = null
|
||||
}
|
||||
|
||||
if (audioContext && audioContext.state !== 'closed') {
|
||||
audioContext.close()
|
||||
console.log('音频上下文已关闭')
|
||||
}
|
||||
|
||||
audioContext = null
|
||||
@@ -500,38 +530,57 @@ export const useAudioSpeak = (config = {}) => {
|
||||
isPaused.value = false
|
||||
isLoading.value = false
|
||||
progress.value = 0
|
||||
console.log('资源清理完成')
|
||||
}
|
||||
|
||||
// 停止播放
|
||||
const stopAudio = () => {
|
||||
console.log('停止音频播放')
|
||||
isCancelled = true
|
||||
isInterrupted = true
|
||||
|
||||
if (audioSource) {
|
||||
try {
|
||||
audioSource.stop()
|
||||
console.log('音频源已停止')
|
||||
} catch (e) {
|
||||
// 忽略错误
|
||||
console.warn('停止音频源失败:', e)
|
||||
}
|
||||
audioSource = null
|
||||
}
|
||||
|
||||
audioQueue = []
|
||||
isPlayingQueue = false
|
||||
currentPlayingIndex = -1
|
||||
isSpeaking.value = false
|
||||
isPaused.value = false
|
||||
isLoading.value = false
|
||||
|
||||
// 恢复中断标志,为下一次播放准备
|
||||
setTimeout(() => {
|
||||
isCancelled = false
|
||||
isInterrupted = false
|
||||
}, 100)
|
||||
}
|
||||
|
||||
// 主speak方法
|
||||
const speak = async (text) => {
|
||||
console.log('开始新的语音播报')
|
||||
|
||||
// 先停止当前播放
|
||||
if (isSpeaking.value || audioQueue.length > 0) {
|
||||
console.log('检测到正在播放,先停止')
|
||||
stopAudio()
|
||||
// 等待一小段时间确保资源清理完成
|
||||
await new Promise(resolve => setTimeout(resolve, 200))
|
||||
}
|
||||
|
||||
text = extractSpeechText(text)
|
||||
console.log('开始语音播报:', text)
|
||||
|
||||
// 如果正在播放,先停止
|
||||
if (isSpeaking.value) {
|
||||
stopAudio()
|
||||
}
|
||||
|
||||
// 重置状态
|
||||
isCancelled = false
|
||||
isInterrupted = false
|
||||
currentText.value = text
|
||||
isLoading.value = true
|
||||
isSpeaking.value = true
|
||||
@@ -562,7 +611,10 @@ export const useAudioSpeak = (config = {}) => {
|
||||
|
||||
// 1. 串行请求所有音频片段
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
if (isCancelled) break
|
||||
if (isCancelled || isInterrupted) {
|
||||
console.log('播放被取消或中断,停止请求')
|
||||
break
|
||||
}
|
||||
|
||||
console.log(`串行请求第${i + 1}/${segments.length}个片段`)
|
||||
|
||||
@@ -572,6 +624,11 @@ export const useAudioSpeak = (config = {}) => {
|
||||
// 请求音频片段
|
||||
const audioItem = await fetchAudioSegment(segments[i], i)
|
||||
|
||||
if (isCancelled || isInterrupted) {
|
||||
console.log('播放被取消或中断,停止添加队列')
|
||||
break
|
||||
}
|
||||
|
||||
// 添加到播放队列
|
||||
addToQueue({
|
||||
blob: audioItem.blob,
|
||||
@@ -586,6 +643,11 @@ export const useAudioSpeak = (config = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (isCancelled || isInterrupted) {
|
||||
console.log('播放被取消或中断,退出播放')
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 所有请求完成
|
||||
console.log('所有音频片段请求完成')
|
||||
allRequestsCompleted = true
|
||||
@@ -594,7 +656,7 @@ export const useAudioSpeak = (config = {}) => {
|
||||
tryMergeRemainingSegments()
|
||||
|
||||
// 4. 等待所有音频播放完成
|
||||
while (audioQueue.length > 0 && !isCancelled) {
|
||||
while (audioQueue.length > 0 && !isCancelled && !isInterrupted) {
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
}
|
||||
|
||||
@@ -604,9 +666,11 @@ export const useAudioSpeak = (config = {}) => {
|
||||
console.error('语音播报失败:', error)
|
||||
} finally {
|
||||
// 最终清理
|
||||
if (isCancelled) {
|
||||
if (isCancelled || isInterrupted) {
|
||||
console.log('播放被取消或中断,进行清理')
|
||||
cleanup()
|
||||
} else {
|
||||
console.log('播放正常完成')
|
||||
isSpeaking.value = false
|
||||
isPaused.value = false
|
||||
isLoading.value = false
|
||||
@@ -642,8 +706,6 @@ export const useAudioSpeak = (config = {}) => {
|
||||
cleanup()
|
||||
}
|
||||
|
||||
// 组件卸载时清理
|
||||
|
||||
return {
|
||||
// 状态
|
||||
isSpeaking,
|
||||
|
||||
Reference in New Issue
Block a user