import Taro from '@tarojs/taro'; export type AccentType = 'us' | 'uk'; const YOUDAO_TYPE: Record = { us: 1, uk: 2, }; let currentAudio: HTMLAudioElement | null = null; let currentInnerAudio: Taro.InnerAudioContext | null = null; function pronunciationUrl(word: string, accent: AccentType) { return `https://dict.youdao.com/dictvoice?audio=${encodeURIComponent(word)}&type=${YOUDAO_TYPE[accent] || 1}`; } function stopCurrentAudio() { try { if (currentAudio) { currentAudio.pause(); currentAudio.src = ''; currentAudio = null; } } catch (_error) { // ignore playback cleanup errors } try { if (currentInnerAudio) { currentInnerAudio.stop(); currentInnerAudio.destroy(); currentInnerAudio = null; } } catch (_error) { // ignore playback cleanup errors } try { if (typeof window !== 'undefined' && 'speechSynthesis' in window) { window.speechSynthesis.cancel(); } } catch (_error) { // ignore unsupported runtime } } function fallbackSpeech(word: string, accent: AccentType) { if (typeof window === 'undefined' || !('speechSynthesis' in window)) return false; try { const utterance = new SpeechSynthesisUtterance(word); utterance.lang = accent === 'uk' ? 'en-GB' : 'en-US'; utterance.rate = 0.86; window.speechSynthesis.speak(utterance); return true; } catch (_error) { return false; } } export async function playWordPronunciation(word: string, accent: AccentType = 'us') { const text = word.trim(); if (!text) return; stopCurrentAudio(); const url = pronunciationUrl(text, accent); if (process.env.TARO_ENV === 'h5' && typeof Audio !== 'undefined') { try { const audio = new Audio(url); audio.preload = 'auto'; currentAudio = audio; audio.onerror = () => { currentAudio = null; fallbackSpeech(text, accent); }; await audio.play(); return; } catch (_error) { if (fallbackSpeech(text, accent)) return; } } try { const audio = Taro.createInnerAudioContext(); currentInnerAudio = audio; audio.src = url; audio.autoplay = true; audio.onEnded(() => { if (currentInnerAudio === audio) currentInnerAudio = null; audio.destroy(); }); audio.onError(() => { if (currentInnerAudio === audio) currentInnerAudio = null; audio.destroy(); }); } catch (_error) { Taro.showToast({ title: '当前环境暂不支持发音', icon: 'none' }); } }