feat: add BGM playback control for big screen display
- Add audio player to display store with play/stop functions - Listen for music state changes from AdminState sync - Support bgm, lottery, and fanfare audio tracks - Create audio directory structure Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,16 @@ import { SOCKET_EVENTS } from '@gala/shared/constants';
|
||||
|
||||
type GalaSocket = Socket<ServerToClientEvents, ClientToServerEvents>;
|
||||
|
||||
// Audio player singleton
|
||||
let audioPlayer: HTMLAudioElement | null = null;
|
||||
|
||||
// Audio file paths - place audio files in public/audio/
|
||||
const AUDIO_TRACKS: Record<string, string> = {
|
||||
bgm: '/audio/bgm.mp3',
|
||||
lottery: '/audio/lottery.mp3',
|
||||
fanfare: '/audio/fanfare.mp3',
|
||||
};
|
||||
|
||||
export const useDisplayStore = defineStore('display', () => {
|
||||
// State - use shallowRef for socket to avoid deep reactivity issues
|
||||
const socket = shallowRef<GalaSocket | null>(null);
|
||||
@@ -35,6 +45,10 @@ export const useDisplayStore = defineStore('display', () => {
|
||||
// QR Code display state (controlled by admin)
|
||||
const showEntryQR = ref(false);
|
||||
|
||||
// Music state
|
||||
const musicPlaying = ref(false);
|
||||
const musicTrack = ref<string>('none');
|
||||
|
||||
// Computed
|
||||
const connectionStatus = computed(() => {
|
||||
if (isConnected.value) return 'connected';
|
||||
@@ -132,6 +146,20 @@ export const useDisplayStore = defineStore('display', () => {
|
||||
detail: { mode: newMode, phase: state.systemPhase }
|
||||
}));
|
||||
}
|
||||
|
||||
// Handle music state changes
|
||||
if (state.music) {
|
||||
const { isPlaying, track } = state.music;
|
||||
if (isPlaying && track && track !== 'none') {
|
||||
// Only play if not already playing the same track
|
||||
if (!musicPlaying.value || musicTrack.value !== track) {
|
||||
const shouldLoop = track === 'bgm' || track === 'lottery';
|
||||
playAudio(track, shouldLoop);
|
||||
}
|
||||
} else if (!isPlaying && musicPlaying.value) {
|
||||
stopAudio();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// QR Code display control events
|
||||
@@ -174,6 +202,43 @@ export const useDisplayStore = defineStore('display', () => {
|
||||
return socket.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Play audio track
|
||||
*/
|
||||
function playAudio(track: string, loop: boolean = false) {
|
||||
stopAudio();
|
||||
const src = AUDIO_TRACKS[track];
|
||||
if (!src) {
|
||||
console.warn(`[Screen] Unknown audio track: ${track}`);
|
||||
return;
|
||||
}
|
||||
|
||||
audioPlayer = new Audio(src);
|
||||
audioPlayer.loop = loop;
|
||||
audioPlayer.volume = 0.7;
|
||||
audioPlayer.play().catch((err) => {
|
||||
console.error('[Screen] Failed to play audio:', err);
|
||||
});
|
||||
|
||||
musicPlaying.value = true;
|
||||
musicTrack.value = track;
|
||||
console.log(`[Screen] Playing audio: ${track}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop audio playback
|
||||
*/
|
||||
function stopAudio() {
|
||||
if (audioPlayer) {
|
||||
audioPlayer.pause();
|
||||
audioPlayer.currentTime = 0;
|
||||
audioPlayer = null;
|
||||
}
|
||||
musicPlaying.value = false;
|
||||
musicTrack.value = 'none';
|
||||
console.log('[Screen] Audio stopped');
|
||||
}
|
||||
|
||||
return {
|
||||
// State (excluding socket to avoid type inference issues)
|
||||
isConnected,
|
||||
@@ -184,6 +249,8 @@ export const useDisplayStore = defineStore('display', () => {
|
||||
currentPrize,
|
||||
currentWinner,
|
||||
showEntryQR,
|
||||
musicPlaying,
|
||||
musicTrack,
|
||||
|
||||
// Computed
|
||||
connectionStatus,
|
||||
@@ -193,5 +260,7 @@ export const useDisplayStore = defineStore('display', () => {
|
||||
disconnect,
|
||||
setMode,
|
||||
getSocket,
|
||||
playAudio,
|
||||
stopAudio,
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user