wip session screen mixer data and showing vu for mixers
This commit is contained in:
parent
aedcc2ed65
commit
a5e6a4644d
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -14,4 +14,5 @@
|
|||
@import './custom/audio_player';
|
||||
@import './custom/landing';
|
||||
@import './custom/buttons';
|
||||
@import './custom/session';
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
#tracks {
|
||||
margin-top:12px;
|
||||
overflow:auto;
|
||||
|
||||
&.no-local-tracks {
|
||||
|
||||
#session-mytracks-notracks {
|
||||
display:block;
|
||||
}
|
||||
|
||||
#session-mytracks-container {
|
||||
display:none;
|
||||
}
|
||||
|
||||
#recording-start-stop {
|
||||
display:none;
|
||||
}
|
||||
|
||||
#session-invite-musicians {
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.track-empty a {
|
||||
color:#aaa;
|
||||
}
|
||||
|
||||
table.vu td {
|
||||
border: 3px solid #770e0e;
|
||||
}
|
||||
|
||||
.track-vu-left {
|
||||
position:absolute;
|
||||
bottom:8px;
|
||||
left:0px;
|
||||
}
|
||||
|
||||
.track-vu-right {
|
||||
position:absolute;
|
||||
bottom:8px;
|
||||
right:0px;
|
||||
}
|
||||
|
||||
.vu-red-off {
|
||||
background-color:#6c1709;
|
||||
}
|
||||
|
||||
.vu-red-on {
|
||||
background-color:#e73619;
|
||||
}
|
||||
|
||||
.vu-green-off {
|
||||
background-color:#244105;
|
||||
}
|
||||
|
||||
.vu-green-on {
|
||||
background-color:#72a43b;
|
||||
}
|
||||
|
||||
.track-label {
|
||||
position: absolute;
|
||||
text-align:center;
|
||||
width: 55px;
|
||||
height: 15px;
|
||||
min-height: 11px;
|
||||
max-height: 33px;
|
||||
max-width: 55px;
|
||||
white-space:normal;
|
||||
top: 3px;
|
||||
left: 7px;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
text-overflow:ellipsis;
|
||||
}
|
||||
|
||||
.track-close {
|
||||
position:absolute;
|
||||
top:3px;
|
||||
right:3px;
|
||||
width:12px;
|
||||
height:12px;
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import React from 'react'
|
||||
|
||||
const DebugPage = () => {
|
||||
return (
|
||||
<div>DebugPage</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DebugPage
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
import React, { useEffect, useContext, useState, memo } from 'react'
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import useJamServer, { ConnectionStatus } from '../../hooks/useJamServer'
|
||||
//import useJamServer, { ConnectionStatus } from '../../hooks/useJamServer'
|
||||
import useGearUtils from '../../hooks/useGearUtils'
|
||||
import useSessionUtils from '../../hooks/useSessionUtils.js';
|
||||
import useSessionModel from '../../hooks/useSessionModel.js';
|
||||
|
|
@ -10,9 +10,10 @@ import useSessionHelper from '../../hooks/useSessionHelper.js';
|
|||
import useRecordingHelpers from '../../hooks/useRecordingHelpers.js';
|
||||
import useMixerStore from '../../hooks/useMixerStore.js';
|
||||
|
||||
|
||||
import { useCurrentSession } from '../../context/CurrentSessionContext.js';
|
||||
import { useCurrentSessionContext } from '../../context/CurrentSessionContext.js';
|
||||
import { useJamServerContext } from '../../context/JamServerContext.js';
|
||||
import { useJamKazamApp } from '../../context/JamKazamAppContext.js';
|
||||
import { useMixersContext } from '../../context/MixersContext.js';
|
||||
|
||||
import { getSessionHistory, getSession, joinSession as joinSessionRest } from '../../helpers/rest';
|
||||
|
||||
|
|
@ -26,15 +27,6 @@ import SessionTrackVU from './SessionTrackVU.js';
|
|||
const JKSessionScreen = () => {
|
||||
const logger = console; // Replace with another logging mechanism if needed
|
||||
const app = useJamKazamApp();
|
||||
const {
|
||||
isConnected,
|
||||
connectionStatus,
|
||||
reconnectAttempts,
|
||||
lastError,
|
||||
jamClient,
|
||||
server,
|
||||
registerMessageCallback,
|
||||
} = useJamServer(process.env.REACT_APP_WEBSOCKET_GATEWAY_URL);
|
||||
|
||||
const {
|
||||
guardAgainstInvalidConfiguration,
|
||||
|
|
@ -42,19 +34,23 @@ const JKSessionScreen = () => {
|
|||
guardAgainstSinglePlayerProfile,
|
||||
} = useGearUtils();
|
||||
|
||||
const { initialize: initializeMixer, mixers, onSessionChange } = useMixerStore();
|
||||
|
||||
const { currentSession, setCurrentSession, currentSessionIdRef, setCurrentSessionId, inSession } = useCurrentSession();
|
||||
|
||||
const { initialize: initializeMixer, onSessionChange } = useMixerStore();
|
||||
const mixerHelper = useMixersContext();
|
||||
const { isConnected,
|
||||
ConnectionStatus,
|
||||
connectionStatus,
|
||||
reconnectAttempts,
|
||||
lastError,
|
||||
jamClient,
|
||||
server,
|
||||
registerMessageCallback } = useJamServerContext();
|
||||
const { currentSession, setCurrentSession, currentSessionIdRef, setCurrentSessionId, inSession } = useCurrentSessionContext();
|
||||
const { getCurrentRecordingState, reset: resetRecordingState } = useRecordingHelpers();
|
||||
|
||||
const { SessionPageEnter } = useSessionUtils();
|
||||
|
||||
// Use the session model hook
|
||||
const sessionModel = useSessionModel(app, server, null); // sessionScreen is null for now
|
||||
|
||||
const sessionHelper = useSessionHelper();
|
||||
|
||||
const { id: sessionId } = useParams();
|
||||
|
||||
// State to hold session data
|
||||
|
|
@ -66,9 +62,13 @@ const JKSessionScreen = () => {
|
|||
const [sessionRules, setSessionRules] = useState(null);
|
||||
const [subscriptionRules, setSubscriptionRules] = useState(null);
|
||||
const [currentOrLastSession, setCurrentOrLastSession] = useState(null);
|
||||
const [sessionGuardsPassed, setSessionGuardsPassed] = useState(false);
|
||||
const [myTracks, setMyTracks] = useState([]);
|
||||
const [mixers, setMixers] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isConnected || !jamClient) return;
|
||||
console.debug("JKSessionScreen: -DEBUG- isConnected changed to true");
|
||||
guardOnJoinSession();
|
||||
}, [isConnected, jamClient]); // Added jamClient to dependencies for stability
|
||||
|
||||
|
|
@ -112,8 +112,7 @@ const JKSessionScreen = () => {
|
|||
await ensureAppropriateProfile(musicianAccessOnJoin)
|
||||
logger.log("user has passed all session guards")
|
||||
|
||||
// all checks passed; join the session
|
||||
await joinSession();
|
||||
setSessionGuardsPassed(true)
|
||||
|
||||
} catch (error) {
|
||||
logger.error("User profile is not appropriate for session:", error);
|
||||
|
|
@ -149,7 +148,13 @@ const JKSessionScreen = () => {
|
|||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!sessionGuardsPassed || userTracks.length === 0 || hasJoined) { return }
|
||||
joinSession();
|
||||
}, [sessionGuardsPassed, userTracks, hasJoined])
|
||||
|
||||
const joinSession = async () => {
|
||||
|
||||
await jamClient.SessionRegisterCallback("JK.HandleBridgeCallback2");
|
||||
await jamClient.RegisterRecordingCallbacks("JK.HandleRecordingStartResult", "JK.HandleRecordingStopResult", "JK.HandleRecordingStarted", "JK.HandleRecordingStopped", "JK.HandleRecordingAborted");
|
||||
|
||||
|
|
@ -159,6 +164,8 @@ const JKSessionScreen = () => {
|
|||
const parentClientId = await jamClient.getParentClientId();
|
||||
console.debug('role when joining session: ' + clientRole + ', parentClientId: ' + parentClientId);
|
||||
|
||||
|
||||
|
||||
if (clientRole === 0) {
|
||||
clientRole = 'child';
|
||||
} else if (clientRole === 1) {
|
||||
|
|
@ -174,11 +181,25 @@ const JKSessionScreen = () => {
|
|||
|
||||
// tell the server we want to join
|
||||
|
||||
const clientId = await jamClient.GetClientID();
|
||||
console.debug("joining session " + sessionId + " as client " + clientId + " with role " + clientRole + " and parent client " + parentClientId);
|
||||
|
||||
|
||||
//const clientId = await jamClient.clientID();
|
||||
const clientId = server.clientId;
|
||||
console.log("xxx joining session " + sessionId + " as client " + JSON.stringify(clientId) + " with role " + clientRole + " and parent client " + parentClientId);
|
||||
|
||||
const latency = await jamClient.FTUEGetExpectedLatency().latency
|
||||
|
||||
console.log("joinSession parameters: ", {
|
||||
client_id: clientId,
|
||||
ip_address: server.publicIP,
|
||||
as_musician: true,
|
||||
tracks: userTracks,
|
||||
session_id: sessionId,
|
||||
client_role: clientRole,
|
||||
parent_client_id: parentClientId,
|
||||
audio_latency: latency,
|
||||
});
|
||||
|
||||
joinSessionRest({
|
||||
client_id: clientId,
|
||||
ip_address: server.publicIP,
|
||||
|
|
@ -189,11 +210,13 @@ const JKSessionScreen = () => {
|
|||
parent_client_id: parentClientId,
|
||||
audio_latency: latency,
|
||||
}).then(async (response) => {
|
||||
console.debug("join session response: ", response);
|
||||
console.debug("joinSessionRest response received", response.errors);
|
||||
if (response.errors) {
|
||||
throw new Error("Unable to join session: " + JSON.stringify(response.errors));
|
||||
} else {
|
||||
|
||||
const data = await response.json();
|
||||
console.debug("join session response xxx: ", data);
|
||||
setHasJoined(true);
|
||||
if (!inSession()) {
|
||||
// the user has left the session before they got joined. We need to issue a leave again to the server to make sure they are out
|
||||
|
|
@ -245,6 +268,7 @@ const JKSessionScreen = () => {
|
|||
}
|
||||
|
||||
}).catch((xhr) => {
|
||||
console.error("joinSessionRest error: ", xhr);
|
||||
let leaveBehavior;
|
||||
sessionModel.updateCurrentSession(null);
|
||||
|
||||
|
|
@ -252,19 +276,19 @@ const JKSessionScreen = () => {
|
|||
// we tried to join the session, but it is already gone. kick user back to join session screen
|
||||
|
||||
} else if (xhr.status === 422) {
|
||||
|
||||
const response = JSON.parse(xhr.responseText);
|
||||
if (response["errors"] && response["errors"]["tracks"] && (response["errors"]["tracks"][0] === "Please select at least one track")) {
|
||||
// You will need to reconfigure your audio device. show an alert
|
||||
} else if (response["errors"] && response["errors"]["music_session"] && (response["errors"]["music_session"][0] == ["is currently recording"])) {
|
||||
//The session is currently recording. You can not join a session that is recording. show an alert
|
||||
} else if (response["errors"] && response["errors"]["remaining_session_play_time"]) {
|
||||
//user has no available playtime. upgrade
|
||||
} else if (response["errors"] && response["errors"]["remaining_month_play_time"]) {
|
||||
//user has no available playtime. upgrade
|
||||
} else {
|
||||
// unknown 422 error. alert unable to join sessio
|
||||
}
|
||||
//console.error("unable to join session - 422 error");
|
||||
// const response = JSON.parse(xhr.responseText);
|
||||
// if (response["errors"] && response["errors"]["tracks"] && (response["errors"]["tracks"][0] === "Please select at least one track")) {
|
||||
// // You will need to reconfigure your audio device. show an alert
|
||||
// } else if (response["errors"] && response["errors"]["music_session"] && (response["errors"]["music_session"][0] == ["is currently recording"])) {
|
||||
// //The session is currently recording. You can not join a session that is recording. show an alert
|
||||
// } else if (response["errors"] && response["errors"]["remaining_session_play_time"]) {
|
||||
// //user has no available playtime. upgrade
|
||||
// } else if (response["errors"] && response["errors"]["remaining_month_play_time"]) {
|
||||
// //user has no available playtime. upgrade
|
||||
// } else {
|
||||
// // unknown 422 error. alert unable to join sessio
|
||||
// }
|
||||
} else {
|
||||
// unable to join session
|
||||
}
|
||||
|
|
@ -273,13 +297,10 @@ const JKSessionScreen = () => {
|
|||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (!isConnected) return;
|
||||
if (!sessionModel) return;
|
||||
if (!hasJoined) return;
|
||||
console.debug("JKSessionScreen: -DEBUG-", sessionHelper);
|
||||
console.log("isConnected or hasJoined changed:", { isConnected, hasJoined });
|
||||
if (!isConnected || !hasJoined) return;
|
||||
onSessionChange(sessionHelper);
|
||||
}, [isConnected, hasJoined, sessionHelper]); // Added sessionModel to dependencies for stability
|
||||
}, [isConnected, hasJoined]); // Added sessionModel to dependencies for stability
|
||||
|
||||
const ensureAppropriateProfile = async (musicianAccess) => {
|
||||
return new Promise(async function (resolve, reject) {
|
||||
|
|
@ -301,9 +322,6 @@ const JKSessionScreen = () => {
|
|||
const refreshCurrentSession = sessionModel.refreshCurrentSession;
|
||||
const updateSession = sessionModel.updateSession;
|
||||
|
||||
|
||||
|
||||
|
||||
// useEffect(() => {
|
||||
// if (!isConnected) return;
|
||||
// // validate session by fetching the session from the server
|
||||
|
|
@ -323,8 +341,6 @@ const JKSessionScreen = () => {
|
|||
// }
|
||||
// };
|
||||
|
||||
|
||||
|
||||
// Monitor connection status changes
|
||||
useEffect(() => {
|
||||
if (connectionStatus === ConnectionStatus.DISCONNECTED ||
|
||||
|
|
@ -335,19 +351,7 @@ const JKSessionScreen = () => {
|
|||
}
|
||||
}, [connectionStatus]);
|
||||
|
||||
|
||||
const loadSessionData = async () => {
|
||||
try {
|
||||
const audioConfigs = await jamClient.FTUEGetAllAudioConfigurations();
|
||||
const controlState = await jamClient.SessionGetAllControlState(true);
|
||||
const sampleRate = await jamClient.GetSampleRate();
|
||||
|
||||
logger.log('Session data loaded:', { audioConfigs, controlState, sampleRate });
|
||||
} catch (error) {
|
||||
logger.error('Error loading session data:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Handlers for recording and playback
|
||||
// const handleStartRecording = () => {
|
||||
// jamClient.StartRecording({ recordingId: `rec_${Date.now()}` });
|
||||
// };
|
||||
|
|
@ -394,6 +398,17 @@ const JKSessionScreen = () => {
|
|||
// // Handle VU meter updates
|
||||
// };
|
||||
|
||||
useEffect(() => {
|
||||
setMyTracks(mixerHelper.myTracks);
|
||||
console.log("My Tracks changing", mixerHelper.myTracks)
|
||||
// Update mixers based on myTracks
|
||||
const updatedMixers = mixerHelper.myTracks.map(track => {
|
||||
return track.mixers;
|
||||
}).filter(mixer => mixer !== undefined);
|
||||
console.log("Updated Mixers:", updatedMixers);
|
||||
setMixers(updatedMixers);
|
||||
}, [mixerHelper.myTracks])
|
||||
|
||||
return (
|
||||
<Card>
|
||||
{!isConnected && <div className='d-flex align-items-center'>Connecting to backend...</div>}
|
||||
|
|
@ -420,38 +435,33 @@ const JKSessionScreen = () => {
|
|||
<div className='audioInputs'>
|
||||
<h5>Audio Inputs</h5>
|
||||
<div className='d-flex' style={{ gap: '0.5rem', borderRight: '1px #ddd solid', paddingRight: '1rem' }}>
|
||||
{/* {participants.map((participant, index) => (
|
||||
<div key={participant.clientId} className='shadow-sm' style={{ border: '1px #ddd solid', width: '100px', height: '600px', backgroundColor: 'white' }}>
|
||||
<div className='d-flex flex-column' style={{ height: '100%' }}>
|
||||
<div className='p-2'>{participant.name}</div>
|
||||
<div className='p-2'>Instrument: {participant.instrument}</div>
|
||||
<div className='p-2'>Latency: {participant.latency}ms</div>
|
||||
<div className='p-2'>Volume Slider</div>
|
||||
<div className='p-2'>Controls</div>
|
||||
</div>
|
||||
</div>
|
||||
))} */}
|
||||
{
|
||||
JSON.stringify(mixers)
|
||||
}
|
||||
<SessionTrackVU lightCount={12} orientation="vertical" lightWidth={5} lightHeight={20} side="left" ptr="STV1" mixers={mixers} />
|
||||
<div>{
|
||||
myTracks.length === 0 ? (
|
||||
<div>No mixers available</div>
|
||||
) : (
|
||||
myTracks.map((track, index) => (
|
||||
<div key={track.id || index}>{
|
||||
<SessionTrackVU
|
||||
lightCount={13}
|
||||
orientation="vertical"
|
||||
lightWidth={5}
|
||||
lightHeight={7}
|
||||
side="best"
|
||||
ptr={`track_vu_${track.id || index}`}
|
||||
mixers={track.mixers}
|
||||
/>
|
||||
}</div>
|
||||
))
|
||||
)
|
||||
}</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='sessionMix'>
|
||||
<h5>Session Mix</h5>
|
||||
<div className='d-flex' style={{ gap: '0.5rem' }}>
|
||||
{/* {mixers.slice(0, 4).map((mixer, index) => (
|
||||
<div key={mixer.id} className='shadow-sm' style={{ border: '1px #ddd solid', width: '100px', height: '600px', backgroundColor: 'white' }}>
|
||||
<div className='d-flex flex-column align-items-center' style={{ height: '100%', padding: '10px' }}>
|
||||
<div className='mb-2'>Mixer {index + 1}</div>
|
||||
<div className='mb-2'>ID: {mixer.id}</div>
|
||||
<div className='mb-2'>Volume: {mixer.volume_left}dB</div>
|
||||
<div className='mb-2'>Mute: {mixer.mute ? 'Yes' : 'No'}</div>
|
||||
<div className='mb-2'>Type: {mixer.group_id}</div>
|
||||
</div>
|
||||
</div>
|
||||
))} */}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,47 +1,43 @@
|
|||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import useVuHelpers from '../../hooks/useVuHelpers';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { useVuContext } from '../../context/VuContext';
|
||||
|
||||
function SessionTrackVU({ lightCount, orientation, lightWidth, lightHeight, side, ptr, mixers }) {
|
||||
const { renderVU, registerVU, unregisterVU } = useVuHelpers();
|
||||
const [registered, setRegistered] = useState(null);
|
||||
const { VuMeter, updateVuState } = useVuContext();
|
||||
const ptrRef = useRef(ptr || `STV${Date.now()}`);
|
||||
const containerRef = useRef(null);
|
||||
const mixerIdRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
console.debug("SessionTrackVU: useEffect - registering VU", { mixers, registered, side, orientation, lightCount });
|
||||
const mixer = mixers?.vuMixer;
|
||||
|
||||
if (!mixer) return;
|
||||
|
||||
let mixerChanged = false;
|
||||
if (registered && mixer) {
|
||||
if (registered.mixer.id !== mixer.id) {
|
||||
unregisterVU(registered.mixer, registered.ptr);
|
||||
mixerChanged = true;
|
||||
}
|
||||
if (!mixer) {
|
||||
mixerIdRef.current = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mixerChanged && (registered || !mixer)) return;
|
||||
// Create a unique ID for this VU meter
|
||||
const mixerId = `${mixer.mode ? 'M' : 'P'}${mixer.id}`;
|
||||
mixerIdRef.current = mixerId;
|
||||
|
||||
const horizontal = orientation === 'horizontal';
|
||||
const lights = containerRef.current.querySelectorAll('td');
|
||||
registerVU(side, mixer, ptrRef.current, horizontal, lightCount, lights);
|
||||
setRegistered({ mixer, ptr: ptrRef.current });
|
||||
}, [mixers]);
|
||||
console.debug("SessionTrackVU: VU registered for mixer", mixerId);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (registered) {
|
||||
unregisterVU(registered.mixer, registered.ptr);
|
||||
// Cleanup: reset VU state when component unmounts or mixer changes
|
||||
if (mixerIdRef.current) {
|
||||
updateVuState(mixerIdRef.current, 0, false);
|
||||
mixerIdRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [registered, unregisterVU]);
|
||||
}, [mixers, updateVuState]);
|
||||
|
||||
const vuType = orientation === 'horizontal' ? 'horizontal' : 'vertical';
|
||||
// Use the React component for rendering
|
||||
return (
|
||||
<div ref={containerRef}>
|
||||
{renderVU({ vuType, lightCount, lightWidth, lightHeight })}
|
||||
</div>
|
||||
<VuMeter
|
||||
mixerId={mixerIdRef.current}
|
||||
orientation={orientation}
|
||||
lightCount={lightCount}
|
||||
lightWidth={lightWidth}
|
||||
lightHeight={lightHeight}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
import useVuHelpers from '../../hooks/useVuHelpers';
|
||||
|
||||
function VUMeter() {
|
||||
const vuRef = React.useRef(null);
|
||||
const { renderVU, updateVU, registerVU } = useVuHelpers();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (vuRef.current) {
|
||||
registerVU(vuRef.current);
|
||||
}
|
||||
}, [vuRef, registerVU]);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div ref={vuRef}>
|
||||
{renderVU({ vuType: 'vertical', lightCount: 12 })}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
import React, { createContext, useContext, useState, useRef } from 'react';
|
||||
import React, { createContext, useContext, useState, useRef, useEffect } from 'react';
|
||||
//import useMixerHelper from '../hooks/useMixerHelper';
|
||||
import { useJamServerContext } from './JamServerContext';
|
||||
|
||||
const CurrentSessionContext = createContext(null);
|
||||
|
||||
|
|
@ -6,6 +8,16 @@ export const CurrentSessionProvider = ({ children }) => {
|
|||
const [currentSession, setCurrentSession] = useState({});
|
||||
const currentSessionIdRef = useRef(null);
|
||||
|
||||
//const mixerHelper = useMixerHelper();
|
||||
// const { isConnected,
|
||||
// connectionStatus,
|
||||
// reconnectAttempts,
|
||||
// lastError,
|
||||
// jamClient,
|
||||
// server,
|
||||
// registerMessageCallback,
|
||||
// ConnectionStatus } = useJamServerContext();
|
||||
|
||||
const inSession = () => {
|
||||
return currentSessionIdRef.current !== null;
|
||||
};
|
||||
|
|
@ -29,4 +41,4 @@ export const CurrentSessionProvider = ({ children }) => {
|
|||
);
|
||||
};
|
||||
|
||||
export const useCurrentSession = () => useContext(CurrentSessionContext);
|
||||
export const useCurrentSessionContext = () => useContext(CurrentSessionContext);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
import React, { createContext, useContext } from 'react';
|
||||
import useJamServer, { ConnectionStatus } from '../hooks/useJamServer';
|
||||
|
||||
const JamServerContext = createContext(null);
|
||||
|
||||
export const JamServerProvider = ({ children }) => {
|
||||
const { isConnected,
|
||||
connectionStatus,
|
||||
reconnectAttempts,
|
||||
lastError,
|
||||
jamClient,
|
||||
server,
|
||||
registerMessageCallback } = useJamServer(process.env.REACT_APP_WEBSOCKET_GATEWAY_URL);
|
||||
|
||||
return (
|
||||
<JamServerContext.Provider value={{
|
||||
ConnectionStatus,
|
||||
connectionStatus,
|
||||
isConnected,
|
||||
jamClient,
|
||||
server,
|
||||
reconnectAttempts,
|
||||
lastError,
|
||||
registerMessageCallback,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</JamServerContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useJamServerContext = () => useContext(JamServerContext);
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import React, { createContext, useContext } from 'react';
|
||||
import useMixerHelper from '../hooks/useMixerHelper.js';
|
||||
|
||||
const MixersContext = createContext();
|
||||
|
||||
export const MixersProvider = ({ children }) => {
|
||||
const mixerHelper = useMixerHelper();
|
||||
|
||||
return (
|
||||
<MixersContext.Provider value={mixerHelper}>
|
||||
{children}
|
||||
</MixersContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useMixersContext = () => {
|
||||
const context = useContext(MixersContext);
|
||||
if (!context) {
|
||||
throw new Error('useMixersContext must be used within a MixersProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export default MixersContext;
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import React, { createContext, useContext } from 'react';
|
||||
import useVuHelpers from '../hooks/useVuHelpers.js';
|
||||
|
||||
const VuContext = createContext();
|
||||
|
||||
export const VuProvider = ({ children }) => {
|
||||
const vuHelpers = useVuHelpers();
|
||||
|
||||
return (
|
||||
<VuContext.Provider value={vuHelpers}>
|
||||
{children}
|
||||
</VuContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useVuContext = () => {
|
||||
const context = useContext(VuContext);
|
||||
if (!context) {
|
||||
throw new Error('useVuContext must be used within a VuProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export default VuContext;
|
||||
|
|
@ -536,3 +536,8 @@ export const EVENTS = {
|
|||
export const SYSTEM_DEFAULT_PLAYBACK_ONLY = 'System Default (Playback Only)';
|
||||
export const JAMKAZAM_VIRTUAL_INPUT = "JamKazam Virtual Input";
|
||||
export const SKIPPED_NETWORK_TEST = -1;
|
||||
|
||||
|
||||
export const getAvatarUrl = (photo_url) => {
|
||||
return photo_url ? photo_url : "/assets/shared/avatar_generic.png";
|
||||
};
|
||||
|
|
@ -342,6 +342,7 @@ const useGearUtils = () => {
|
|||
|
||||
const guardAgainstBadNetworkScore = useCallback((app) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
resolve();
|
||||
if (!(await validNetworkScore())) {
|
||||
reject();
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useRef, useState, useCallback } from 'react';
|
||||
import { useEffect, useRef, useState, useCallback, useMemo } from 'react';
|
||||
import { generateUUID, getCookieValue, createCookie } from '../helpers/utils';
|
||||
import { messageFactory, MessageType } from '../helpers/MessageFactory';
|
||||
import { useJamClient } from '../context/JamClientContext';
|
||||
|
|
@ -66,11 +66,15 @@ export default function useJamServer(url) {
|
|||
|
||||
try {
|
||||
const clientType = 'client'; //TODO: make dynamic
|
||||
const clientId = await getClientId(); //get from cookie or jamClient
|
||||
//const clientId = await jamClient.clientID(); #This currently retuens empty string. need to add this as a method to c++ client. for now use hardcoded uuid
|
||||
const clientId = generateUUID(); //TODO: replace with jamClient.clientID() when available
|
||||
console.log("_DEBUG_ clientId", clientId);
|
||||
server.current.clientId = clientId;
|
||||
|
||||
// Gather necessary parameters before establishing the connection
|
||||
// Use Promise.all to wait for all async calls to complete
|
||||
const operatingModePromise = await jamClient.getOperatingMode();
|
||||
console.log("_DEBUG_ operatingModePromise", operatingModePromise);
|
||||
const macHashPromise = await jamClient.SessionGetMacHash();
|
||||
const osStringPromise = await jamClient.GetDetailedOS();
|
||||
|
||||
|
|
@ -81,6 +85,7 @@ export default function useJamServer(url) {
|
|||
];
|
||||
|
||||
Promise.all(allPromises).then(async ([operatingMode, machine, osString]) => {
|
||||
console.log("_DEBUG_ ", operatingMode, machine, osString)
|
||||
const channelId = generateUUID();
|
||||
const rememberToken = getCookieValue('remember_token');
|
||||
|
||||
|
|
@ -224,7 +229,7 @@ export default function useJamServer(url) {
|
|||
}, [url, reconnectAttempts]);
|
||||
|
||||
const getClientId = (async () => {
|
||||
return getCookieValue('client_id') || await jamClient.GetClientID() || '';
|
||||
return getCookieValue('client_id') || await jamClient.clientID() || '';
|
||||
});
|
||||
|
||||
const rememberLogin = useCallback((rememberToken, clientId) => {
|
||||
|
|
@ -731,8 +736,9 @@ export default function useJamServer(url) {
|
|||
};
|
||||
}, []);
|
||||
|
||||
// Legacy compatibility - keep isConnected for existing code
|
||||
const isConnected = connectionStatus === ConnectionStatus.CONNECTED;
|
||||
const isConnected = useMemo(() => {
|
||||
return connectionStatus === ConnectionStatus.CONNECTED;
|
||||
}, [connectionStatus]);
|
||||
|
||||
return {
|
||||
// Legacy properties for backward compatibility
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,23 +1,30 @@
|
|||
import { useState, useCallback, useEffect, useRef } from 'react';
|
||||
import { throttle } from 'lodash'; // Add lodash for throttling
|
||||
import { useJamClient } from '../context/JamClientContext';
|
||||
//import { useJamClient } from '../context/JamClientContext';
|
||||
import { MIX_MODES } from '../helpers/globals';
|
||||
import { putTrackSyncChange } from '../helpers/rest';
|
||||
import useVuHelpers from './useVuHelpers';
|
||||
import { useCurrentSession } from '../context/CurrentSessionContext';
|
||||
import MixerHelper from '../helpers/MixerHelper';
|
||||
//import { useMixerHelper } from './useMixerHelper';
|
||||
import { useCurrentSessionContext } from '../context/CurrentSessionContext';
|
||||
import { useJamServerContext } from '../context/JamServerContext';
|
||||
import { useMixersContext } from '../context/MixersContext';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Custom hook for managing mixer store state and actions.
|
||||
* Converted from the original CoffeeScript Reflux store to modern React.
|
||||
*/
|
||||
export default function useMixerStore() {
|
||||
const jamClient = useJamClient();
|
||||
//const jamClient = useJamClient();
|
||||
const { currentSession, inSession, isConnected } = useCurrentSessionContext();
|
||||
const {
|
||||
jamClient,
|
||||
} = useJamServerContext();
|
||||
const mixerHelper = useMixersContext();
|
||||
|
||||
const logger = console; // Replace with your logging mechanism if needed
|
||||
const { updateVU } = useVuHelpers();
|
||||
const { currentSession, inSession } = useCurrentSession();
|
||||
//const { updateVU } = useVuHelpers();
|
||||
|
||||
|
||||
// State management
|
||||
const [metro, setMetro] = useState({ tempo: 120, cricket: false, sound: "Beep" });
|
||||
|
|
@ -27,9 +34,9 @@ export default function useMixerStore() {
|
|||
const [clientsWithAudioOverride, setClientsWithAudioOverride] = useState({});
|
||||
const [vuMeterUpdatePrefMap, setVuMeterUpdatePrefMap] = useState({ count: 0 });
|
||||
const [session, setSession] = useState(null);
|
||||
const [masterMixers, setMasterMixers] = useState(null);
|
||||
const [personalMixers, setPersonalMixers] = useState(null);
|
||||
const [mixers, setMixers] = useState([]);
|
||||
//const [masterMixers, setMasterMixers] = useState(null);
|
||||
//const [personalMixers, setPersonalMixers] = useState(null);
|
||||
//const [mixers, setMixers] = useState(null); //holds MixerHelper instance
|
||||
|
||||
// Refs for persistent values
|
||||
const appRef = useRef(null);
|
||||
|
|
@ -51,7 +58,7 @@ export default function useMixerStore() {
|
|||
// Initialize global callbacks
|
||||
useEffect(() => {
|
||||
if (!window.JK) window.JK = {};
|
||||
console.debug("MixerHelpers: setting up global callbacks");
|
||||
console.debug("MixerStore: setting up global callbacks", mixerHelper.mixers.current);
|
||||
//window.JK.HandleVolumeChangeCallback2 = handleVolumeChangeCallback;
|
||||
//window.JK.HandleMetronomeCallback2 = handleMetronomeCallback;
|
||||
window.JK.HandleBridgeCallback2 = handleBridgeCallback;
|
||||
|
|
@ -84,12 +91,6 @@ export default function useMixerStore() {
|
|||
// }
|
||||
}, [jamClient]);
|
||||
|
||||
// Issue change - trigger re-render/update
|
||||
const issueChange = useCallback(() => {
|
||||
// In React, state updates will trigger re-renders
|
||||
// This is equivalent to the original trigger method
|
||||
}, [session]);
|
||||
|
||||
// // Callback handlers
|
||||
// const handleVolumeChangeCallback = useCallback((mixerId, isLeft, value, isMuted) => {
|
||||
// // TODO: Visually update mixer
|
||||
|
|
@ -115,58 +116,59 @@ export default function useMixerStore() {
|
|||
// issueChange();
|
||||
// }, [session, masterMixers, personalMixers, noAudioUsers, clientsWithAudioOverride, issueChange]);
|
||||
|
||||
const handleBridgeCallback = useCallback(throttle((...args) => {
|
||||
|
||||
const vuMeterUpdateRate = localStorage.getItem('vuMeterUpdateRate') || 'fast';
|
||||
setVuMeterUpdatePrefMap(prev => {
|
||||
const newCount = (prev.count || 0) + 1;
|
||||
|
||||
if (vuMeterUpdateRate === 'medium' && newCount % 3 !== 0) {
|
||||
return { count: newCount };
|
||||
}
|
||||
if (vuMeterUpdateRate === 'slow' && newCount % 9 !== 0) {
|
||||
return { count: newCount };
|
||||
}
|
||||
const handleBridgeCallback = useCallback((vuData) => {
|
||||
let eventName = null;
|
||||
let mixerId = null;
|
||||
const value = null;
|
||||
let vuInfo = null;
|
||||
|
||||
// Reset count and process VU data
|
||||
setVuMeterUpdatePrefMap({ count: 0 });
|
||||
// const vuMeterUpdateRate = localStorage.getItem('vuMeterUpdateRate') || 'fast';
|
||||
// this.vuMeterUpdatePrefMap['count'] = (this.vuMeterUpdatePrefMap['count'] || 0) + 1;
|
||||
|
||||
// Parse arguments into vuData format: groups of 3 (eventName, mixerId, value)
|
||||
const vuData = [];
|
||||
for (let i = 0; i < args.length; i += 3) {
|
||||
if (args[i] === "vu" && i + 2 < args.length) {
|
||||
const eventName = args[i];
|
||||
const mixerId = args[i + 1];
|
||||
const value = args[i + 2];
|
||||
// Format: [eventName, mixerId, mode, leftValue, leftClipping, rightValue, rightClipping]
|
||||
// For fake client, mode=0, clipping=false, left=right=value
|
||||
vuData.push([eventName, mixerId, 0, value, false, value, false]);
|
||||
// if ((vuMeterUpdateRate === 'medium') && ((this.vuMeterUpdatePrefMap['count'] % 3) !== 0)) {
|
||||
// // skip this update
|
||||
// return;
|
||||
// }
|
||||
// if ((vuMeterUpdateRate === 'slow') && ((this.vuMeterUpdatePrefMap['count'] % 9) !== 0)) {
|
||||
// // skip this update
|
||||
// return;
|
||||
// }
|
||||
//console.log('vuMeterUpdateRate', vuMeterUpdateRate, @vuMeterUpdatePrefMap['count'])
|
||||
//this.vuMeterUpdatePrefMap['count'] = 0;
|
||||
|
||||
//console.log("_DEBUG_ XXX: ", vuData);
|
||||
|
||||
|
||||
//const result = [];
|
||||
for (vuInfo of vuData) {
|
||||
eventName = vuInfo[0];
|
||||
const vuVal = 0.0;
|
||||
if (eventName === "vu") {
|
||||
mixerId = vuInfo[1];
|
||||
const mode = vuInfo[2];
|
||||
const leftValue = vuInfo[3];
|
||||
const leftClipping = vuInfo[4];
|
||||
const rightValue = vuInfo[5];
|
||||
const rightClipping = vuInfo[6];
|
||||
// TODO - no guarantee range will be -80 to 20. Get from the
|
||||
// GetControlState for this mixer which returns min/max
|
||||
// value is a DB value from -80 to 20. Convert to float from 0.0-1.0
|
||||
//console.log('handleBridgeCallback@mixers',@mixers)
|
||||
|
||||
console.log("mixerHelper.isReady", mixerHelper.isReady.current);
|
||||
if (mixerHelper.isReady.current) {
|
||||
console.log("mixerHelper handleBridgeCallback: ", mixerId, mode, leftValue, rightValue, leftClipping, rightClipping);
|
||||
|
||||
mixerHelper.updateVU(mixerId, mode, (leftValue + 80) / 80, leftClipping, (rightValue + 80) / 80, rightClipping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const vuInfo of vuData) {
|
||||
const eventName = vuInfo[0];
|
||||
if (eventName === "vu") {
|
||||
const mixerId = vuInfo[1];
|
||||
const mode = vuInfo[2];
|
||||
const leftValue = vuInfo[3];
|
||||
const leftClipping = vuInfo[4];
|
||||
const rightValue = vuInfo[5];
|
||||
const rightClipping = vuInfo[6];
|
||||
|
||||
console.debug("_DEBUG_ handleBridgeCallback: ", mixerId, mode, leftValue, rightValue, leftClipping, rightClipping);
|
||||
}, []);
|
||||
|
||||
setMixers(prevMixers => {
|
||||
if (!prevMixers) return prevMixers;
|
||||
updateVU(mixerId, mode, (leftValue + 80) / 80, leftClipping, (rightValue + 80) / 80, rightClipping);
|
||||
return { ...prevMixers }; // Trigger re-render
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { count: 0 };
|
||||
});
|
||||
}, 100), []); // Throttle to max once per 100ms
|
||||
|
||||
// const handleBackingTrackSelectedCallback = useCallback(() => {
|
||||
// console.debug("backing track selected");
|
||||
|
|
@ -197,14 +199,16 @@ export default function useMixerStore() {
|
|||
const newMasterMixers = await jamClient.SessionGetAllControlState(true);
|
||||
const newPersonalMixers = await jamClient.SessionGetAllControlState(false);
|
||||
|
||||
setMasterMixers(newMasterMixers);
|
||||
setPersonalMixers(newPersonalMixers);
|
||||
// Initialize the mixerHelper with the constructor parameters
|
||||
console.debug("MixerStore: onSessionChange: initializing mixerHelper", { newSession, newMasterMixers, newPersonalMixers, metro, noAudioUsers, clientsWithAudioOverride });
|
||||
|
||||
const mixerHelper = new MixerHelper(newSession, newMasterMixers, newPersonalMixers, metro, noAudioUsers, clientsWithAudioOverride, mixers?.mixMode || MIX_MODES.PERSONAL);
|
||||
console.debug("MixerStore: onSessionChange - new mixers", mixerHelper.allMixers);
|
||||
setMixers(mixerHelper.allMixers);
|
||||
mixerHelper.updateMixerData(newSession, newMasterMixers, newPersonalMixers, metro, noAudioUsers, clientsWithAudioOverride, MIX_MODES.PERSONAL);
|
||||
|
||||
// const m = useMixerHelper(newSession, newMasterMixers, newPersonalMixers, metro, noAudioUsers, clientsWithAudioOverride, mixers?.mixMode || MIX_MODES.PERSONAL);
|
||||
// console.debug("MixerStore: onSessionChange: ", m);
|
||||
// setMixers(m);
|
||||
}
|
||||
}, []);
|
||||
}, [jamClient, mixerHelper, metro, noAudioUsers, clientsWithAudioOverride, inSession, sessionEnded]);
|
||||
|
||||
// // Mixer actions
|
||||
// const onMute = useCallback(async (mixersToMute, muting) => {
|
||||
|
|
@ -470,7 +474,6 @@ export default function useMixerStore() {
|
|||
return {
|
||||
// State
|
||||
// session,
|
||||
mixers,
|
||||
// metro,
|
||||
// noAudioUsers,
|
||||
// clientsWithAudioOverride,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { useState, useCallback, useRef, useEffect, useContext } from 'react';
|
||||
import { useCurrentSession } from '../context/CurrentSessionContext';
|
||||
import { useCurrentSessionContext } from '../context/CurrentSessionContext';
|
||||
import { useJamKazamApp } from '../context/JamKazamAppContext';
|
||||
import { startRecording as startRecordingRest, stopRecording as stopRecordingRest, getRecordingPromise } from '../helpers/rest';
|
||||
|
||||
const useRecordingHelpers = (jamClient) => {
|
||||
const app = useJamKazamApp();
|
||||
const { currentSession } = useCurrentSession();
|
||||
const { currentSession, setCurrentSession, currentSessionIdRef, setCurrentSessionId, inSession } = useCurrentSessionContext();
|
||||
|
||||
// State variables from original RecordingModel
|
||||
const [currentRecording, setCurrentRecording] = useState(null);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { useMemo } from 'react';
|
||||
import { useCurrentSession } from '../context/CurrentSessionContext';
|
||||
import { useCurrentSessionContext } from '../context/CurrentSessionContext';
|
||||
|
||||
const useSessionHelper = () => {
|
||||
const { currentSession, inSession } = useCurrentSession();
|
||||
const { currentSession, inSession } = useCurrentSessionContext();
|
||||
|
||||
const sessionHelper = useMemo(() => {
|
||||
const inSessionCheck = () => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useState, useCallback, useRef, useEffect } from 'react';
|
||||
import { debounce } from 'lodash'; // Add lodash for debouncing
|
||||
import { useJamClient } from '../context/JamClientContext';
|
||||
import { useCurrentSession } from '../context/CurrentSessionContext';
|
||||
import { useCurrentSessionContext } from '../context/CurrentSessionContext';
|
||||
import useGearUtils from './useGearUtils';
|
||||
import useTrackHelpers from './useTrackHelpers';
|
||||
import useRecordingHelpers from './useRecordingHelpers';
|
||||
|
|
@ -33,7 +33,7 @@ export default function useSessionModel(app, server, sessionScreen) {
|
|||
const { isNoInputProfile } = useGearUtils();
|
||||
const { getTrackInfo, getUserTracks } = useTrackHelpers();
|
||||
const { getCurrentRecordingState, reset: resetRecordingState } = useRecordingHelpers();
|
||||
const { currentSession, setCurrentSession, currentSessionIdRef, setCurrentSessionId, inSession } = useCurrentSession();
|
||||
const { currentSession, setCurrentSession, currentSessionIdRef, setCurrentSessionId, inSession } = useCurrentSessionContext();
|
||||
|
||||
// State variables from original SessionModel
|
||||
const [userTracks, setUserTracks] = useState(null);
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
import React, { useCallback, useRef } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
const logger = console;
|
||||
|
||||
export default function useVuHelpers() {
|
||||
const registeredMixers = useRef({});
|
||||
const [vuStates, setVuStates] = useState({});
|
||||
|
||||
const createQualifiedId = useCallback(mixer => {
|
||||
return (mixer.mode ? 'M' : 'P') + mixer.id;
|
||||
}, []);
|
||||
|
||||
|
||||
|
||||
// Legacy renderVU for backward compatibility
|
||||
const renderVU = useCallback((userOptions = {}) => {
|
||||
const renderVUDefaults = {
|
||||
vuType: 'vertical',
|
||||
|
|
@ -25,7 +28,7 @@ export default function useVuHelpers() {
|
|||
lights.push(
|
||||
<td
|
||||
key={i}
|
||||
className="vulight vu-green-off"
|
||||
className="vulight vu-off"
|
||||
style={{
|
||||
width: vuType === 'horizontal' ? lightWidth : 'auto',
|
||||
height: vuType === 'horizontal' ? 'auto' : lightHeight
|
||||
|
|
@ -54,6 +57,7 @@ export default function useVuHelpers() {
|
|||
);
|
||||
}, []);
|
||||
|
||||
// Legacy updateVU for backward compatibility
|
||||
const updateVU = useCallback((containerRef, value) => {
|
||||
if (!containerRef?.current) return;
|
||||
|
||||
|
|
@ -70,7 +74,7 @@ export default function useVuHelpers() {
|
|||
|
||||
// Remove all light classes
|
||||
$lights.forEach($light => {
|
||||
$light.classList.remove('vu-green-off', 'vu-green-on', 'vu-red-off', 'vu-red-on');
|
||||
$light.classList.remove('vu-green-off', 'vu-green-on', 'vu-red-off', 'vu-red-on', 'vu-off');
|
||||
});
|
||||
|
||||
// Set the lights
|
||||
|
|
@ -91,129 +95,103 @@ export default function useVuHelpers() {
|
|||
}
|
||||
}, []);
|
||||
|
||||
const registerVU = useCallback(
|
||||
(type, mixer, someFunction, horizontal, lightCount, lights) => {
|
||||
const fqId = createQualifiedId(mixer);
|
||||
let registrations = registeredMixers.current[fqId];
|
||||
if (!registrations) {
|
||||
registrations = [];
|
||||
registeredMixers.current[fqId] = registrations;
|
||||
}
|
||||
|
||||
if (type === 'best') {
|
||||
registrations.push({ type, ptr: someFunction, ptrCount: 1, horizontal, lightCount, lights });
|
||||
} else {
|
||||
// find the right registration and add left lights or right lights to it
|
||||
let found = null;
|
||||
found = registrations.find(registration => registration.ptr === someFunction) || null;
|
||||
|
||||
if (!found) {
|
||||
found = { type, ptr: someFunction, ptrCount: 1, horizontal, lightCount };
|
||||
registrations.push(found);
|
||||
} else {
|
||||
found.ptrCount++;
|
||||
}
|
||||
|
||||
if (type === 'left') {
|
||||
found.leftLights = lights;
|
||||
} else {
|
||||
found.rightLights = lights;
|
||||
}
|
||||
}
|
||||
},
|
||||
[createQualifiedId]
|
||||
);
|
||||
|
||||
const unregisterVU = useCallback(
|
||||
(mixer, someFunction) => {
|
||||
const fqId = createQualifiedId(mixer);
|
||||
let registrations = registeredMixers.current[fqId];
|
||||
if (!registrations || registrations.length === 0) {
|
||||
logger.debug('no registration found for:' + fqId, registrations, registeredMixers.current);
|
||||
return;
|
||||
}
|
||||
|
||||
registrations = registrations.filter(element => {
|
||||
const isMatch = element.ptr === someFunction;
|
||||
|
||||
if (isMatch) {
|
||||
element.ptrCount--;
|
||||
|
||||
const keepRegistration = element.ptrCount > 0;
|
||||
if (!keepRegistration) {
|
||||
logger.debug('getting rid of the registration; no more ptrs');
|
||||
}
|
||||
return keepRegistration;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
registeredMixers.current[fqId] = registrations;
|
||||
},
|
||||
[createQualifiedId]
|
||||
);
|
||||
|
||||
const updateSingleVU = useCallback((horizontal, lightCount, $lights, value, isClipping) => {
|
||||
const lights = Math.round(value * lightCount);
|
||||
const redSwitch = Math.round(lightCount * 0.6666667);
|
||||
|
||||
// Remove all light classes from all lights
|
||||
$lights.forEach($light => {
|
||||
$light.classList.remove('vu-green-off', 'vu-green-on', 'vu-red-off', 'vu-red-on');
|
||||
});
|
||||
|
||||
// Set the lights
|
||||
for (let i = 0; i < lightCount; i++) {
|
||||
let colorClass = 'vu-green-';
|
||||
let state = 'on';
|
||||
if (i >= redSwitch) {
|
||||
colorClass = 'vu-red-';
|
||||
}
|
||||
if (i >= lights) {
|
||||
state = 'off';
|
||||
}
|
||||
|
||||
const lightIndex = horizontal ? i : lightCount - i - 1;
|
||||
if ($lights[lightIndex]) {
|
||||
$lights[lightIndex].classList.add(colorClass + state);
|
||||
}
|
||||
}
|
||||
// New React-like VU update function
|
||||
const updateVuState = useCallback((mixerId, level, clipping = false) => {
|
||||
setVuStates(prev => ({
|
||||
...prev,
|
||||
[mixerId]: { level, clipping }
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const updateVU3 = useCallback(
|
||||
(mixer, leftValue, leftClipping, rightValue, rightClipping) => {
|
||||
const fqId = createQualifiedId(mixer);
|
||||
logger.debug('useVuHelpers: updateVU3', { fqId, mixer, leftValue, rightValue });
|
||||
|
||||
const registrations = registeredMixers.current[fqId];
|
||||
if (registrations) {
|
||||
registrations.forEach(registration => {
|
||||
const { horizontal, lightCount } = registration;
|
||||
|
||||
if (registration.type === 'best') {
|
||||
const $lights = registration.lights;
|
||||
updateSingleVU(horizontal, lightCount, $lights, leftValue, leftClipping);
|
||||
} else {
|
||||
if (mixer.stereo) {
|
||||
updateSingleVU(horizontal, lightCount, registration.leftLights, leftValue, leftClipping);
|
||||
updateSingleVU(horizontal, lightCount, registration.rightLights, rightValue, rightClipping);
|
||||
} else {
|
||||
updateSingleVU(horizontal, lightCount, registration.leftLights, leftValue, leftClipping);
|
||||
updateSingleVU(horizontal, lightCount, registration.rightLights, leftValue, leftClipping);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// Update React state for declarative rendering
|
||||
updateVuState(fqId, leftValue, leftClipping);
|
||||
},
|
||||
[createQualifiedId, updateSingleVU]
|
||||
[createQualifiedId, updateVuState]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
// Cleanup on unmount
|
||||
setVuStates({});
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Create a proper React component that can be used outside the hook
|
||||
function VuMeterComponent({ mixerId, orientation = 'vertical', lightCount = 12, lightWidth = 3, lightHeight = 17 }) {
|
||||
const vuState = vuStates[mixerId] || { level: 0, clipping: false };
|
||||
const { level, clipping } = vuState;
|
||||
|
||||
const lights = [];
|
||||
for (let i = 0; i < lightCount; i++) {
|
||||
// Calculate if this light should be on based on the level
|
||||
const lightThreshold = (i + 1) / lightCount;
|
||||
const isOn = level >= lightThreshold;
|
||||
|
||||
let lightClass = 'vulight ';
|
||||
if (isOn) {
|
||||
if (clipping) {
|
||||
lightClass += 'vu-red-on';
|
||||
} else if (i >= Math.floor(lightCount * 0.75)) { // Red zone (top 25%)
|
||||
lightClass += 'vu-red-on';
|
||||
} else if (i >= Math.floor(lightCount * 0.5)) { // Yellow zone (middle 25%)
|
||||
lightClass += 'vu-yellow-on';
|
||||
} else { // Green zone (bottom 50%)
|
||||
lightClass += 'vu-green-on';
|
||||
}
|
||||
} else {
|
||||
lightClass += 'vu-off';
|
||||
}
|
||||
|
||||
console.debug('VuMeterComponent render', { i, lightThreshold, isOn, lightClass });
|
||||
|
||||
lights.push(
|
||||
<td
|
||||
key={i}
|
||||
className={lightClass}
|
||||
style={{
|
||||
width: orientation === 'horizontal' ? lightWidth : 'auto',
|
||||
height: orientation === 'horizontal' ? 'auto' : lightHeight
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const tableClass = `vu ${orientation}`;
|
||||
const tableStyle = orientation === 'horizontal' ? { display: 'inline-block' } : {};
|
||||
|
||||
return (
|
||||
<table className={tableClass} style={tableStyle}>
|
||||
{orientation === 'vertical' ? (
|
||||
<tbody>
|
||||
{lights.map((light, index) => (
|
||||
<tr key={index}>{light}</tr>
|
||||
))}
|
||||
</tbody>
|
||||
) : (
|
||||
<tbody>
|
||||
<tr>{lights}</tr>
|
||||
</tbody>
|
||||
)}
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
// React-like components and functions
|
||||
VuMeter: VuMeterComponent,
|
||||
updateVuState,
|
||||
vuStates,
|
||||
|
||||
// Legacy functions for backward compatibility
|
||||
renderVU,
|
||||
updateVU,
|
||||
registerVU,
|
||||
unregisterVU,
|
||||
updateVU3,
|
||||
updateSingleVU
|
||||
updateVU3
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Ported from web/app/assets/javascripts/jamClientProxy.js
|
||||
|
||||
import { QWebChannel } from 'qwebchannel'
|
||||
// import useJamServer from './hooks/useJamServer';
|
||||
|
||||
class Deferred {
|
||||
constructor(request_id) {
|
||||
|
|
@ -24,10 +25,10 @@ class JamClientProxy {
|
|||
this.request_id = 1;
|
||||
this.skipLogMethods = [];
|
||||
this.displayLogMethod = [];
|
||||
}
|
||||
// const server = useJamServer();
|
||||
// this.JamServer = server;
|
||||
|
||||
get JKFrontendMethods() {
|
||||
return Object.freeze({
|
||||
this.JKFrontendMethods = Object.freeze({
|
||||
UnknownJKAppMessage: this.enumAppCounter++,
|
||||
AbortRecording: this.enumAppCounter++,
|
||||
addUserBackingTracksToJamkazamAsset: this.enumAppCounter++,
|
||||
|
|
@ -455,7 +456,7 @@ class JamClientProxy {
|
|||
if (deferred) deferred.resolve(null);
|
||||
} else {
|
||||
let msg = JSON.parse(message);
|
||||
console.log("[jamClientProxy] Message received via QWebChannel: ", msg);
|
||||
//console.log("[jamClientProxy] Message received via QWebChannel: ", msg);
|
||||
let req_id = msg.request_id;
|
||||
let response = msg.response;
|
||||
let evt_id = msg.event_id;
|
||||
|
|
@ -467,7 +468,7 @@ class JamClientProxy {
|
|||
if (this.skipLogMethods.length > 0 && this.skipLogMethods.includes(methodName)) {
|
||||
// Skip logging
|
||||
} else if (this.displayLogMethod.includes(methodName)) {
|
||||
this.logger.log('[jamClientProxy] Message received via QWebChannel: ', msg);
|
||||
//this.logger.log('[jamClientProxy] Message received via QWebChannel: ', msg);
|
||||
}
|
||||
|
||||
deferred.resolve(response);
|
||||
|
|
@ -496,21 +497,11 @@ class JamClientProxy {
|
|||
socket.onerror = error => { }; // Handle error if needed
|
||||
}
|
||||
|
||||
|
||||
|
||||
handleEvent(evt_id, response) {
|
||||
// const method = Object.keys(response)[0]; // Available if needed for debugging
|
||||
|
||||
switch (evt_id.toString()) {
|
||||
case '3006': // execute_script
|
||||
if (!response['execute_script'].match('HandleBridgeCallback2')) {
|
||||
// this.logger.log(`[jamClientProxy] 3006 execute_script: ${response['execute_script']}`);
|
||||
}
|
||||
if (response['execute_script'].includes('HandleBridgeCallback2')) {
|
||||
//log this for now, need to investigate why this is causing issues
|
||||
this.logger.log(`[jamClientProxy] 3006 execute_script (skipping eval): ${response['execute_script']}`);
|
||||
break;
|
||||
}
|
||||
try {
|
||||
// eslint-disable-next-line no-eval
|
||||
eval(response['execute_script']);
|
||||
|
|
@ -534,7 +525,7 @@ class JamClientProxy {
|
|||
break;
|
||||
|
||||
case '3010': // JKVideoSession
|
||||
this.logger.log(`[jamClientProxy] 3010 JKVideoSession: ${response['JKVideoSession']['connect']}`);
|
||||
//this.logger.log(`[jamClientProxy] 3010 JKVideoSession: ${response['JKVideoSession']['connect']}`);
|
||||
const vidConnect = response['JKVideoSession']['connect'];
|
||||
if (this.context.ExternalVideoActions) {
|
||||
this.context.ExternalVideoActions.setVideoEnabled(vidConnect);
|
||||
|
|
@ -545,7 +536,7 @@ class JamClientProxy {
|
|||
break;
|
||||
|
||||
case '3011': // AudioFormatChangeEvent
|
||||
this.logger.log(`[jamClientProxy] 3011 AudioFormatChangeEvent: ${response['AudioFormat']}`);
|
||||
//this.logger.log(`[jamClientProxy] 3011 AudioFormatChangeEvent: ${response['AudioFormat']}`);
|
||||
const audioFormat = response['AudioFormat'];
|
||||
if (this.context.RecordingActions) {
|
||||
this.context.RecordingActions.audioRecordingFormatChanged(`.${audioFormat}`);
|
||||
|
|
@ -616,12 +607,15 @@ class JamClientProxy {
|
|||
}
|
||||
|
||||
sendMessage(prop, args) {
|
||||
|
||||
let appMessage = {
|
||||
request_id: ++this.request_id,
|
||||
arguments: Array.from(args) || [],
|
||||
method: this.JKFrontendMethods[prop]
|
||||
};
|
||||
|
||||
//console.log('_DEBUG_ appMessage', prop, appMessage);
|
||||
|
||||
let deferred = new Deferred(appMessage.request_id);
|
||||
|
||||
if (this.skipLogMethods.length > 0 && this.skipLogMethods.includes(prop)) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ import { JamKazamAppProvider } from '../context/JamKazamAppContext';
|
|||
import { AppDataProvider } from '../context/AppDataContext';
|
||||
import { AppRoutesProvider } from '../context/AppRoutesContext';
|
||||
import { JamClientProvider } from '../context/JamClientContext';
|
||||
import { JamServerProvider } from '../context/JamServerContext';
|
||||
import { CurrentSessionProvider } from '../context/CurrentSessionContext';
|
||||
import { MixersProvider } from '../context/MixersContext';
|
||||
import { VuProvider } from '../context/VuContext';
|
||||
|
||||
const JKClientLayout = ({ location }) => {
|
||||
|
||||
|
|
@ -19,9 +22,15 @@ const JKClientLayout = ({ location }) => {
|
|||
<JamKazamAppProvider>
|
||||
<BrowserQueryProvider>
|
||||
<JamClientProvider>
|
||||
<CurrentSessionProvider>
|
||||
<ClientRoutes />
|
||||
</CurrentSessionProvider>
|
||||
<JamServerProvider>
|
||||
<CurrentSessionProvider>
|
||||
<VuProvider>
|
||||
<MixersProvider>
|
||||
<ClientRoutes />
|
||||
</MixersProvider>
|
||||
</VuProvider>
|
||||
</CurrentSessionProvider>
|
||||
</JamServerProvider>
|
||||
</JamClientProvider>
|
||||
</BrowserQueryProvider>
|
||||
</JamKazamAppProvider>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import PropTypes from 'prop-types';
|
|||
import NavbarTop from '../components/navbar/JKNavbarTop';
|
||||
import NavbarVertical from '../components/navbar/JKNavbarVertical';
|
||||
import Footer from '../components/footer/JKFooter';
|
||||
import DebugPage from '../components/client/DebugPage';
|
||||
|
||||
// Import your page components here
|
||||
import JKSessionScreen from '../components/client/JKSessionScreen';
|
||||
|
|
@ -29,6 +30,7 @@ const JKClientRoutes = ({ match: { url } }) => {
|
|||
<NavbarTop logoWidth={240} />
|
||||
<Switch>
|
||||
<PrivateRoute exact path={`${url}/s/:id`} component={JKSessionScreenWithErrorBoundary} />
|
||||
{/* <PrivateRoute exact path={`${url}/s/:id`} component={DebugPage} /> */}
|
||||
{/*Redirect*/}
|
||||
<Redirect to="/errors/404" />
|
||||
</Switch>
|
||||
|
|
|
|||
Loading…
Reference in New Issue