From 8676210d23b7908f775195f55e28a25e65d1d966 Mon Sep 17 00:00:00 2001 From: Nuwan Date: Tue, 9 Dec 2025 22:05:39 +0530 Subject: [PATCH] show tracks for audio inputs and session mix --- .../src/components/client/JKSessionMyTrack.js | 28 +-- .../components/client/JKSessionPluginModal.js | 167 ++++++++++++++++++ .../src/components/client/SessionTrackGain.js | 134 ++++++++------ 3 files changed, 261 insertions(+), 68 deletions(-) create mode 100644 jam-ui/src/components/client/JKSessionPluginModal.js diff --git a/jam-ui/src/components/client/JKSessionMyTrack.js b/jam-ui/src/components/client/JKSessionMyTrack.js index b4aa54ba6..598d4e7f6 100644 --- a/jam-ui/src/components/client/JKSessionMyTrack.js +++ b/jam-ui/src/components/client/JKSessionMyTrack.js @@ -6,6 +6,7 @@ import SessionTrackVU from './SessionTrackVU'; import SessionTrackGain from './SessionTrackGain'; import TrackDiagnostics from './TrackDiagnostics'; import JKSessionInstrumentModal from './JKSessionInstrumentModal'; +import JKSessionPluginModal from './JKSessionPluginModal'; import { UncontrolledTooltip } from 'reactstrap'; import { getInstrumentName } from '../../helpers/utils'; import { ASSIGNMENT } from '../../helpers/globals'; @@ -19,6 +20,7 @@ const JKSessionMyTrack = ({ track, mixers, hasMixer, name, trackName, instrument const [showMenu, setShowMenu] = useState(false); const [showDiagnostics, setShowDiagnostics] = useState(false); const [showInstrumentModal, setShowInstrumentModal] = useState(false); + const [showPluginModal, setShowPluginModal] = useState(false); useEffect(() => { if (mixers?.mixer) { @@ -75,9 +77,6 @@ const JKSessionMyTrack = ({ track, mixers, hasMixer, name, trackName, instrument > avatar -
- remote?: {JSON.stringify(isRemote)} -
{ console.log('Configure'); setShowMenu(false); }}>Configure
-
{ console.log('Instrument'); setShowMenu(false); }}>Instrument
- {!isRemote &&
{ console.log('Plugin'); setShowMenu(false); }}>Plugin
} +
{ setShowInstrumentModal(true); setShowMenu(false); }}>Instrument
+ {!isRemote &&
{ setShowPluginModal(true); setShowMenu(false); }}>Plugin
}
)} @@ -134,12 +133,19 @@ const JKSessionMyTrack = ({ track, mixers, hasMixer, name, trackName, instrument
{!isRemote && ( - + <> + + setShowPluginModal(false)} + trackNumber={ASSIGNMENT.TRACK1} + /> + )} ); diff --git a/jam-ui/src/components/client/JKSessionPluginModal.js b/jam-ui/src/components/client/JKSessionPluginModal.js new file mode 100644 index 000000000..6a09c0a9f --- /dev/null +++ b/jam-ui/src/components/client/JKSessionPluginModal.js @@ -0,0 +1,167 @@ +import React, { useState, useEffect } from 'react'; +import { + Modal, + ModalHeader, + ModalBody, + ModalFooter, + Button, + Form, + FormGroup, + Label, + Input, +} from 'reactstrap'; +import { useJamClient } from '../../context/JamClientContext'; + +const JKSessionPluginModal = ({ isOpen, toggle, trackNumber = 1 }) => { + const jamClient = useJamClient(); + const [selectedPlugin, setSelectedPlugin] = useState(''); + const [availablePlugins, setAvailablePlugins] = useState([]); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + if (isOpen) { + loadAvailablePlugins(); + } + }, [isOpen]); + + const loadAvailablePlugins = async () => { + try { + setIsLoading(true); + const plugins = await jamClient.VSTListVsts(); + setAvailablePlugins(plugins || []); + } catch (error) { + console.error('Failed to load VST plugins:', error); + setAvailablePlugins([]); + } finally { + setIsLoading(false); + } + }; + + const handlePluginChange = async (event) => { + const pluginId = event.target.value; + setSelectedPlugin(pluginId); + + if (pluginId) { + try { + await jamClient.VSTSetTrackAssignment(trackNumber, pluginId); + } catch (error) { + console.error('Failed to assign VST plugin:', error); + } + } + }; + + const handleOpenPluginApp = () => { + // Placeholder for opening plugin app + console.log('Open Plugin App clicked'); + }; + + const handleScanPlugins = async () => { + try { + setIsLoading(true); + await jamClient.VSTScan(() => { + // Callback after scan completes + loadAvailablePlugins(); + }); + } catch (error) { + console.error('Failed to scan for plugins:', error); + setIsLoading(false); + } + }; + + const handleManageFolders = async () => { + try { + const paths = await jamClient.VSTListSearchPaths(); + console.log('Current search paths:', paths); + // TODO: Implement folder management UI + } catch (error) { + console.error('Failed to get search paths:', error); + } + }; + + const handleClearPluginList = async () => { + if (window.confirm('Are you sure you want to clear the plugin list?')) { + try { + await jamClient.VSTClearAll(); + setAvailablePlugins([]); + setSelectedPlugin(''); + } catch (error) { + console.error('Failed to clear plugin list:', error); + } + } + }; + + return ( + + + VST/AU Virtual Instrument + + +
+ + + + + {availablePlugins.map((plugin, index) => ( + + ))} + + + + + + + + + + + +
+
+ + + +
+ ); +}; + +export default JKSessionPluginModal; diff --git a/jam-ui/src/components/client/SessionTrackGain.js b/jam-ui/src/components/client/SessionTrackGain.js index 107b7d843..c27db49f5 100644 --- a/jam-ui/src/components/client/SessionTrackGain.js +++ b/jam-ui/src/components/client/SessionTrackGain.js @@ -1,15 +1,10 @@ import React, { useState, useRef, useEffect, useCallback } from 'react'; +import PropTypes from 'prop-types'; import './VolumeSlider.css'; // Keep the same CSS for now import { useMixersContext } from '../../context/MixersContext'; import useFaderHelpers from '../../hooks/useFaderHelpers'; -const SessionTrackGain = ({ - mixers, - gainType, - controlGroup, - sessionController, - orientation = 'vertical' -}) => { +const SessionTrackGain = ({ mixers, gainType, controlGroup, sessionController, orientation = 'vertical' }) => { //console.debug("SessionTrackGain: Rendering for mixers", mixers, "gainType", gainType, "controlGroup", controlGroup); const mixerHelper = useMixersContext(); @@ -35,6 +30,7 @@ const SessionTrackGain = ({ }, [getMixer, faderHelpers]); const [currentValue, setCurrentValue] = useState(getInitialValue); + const [displayedVolume, setDisplayedVolume] = useState(getMixer()?.volume_left || 0); const prevVolumeRef = useRef(); // Update value when mixer changes or volume changes @@ -48,69 +44,80 @@ const SessionTrackGain = ({ if (mixer && currentVolume !== undefined) { const newValue = faderHelpers.convertAudioTaperToPercent(currentVolume); - console.log("SessionTrackGain: initial value", newValue); + console.log('SessionTrackGain: initial value', newValue); setCurrentValue(newValue); + setDisplayedVolume(currentVolume); mixerHelper.initGain(mixer); } else { setCurrentValue(50); // fallback + setDisplayedVolume(0); } } }, [mixers, faderHelpers, mixerHelper]); - // Function to calculate and update the slider's position and value - const updateSlider = useCallback(async (clientPos) => { - if (!sliderTrackRef.current) return; + const updateSlider = useCallback( + async clientPos => { + if (!sliderTrackRef.current) return; - const trackRect = sliderTrackRef.current.getBoundingClientRect(); - let positionInTrack, trackSize; + const trackRect = sliderTrackRef.current.getBoundingClientRect(); + let positionInTrack, trackSize; - if (orientation === 'vertical') { - // Calculate position from the BOTTOM of the track - positionInTrack = trackRect.bottom - clientPos; - trackSize = trackRect.height; - } else { - // Calculate position from the LEFT of the track - positionInTrack = clientPos - trackRect.left; - trackSize = trackRect.width; - } + if (orientation === 'vertical') { + // Calculate position from the BOTTOM of the track + positionInTrack = trackRect.bottom - clientPos; + trackSize = trackRect.height; + } else { + // Calculate position from the LEFT of the track + positionInTrack = clientPos - trackRect.left; + trackSize = trackRect.width; + } - // Clamp the position within the valid track bounds (0 to trackSize) - const clampedPosition = Math.max(0, Math.min(positionInTrack, trackSize)); + // Clamp the position within the valid track bounds (0 to trackSize) + const clampedPosition = Math.max(0, Math.min(positionInTrack, trackSize)); - // Calculate the percentage - const percentage = clampedPosition / trackSize * 100; + // Calculate the percentage + const percentage = (clampedPosition / trackSize) * 100; - // Update the mixer - now mixers is a real mixer object directly - if (mixers) { - await mixerHelper.faderChanged( - { percentage, dragging: isDragging }, - mixers, // Pass the real mixer object directly - gainType, - controlGroup - ); - } + // Update the mixer - now mixers is a real mixer object directly + if (mixers) { + await mixerHelper.faderChanged( + { percentage, dragging: isDragging }, + mixers, // Pass the real mixer object directly + gainType, + controlGroup + ); + } - console.log("SessionTrackGain: updateSlider", { clientPos, clampedPosition, percentage }); + console.log('SessionTrackGain: updateSlider', { clientPos, clampedPosition, percentage }); - // Update local state for UI - setCurrentValue(Math.round(percentage)); + // Update local state for UI + setCurrentValue(Math.round(percentage)); - // Directly manipulating the DOM for immediate visual updates during drag - if (orientation === 'vertical') { - sliderThumbRef.current.style.bottom = `${clampedPosition}px`; - sliderFillRef.current.style.height = `${clampedPosition}px`; - } else { - sliderThumbRef.current.style.left = `${clampedPosition}px`; - sliderFillRef.current.style.width = `${clampedPosition}px`; - } - }, [orientation, mixers, gainType, controlGroup, mixerHelper, isDragging]); + // Update displayed volume during dragging + const newVolumeDb = faderHelpers.convertPercentToAudioTaper(percentage); + setDisplayedVolume(newVolumeDb); + + // Directly manipulating the DOM for immediate visual updates during drag + if (orientation === 'vertical') { + sliderThumbRef.current.style.bottom = `${clampedPosition}px`; + sliderFillRef.current.style.height = `${clampedPosition}px`; + } else { + sliderThumbRef.current.style.left = `${clampedPosition}px`; + sliderFillRef.current.style.width = `${clampedPosition}px`; + } + }, + [orientation, mixers, faderHelpers, mixerHelper, isDragging, gainType, controlGroup] + ); // Mouse Move and Up Handlers (Global listeners) - const handleMouseMove = useCallback((e) => { - e.preventDefault(); // Prevent text selection - updateSlider(orientation === 'vertical' ? e.clientY : e.clientX); - }, [updateSlider, orientation]); + const handleMouseMove = useCallback( + e => { + e.preventDefault(); // Prevent text selection + updateSlider(orientation === 'vertical' ? e.clientY : e.clientX); + }, + [updateSlider, orientation] + ); const handleMouseUp = useCallback(() => { setIsDragging(false); @@ -122,7 +129,7 @@ const SessionTrackGain = ({ }, [handleMouseMove]); // Mouse Down Handler (Starts the drag) - const handleMouseDown = (e) => { + const handleMouseDown = e => { e.preventDefault(); setIsDragging(true); // Add global listeners when dragging starts @@ -134,7 +141,7 @@ const SessionTrackGain = ({ }; // Click on Track Handler (Jump to value) - const handleTrackClick = (e) => { + const handleTrackClick = e => { updateSlider(orientation === 'vertical' ? e.clientY : e.clientX); }; @@ -157,19 +164,32 @@ const SessionTrackGain = ({ return (
-
{getMixer()?.volume_left || 0}db
-
+
+ {displayedVolume}db +
+
-
+
+ />
); }; +SessionTrackGain.propTypes = { + mixers: PropTypes.object, + gainType: PropTypes.string, + controlGroup: PropTypes.string, + sessionController: PropTypes.object, + orientation: PropTypes.string +}; + export default SessionTrackGain;