import { useState, useEffect, useRef } from "react"; import {Howl, Howler} from 'howler'; import "./App.css"; const DefaultFadeDuration = 3; // 1 second const CueType={ Bg: 'bg', Announce: 'announce', Light: 'light' } const EmojiType={ bg: '🎵', announce: '📢', light: '💡' } function App() { const [cuelist, setCuelist] = useState([]); const refAudioBg= useRef(null); const refAudioAnnounce= useRef(null); const refAudio= useRef({}); const [currentCue, setCurrentCue] = useState(null); const [fadeDuration, setFadeDuration] = useState(DefaultFadeDuration); // Default fade duration in seconds const [timestamp, setTimestamp] = useState(); const refCue = useRef(null); const refNextCue = useRef(null); function onfade(e){ console.log('onfade', e); if(refNextCue.current) { playCue(refNextCue.current); refNextCue.current = null; } } function onAnounceFade(e){ console.log('onAnounceFade', e); if(refAudioAnnounce.current.volume() === 0) { refAudioAnnounce.current.stop(); } } function loadAudio(audioFile, loop = false, type) { if(refAudio.current[audioFile]) { return refAudio.current[audioFile]; } const sound = new Howl({ src: [audioFile], volume: 1, loop: loop, onfade: type==CueType.Bg ? onfade : onAnounceFade, }); refAudio.current[audioFile] = sound; return sound; } function playAudio(audioFile, loop = false, type) { switch(type){ case CueType.Bg: refAudioBg.current = loadAudio(audioFile, loop, type); refAudioBg.current.play(); refAudioBg.current.fade(0, 1, fadeDuration * 1000); break; case CueType.Announce: refAudioAnnounce.current = loadAudio(audioFile, loop, type); refAudioAnnounce.current.seek(0); refAudioAnnounce.current.volume(1); refAudioAnnounce.current.play(); break; } } function playCue({id, name, description, type, auto, audioFile, ...props}) { console.log('Playing cue:', {id, name, description, type, auto, audioFile, ...props}); // Handle other cue types and properties here switch(type) { case CueType.Bg: if(refAudioBg.current) { if(refAudioBg.current.volume() === 0) { refAudioBg.current.stop(); playAudio(audioFile, props.loop || false, type); setCurrentCue({id, name, description, type, auto, audioFile, ...props}); }else{ refAudioBg.current.fade(1,0, fadeDuration * 1000); refNextCue.current = {id, name, description, type, auto, audioFile, ...props}; } }else{ playAudio(audioFile, props.loop || false, type); setCurrentCue({id, name, description, type, auto, audioFile, ...props}); } break; case CueType.Announce: playAudio(audioFile, props.loop || false, type); setCurrentCue({id, name, description, type, auto, audioFile, ...props}); break; case CueType.Light: // Handle light cue logic here console.log('Light cue:', {id, name, description, ...props}); setCurrentCue({id, name, description, type, auto, audioFile, ...props}); break; default: console.warn('Unknown cue type:', type); } } function stop() { console.log('Stop all audio'); if(refAudioBg.current) { refAudioBg.current.fade(1,0, fadeDuration*1000); } if(refAudioAnnounce.current) { refAudioAnnounce.current.fade(1,0, fadeDuration*1000); } } function secondToTime(seconds) { const minutes = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${minutes}:${secs < 10 ? '0' : ''}${secs}`; } function getAudioDuration(){ const type= refCue.current?.type; // console.log('getAudioDuration', type, refCue.current); // switch(type) { // case CueType.Bg: if(refAudioBg.current) { return `${secondToTime(refAudioBg.current.seek())} / ${secondToTime(refAudioBg.current.duration())} ${refAudioBg.current?.volume()}`; } else { return 'N/A'; } // case CueType.Announce: // return refAudioAnnounce.current ? `${secondToTime(refAudioAnnounce.current.seek())} / ${secondToTime(refAudioAnnounce.current.duration())}` : 'N/A'; // default: // return 'N/A'; // } } useEffect(()=>{ if(currentCue) { refCue.current = currentCue; // console.log('Current Cue:', currentCue); } },[currentCue]); useEffect(()=>{ fetch('/cuelist.json') .then(response => response.json()) .then(data => { console.log('Cuelist data:', data); setCuelist(data.cuelist); }) .catch(error => { console.error('Error fetching cuelist:', error); }); const intervalId = setInterval(() => { setTimestamp(getAudioDuration()); }, 500); return () => clearInterval(intervalId); },[]); return (
{currentCue ? `${currentCue.name}` : 'None'}

{timestamp}

{ const value = parseFloat(e.target.value); setFadeDuration(value); }}> {fadeDuration}s
{/* */} {cuelist?.map(({id, name, description, type, auto, audioFile,...props}, index) => ( ))}
ID Name Description Type Auto Audio / Due
{type==CueType.Announce && } {name} {description} {EmojiType[type]} {auto ? '⤵️' : ''} {audioFile || props.duration} {props.callback && `<${props.callback}>`}
); } export default App;