diff --git a/src/App.jsx b/src/App.jsx index e87aa4e..10c53c8 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,5 +1,5 @@ import { useState, useEffect, useRef } from "react"; -import {Howl, Howler} from 'howler'; +import { Howl, Howler } from 'howler'; import { invoke } from "@tauri-apps/api/core"; import { listen } from '@tauri-apps/api/event'; import "./App.css"; @@ -8,165 +8,206 @@ import { Light } from "./utils/light"; const DefaultFadeDuration = 3; // 1 second -const CLIENT_COUNT=13; -const CUE_FILE='cuelist_0924.json'; +const CLIENT_COUNT = 13; +const CUE_FILE = 'cuelist_0924.json'; -const CueType={ - Bg: 'bg', - Announce: 'announce', - Light: 'light' +const CueType = { + Bg: 'bg', + Announce: 'announce', + Light: 'light' } -const EmojiType={ - bg: '🎵', - announce: '📢', - light: '💡' +const EmojiType = { + bg: '🎵', + announce: '📢', + light: '💡' } function App() { - + const [cuelist, setCuelist] = useState([]); const [currentCue, setCurrentCue] = useState(null); const [fadeDuration, setFadeDuration] = useState(DefaultFadeDuration); // Default fade duration in seconds - const [timestamp, setTimestamp] = useState(); const [clientStatus, setClientStatus] = useState({}); const refCue = useRef(null); // const refNextCue = useRef(null); - const refLight= useRef(); - const refDuration= useRef(); - const refTimer= useRef(); + const refLight = useRef(); + const refDuration = useRef(); + const refTotalTime=useRef(); + const refTimer = useRef(); - function sendOsc(addr, message, id){ + function sendOsc(addr, message, id) { invoke('send_osc_message', { - key: addr, - message: `${id}#${message}`, - host:'0.0.0.0:0', - target:'192.168.51.255:8000', + key: addr, + message: `${id}#${message}`, + host: '0.0.0.0:0', + target: '192.168.51.255:8000', }); } - function sendOscToSound(addr, message){ + function sendOscToSound(addr, message) { invoke('send_osc_message', { - key: addr, - message, - host:'0.0.0.0:0', - target:'192.168.51.100:58200', + key: addr, + message, + host: '0.0.0.0:0', + target: '192.168.51.100:58200', }); } - function onOsc(message){ - - const addr= message.addr; - const [id, status, name]= message.args[0]?.split('#'); - // console.log('receive osc:', id, status,name); + function onOsc(message) { + + const addr = message.addr; + const [id, status, name] = message.args[0]?.split('#'); + // console.log('receive osc:', id, status,name); - switch(addr){ + switch (addr) { case OSC_ADDRESS.CLIENT_STATUS: - - setClientStatus(prev=>({ - ...prev, - [id]:{ - status, - name, - timestamp: new Date().toLocaleTimeString(), - } + + setClientStatus(prev => ({ + ...prev, + [id]: { + status, + name, + timestamp: new Date().toLocaleTimeString(), + } })); break; case OSC_ADDRESS.CLIENT_DURATION: - setClientStatus(prev=>({ - ...prev, - [id]:{ - ...prev[id], - duration: status, - timestamp: new Date().toLocaleTimeString(), - } - })); + setClientStatus(prev => ({ + ...prev, + [id]: { + ...prev[id], + duration: status, + timestamp: new Date().toLocaleTimeString(), + } + })); break; case OSC_ADDRESS.CLIENT_INPUT: - setClientStatus(prev=>({ - ...prev, - [id]:{ - ...prev[id], - input: status, - timestamp: new Date().toLocaleTimeString(), - } - })); + setClientStatus(prev => ({ + ...prev, + [id]: { + ...prev[id], + input: status, + timestamp: new Date().toLocaleTimeString(), + } + })); break; } } - - + function getTotalLeftTime(id){ + const cue=cuelist.find(el=>el.id==id); + const index=cuelist.indexOf(cue); + if(index==-1) return; - function playCue({id, name, description, type, auto, audioFile, ...props}) { - - console.log('Playing cue:', {id, name, description, type, auto, audioFile, ...props}); - setCurrentCue({id, name, description, type, auto, audioFile, ...props}); - - // Handle other cue types and properties here - if(props.audioCue){ - sendOscToSound(OSC_ADDRESS.SCS_GO_CUE, props.audioCue); - } + let sum=0; + for(var i=index;ic.id==id+1); - - refTimer.current= setInterval(()=>{ - due--; - if(due<=0){ - clearInterval(refTimer.current); - refTimer.current=null; - if(auto){ - console.log('Auto play next cue:', next); - playCue(next); - } - } - refDuration.current.innerText= secondToTime(due); - },1000); + if (props.lightCue) { + // sendOsc(OSC_ADDRESS.CLIENT_INPUT, props.lightCue); + + if (props.lightCue == 'fade_in_light') refLight.current.fadeIn(); // Fade in light for conversation start + if (props.lightCue == 'fade_out_light') refLight.current.fadeOut(); // Fade out light for conversation end + + } + if (props.reset) { + //sendOsc(OSC_ADDRESS.RESETCUE,'','all'); + refLight.current.set(1); + + } + + if (refTimer.current) { + clearTimeout(refTimer.current); } + if (props.duration) { + + // calculate last time + let totalLefTime=getTotalLeftTime(id); + + // Clear any existing timer + + // Store the end time of the timer + const totalEndTime=new Date().getTime()+totalLefTime*1000; + const endTime = new Date().getTime() + (props.duration * 1000); + + // Function to update the timer + const tick = () => { + const now = new Date().getTime(); + const timeLeft = endTime - now; + const totalTimeLeft=totalEndTime-now; + //console.log(totalTimeLeft); + + if (timeLeft <= 0) { + // Timer has finished + clearTimeout(refTimer.current); + refTimer.current = null; + + if (auto) { + console.log('Auto play next cue:', next); + const next = cuelist.find(c => c.id === id + 1); + playCue(next); + } + return; + } + + // Update the displayed duration + refDuration.current.innerText = secondToTime(timeLeft / 1000); + refTotalTime.current.innerText=`${secondToTime(totalTimeLeft/1000)} / ${secondToTime(totalLefTime)}`; + + + + // Call the next tick + refTimer.current = setTimeout(tick, 100); + }; + + // Start the timer + console.log('Start timer:', props.duration); + refTimer.current = setTimeout(tick, 100); + } + } function stop() { console.log('Stop all'); - sendOsc(OSC_ADDRESS.STOPCUE,'','all'); + sendOsc(OSC_ADDRESS.STOPCUE, '', 'all'); sendOscToSound(OSC_ADDRESS.SCS_FADE_ALL, ''); // cear timer - if(refTimer.current) clearInterval(refTimer.current); + if (refTimer.current) clearInterval(refTimer.current); } function reset() { console.log('Reset all'); - sendOsc(OSC_ADDRESS.RESETCUE,'','all'); + sendOsc(OSC_ADDRESS.RESETCUE, '', 'all'); sendOscToSound(OSC_ADDRESS.SCS_STOP_ALL, ''); refLight.current.set(1); @@ -174,18 +215,18 @@ function App() { function secondToTime(seconds) { const minutes = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); - return `${minutes}:${secs < 10 ? '0' : ''}${secs}`; + return `${minutes.toString().padStart(2,'0')}:${secs < 10 ? '0' : ''}${secs}`; } - function getAudioDuration(){ + function getAudioDuration() { - const type= refCue.current?.type; + const type = refCue.current?.type; // console.log('getAudioDuration', type, refCue.current); // switch(type) { // case CueType.Bg: - if(refAudioBg.current) { + if (refAudioBg.current) { return `${secondToTime(refAudioBg.current.seek())} / ${secondToTime(refAudioBg.current.duration())} ${refAudioBg.current?.volume()}`; } else { - return 'N/A'; + return 'N/A'; } // case CueType.Announce: // return refAudioAnnounce.current ? `${secondToTime(refAudioAnnounce.current.seek())} / ${secondToTime(refAudioAnnounce.current.duration())}` : 'N/A'; @@ -195,138 +236,137 @@ function App() { } - useEffect(()=>{ - if(currentCue) { + useEffect(() => { + if (currentCue) { refCue.current = currentCue; // console.log('Current Cue:', currentCue); } - },[currentCue]); - useEffect(()=>{ - fetch(`/${CUE_FILE}`) - .then(response => response.json()) - .then(data => { - console.log('Cuelist data:', data); - setCuelist(data.cuelist); - }) - .catch(error => { - console.error('Error fetching cuelist:', error); - }); - - - listen('osc_message', (event) => { - // console.log(`Received OSC message: ${JSON.stringify(event.payload)}`); - onOsc(event.payload); + }, [currentCue]); + useEffect(() => { + fetch(`/${CUE_FILE}`) + .then(response => response.json()) + .then(data => { + console.log('Cuelist data:', data); + setCuelist(data.cuelist); + }) + .catch(error => { + console.error('Error fetching cuelist:', error); }); + listen('osc_message', (event) => { + // console.log(`Received OSC message: ${JSON.stringify(event.payload)}`); + onOsc(event.payload); + }); + - },[]); + + }, []); return (
-
-
-
-
{currentCue ? `${currentCue.name}` : 'None'}
-
-
-

