| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- import { defineStore } from 'pinia';
- import { ref, computed } from 'vue';
- import { get, post, getFullUrl } from '../utils/request';
- import type { AudioItem, Voice, VoiceParams } from '../types';
- export const useAudioStore = defineStore('audio', () => {
- // 状态
- const voices = ref<Voice[]>([]);
- const currentAudio = ref<AudioItem | null>(null);
- const playlist = ref<AudioItem[]>([]);
- const currentIndex = ref(-1);
- const isPlaying = ref(false);
- const currentTime = ref(0);
- const duration = ref(0);
- const playRate = ref(1);
-
- // 播放模式: 'sequence' | 'loop' | 'single' | 'random'
- const playMode = ref<'sequence' | 'loop' | 'single' | 'random'>('sequence');
- // 音频上下文
- let audioContext: UniApp.InnerAudioContext | null = null;
- // 计算属性
- const hasPlaylist = computed(() => playlist.value.length > 0);
- const hasNext = computed(() => currentIndex.value < playlist.value.length - 1);
- const hasPrev = computed(() => currentIndex.value > 0);
- // 初始化音频上下文
- function initAudioContext() {
- if (audioContext) return;
- audioContext = uni.createInnerAudioContext();
- audioContext.onPlay(() => {
- isPlaying.value = true;
- });
- audioContext.onPause(() => {
- isPlaying.value = false;
- });
- audioContext.onEnded(() => {
- isPlaying.value = false;
- // 自动播放下一首
- if (hasNext.value) {
- playNext();
- } else {
- // 列表播放完毕,停止播放
- handlePlayMode();
- }
- });
- audioContext.onTimeUpdate(() => {
- currentTime.value = audioContext?.currentTime || 0;
- const d = audioContext?.duration || 0;
- // 只有当音频元数据时长合理(小于1小时)且当前显示时长异常(为0或异常大)时才更新
- if (d > 0 && d < 3600) {
- if (duration.value === 0 || duration.value > 3600 || Math.abs(duration.value - d) < 1) {
- duration.value = d;
- }
- }
- });
- audioContext.onError((err: any) => {
- console.error('音频播放错误:', err);
- console.error('错误码:', err.errCode);
- console.error('错误信息:', err.errMsg);
- isPlaying.value = false;
- // 尝试获取更详细的错误信息
- if (audioContext) {
- console.error('audioContext.src:', audioContext.src);
- console.error('audioContext.readyState:', audioContext.readyState);
- }
- });
- audioContext.onCanplay(() => {
- console.log('音频已准备好播放');
- });
- }
- // 获取音色列表
- async function fetchVoices() {
- const result = await get<{ voices: Voice[] }>('/tts/voices');
- voices.value = result.voices;
- return result.voices;
- }
- // 生成音频
- async function generateAudio(
- text: string,
- voiceId: string,
- voiceParams: VoiceParams
- ) {
- uni.showLoading({ title: '生成中...', mask: true });
- try {
- const result = await post<{
- audioId: string;
- audioUrl: string;
- duration: number;
- size: number;
- }>('/tts/generate', { text, voiceId, voiceParams });
- uni.hideLoading();
- return result;
- } catch (error) {
- uni.hideLoading();
- throw error;
- }
- }
- // 播放音频
- function play(audio: AudioItem) {
- initAudioContext();
- // 重置时长,避免切换书籍后显示旧时长
- currentTime.value = 0;
- duration.value = 0;
- currentAudio.value = audio;
- // 获取完整的音频 URL
- const fullUrl = getFullUrl(audio.audioUrl);
- console.log('播放音频:', audio.title, 'URL:', fullUrl, '时长:', audio.audioDuration);
- // 如果音频源相同,只切换播放状态
- if (audioContext && audioContext.src === fullUrl) {
- console.log('音频源相同,切换播放状态');
- audioContext.play();
- } else if (audioContext) {
- console.log('设置新音频源并播放:', fullUrl);
- audioContext.src = fullUrl;
- audioContext.play();
- } else {
- console.error('audioContext 未初始化!');
- }
- // 使用 API 返回的 audioDuration 作为初始时长(避免音频文件 metadata 错误的问题)
- if (audio.audioDuration && audio.audioDuration > 0 && audio.audioDuration < 3600) {
- duration.value = audio.audioDuration;
- }
- }
- // 暂停
- function pause() {
- if (audioContext) {
- audioContext.pause();
- }
- }
- // 继续播放
- function resume() {
- if (audioContext) {
- audioContext.play();
- }
- }
- // 切换播放/暂停
- function togglePlay() {
- if (isPlaying.value) {
- pause();
- } else {
- resume();
- }
- }
- // 播放上一首
- function playPrev() {
- if (hasPrev.value) {
- currentIndex.value--;
- play(playlist.value[currentIndex.value]);
- }
- }
- // 播放下一首
- function playNext() {
- if (hasNext.value) {
- currentIndex.value++;
- play(playlist.value[currentIndex.value]);
- }
- }
- // 处理播放模式
- function handlePlayMode() {
- if (playlist.value.length === 0) return;
- switch (playMode.value) {
- case 'single':
- // 单曲循环
- play(playlist.value[currentIndex.value]);
- break;
- case 'loop':
- // 列表循环
- if (currentIndex.value < playlist.value.length - 1) {
- playNext();
- } else {
- // 回到第一首
- currentIndex.value = 0;
- play(playlist.value[0]);
- }
- break;
- case 'random':
- // 随机播放
- const randomIndex = Math.floor(Math.random() * playlist.value.length);
- currentIndex.value = randomIndex;
- play(playlist.value[randomIndex]);
- break;
- case 'sequence':
- default:
- // 顺序播放(不自动播放)
- break;
- }
- }
- // 切换播放模式
- function togglePlayMode() {
- const modes: Array<'sequence' | 'loop' | 'single' | 'random'> = ['sequence', 'loop', 'single', 'random'];
- const currentIndex = modes.indexOf(playMode.value);
- playMode.value = modes[(currentIndex + 1) % modes.length];
-
- const modeNames = {
- sequence: '顺序播放',
- loop: '列表循环',
- single: '单曲循环',
- random: '随机播放',
- };
-
- uni.showToast({
- title: modeNames[playMode.value],
- icon: 'none',
- });
- }
- // 跳转到指定位置
- function seek(time: number) {
- if (audioContext) {
- audioContext.seek(time);
- currentTime.value = time;
- }
- }
- // 设置播放速度
- function setPlayRate(rate: number) {
- playRate.value = rate;
- if (audioContext) {
- audioContext.playbackRate = rate;
- }
- }
- // 设置播放列表
- function setPlaylist(list: AudioItem[], index: number = 0, autoPlay: boolean = true) {
- playlist.value = list;
- currentIndex.value = index;
- if (list.length > 0 && autoPlay) {
- play(list[index]);
- }
- }
- // 销毁音频上下文
- function destroy() {
- if (audioContext) {
- audioContext.destroy();
- audioContext = null;
- }
- }
- return {
- // 状态
- voices,
- currentAudio,
- playlist,
- currentIndex,
- isPlaying,
- currentTime,
- duration,
- playRate,
- playMode,
- // 计算属性
- hasPlaylist,
- hasNext,
- hasPrev,
- // 方法
- fetchVoices,
- generateAudio,
- play,
- pause,
- resume,
- togglePlay,
- playPrev,
- playNext,
- handlePlayMode,
- togglePlayMode,
- seek,
- setPlayRate,
- setPlaylist,
- destroy,
- };
- });
|