Files
gongxue-base/apps/taro/src/services/pronunciation.ts

98 lines
2.5 KiB
TypeScript

import Taro from '@tarojs/taro';
export type AccentType = 'us' | 'uk';
const YOUDAO_TYPE: Record<AccentType, number> = {
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' });
}
}