- {timestamp} -

- - - - - - { - const value = parseFloat(e.target.value); - setFadeDuration(value); - }}> - {fadeDuration}s - -
- - - - {/* */} - - - - - - - - - - - - - {cuelist?.map(({id, name, description, type, auto,...props}, index) => ( - - - - - - - - - - - - ))} - -
IDNameDescriptionTypeAutoDueLightAudioClient
- - {name}{description}{EmojiType[type]}{auto ? '⤵️' : ''}{props.duration}{props.lightCue && `L${props.lightCue}`}{props.audioCue && `S${props.audioCue}`}{props.clientCue || ''}
-
- - - - - - - - - - +
+
+
+
{currentCue ? `${currentCue.name}` : 'None'}
+
+
+ + + + + + { + const value = parseFloat(e.target.value); + setFadeDuration(value); + }}> + {fadeDuration}s + +
+
idstatuscueinputduetimestamp
+ + + {/* */} + + + + + + + + + + + + + {cuelist?.map(({ id, name, description, type, auto, ...props }, index) => ( + + + + + + + + + + - - - {Array.from(Array(CLIENT_COUNT).keys()).map((i) => { - const id=(i+1).toString();//.padStart(2,'0'); - const log = clientStatus[id.toString()]; - return ( - - - {<> - - - - - - } - +
IDNameDescriptionTypeAutoDueLightAudioClient
+ + {name}{description}{EmojiType[type]}{auto ? '⤵️' : ''}{props.duration}{props.lightCue && `L${props.lightCue}`}{props.audioCue && `S${props.audioCue}`}{props.clientCue || ''}
{id}{log?.status} - {log?.name} - - {log?.input?.substr(log.input.length - 20)?.split('').map((ch,i)=>i%3<1?ch:'*').join('')} - {log?.duration}{log?.timestamp} - - - {cuelist?.filter(c=>c.clientCue && !c.debug).map((c, index)=>( - - ))} + ))} +
+
+ + + + + + + + + + + + + + + + {Array.from(Array(CLIENT_COUNT).keys()).map((i) => { + const id = (i + 1).toString();//.padStart(2,'0'); + const log = clientStatus[id.toString()]; + return ( + + + {<> + + + - - ); - })} - -
idstatuscueinputduetimestamp
{id}{log?.status} + {log?.name} + + {log?.input?.substr(log.input.length - 20)?.split('').map((ch, i) => i % 3 < 1 ? ch : '*').join('')}
+ {log?.duration} + {log?.timestamp} + } + + + + {cuelist?.filter(c => c.clientCue && !c.debug).map((c, index) => ( + + ))} + + + ); + })} + +
); } diff --git a/src/utils/light.jsx b/src/utils/light.jsx index 1af44bc..6e52959 100644 --- a/src/utils/light.jsx +++ b/src/utils/light.jsx @@ -2,7 +2,7 @@ import gsap from "gsap" import { forwardRef, useEffect, useImperativeHandle, useRef } from "react" import { invoke } from '@tauri-apps/api/core'; -const FADE_TIME=5; +const FADE_TIME=8; export const Light=forwardRef((props, ref)=>{ @@ -90,7 +90,7 @@ export const Light=forwardRef((props, ref)=>{
{/* */} - + {/* */}