forked from wangziqi/gongxue-base
98 lines
2.5 KiB
TypeScript
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' });
|
|
}
|
|
}
|