show tracks for audio inputs and session mix

This commit is contained in:
Nuwan 2025-12-09 22:05:39 +05:30
parent baba15693b
commit 8676210d23
3 changed files with 261 additions and 68 deletions

View File

@ -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
>
<img src={photoUrl} alt="avatar" />
</div>
<div>
remote?: {JSON.stringify(isRemote)}
</div>
<div
className="track-instrument"
id={`instrument-tooltip-${clientId}-${track?.client_track_id || 'chat'}`}
@ -119,8 +118,8 @@ const JKSessionMyTrack = ({ track, mixers, hasMixer, name, trackName, instrument
{showMenu && (
<div className="track-menu">
<div onClick={() => { console.log('Configure'); setShowMenu(false); }}>Configure</div>
<div onClick={() => { console.log('Instrument'); setShowMenu(false); }}>Instrument</div>
{!isRemote && <div onClick={() => { console.log('Plugin'); setShowMenu(false); }}>Plugin</div>}
<div onClick={() => { setShowInstrumentModal(true); setShowMenu(false); }}>Instrument</div>
{!isRemote && <div onClick={() => { setShowPluginModal(true); setShowMenu(false); }}>Plugin</div>}
</div>
)}
</div>
@ -134,12 +133,19 @@ const JKSessionMyTrack = ({ track, mixers, hasMixer, name, trackName, instrument
<br className="clearall" />
</div>
{!isRemote && (
<JKSessionInstrumentModal
isOpen={showInstrumentModal}
toggle={handleInstrumentModalClose}
currentInstrument={track?.instrument}
onSave={handleInstrumentSave}
/>
<>
<JKSessionInstrumentModal
isOpen={showInstrumentModal}
toggle={handleInstrumentModalClose}
currentInstrument={track?.instrument}
onSave={handleInstrumentSave}
/>
<JKSessionPluginModal
isOpen={showPluginModal}
toggle={() => setShowPluginModal(false)}
trackNumber={ASSIGNMENT.TRACK1}
/>
</>
)}
</div>
);

View File

@ -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 (
<Modal isOpen={isOpen} toggle={toggle} modalClassName="theme-modal" contentClassName="border" size="md">
<ModalHeader toggle={toggle} className="bg-light d-flex flex-between-center border-bottom-0">
VST/AU Virtual Instrument
</ModalHeader>
<ModalBody>
<Form>
<FormGroup>
<Label for="pluginSelect">VST/AU Plugin:</Label>
<Input
type="select"
id="pluginSelect"
value={selectedPlugin}
onChange={handlePluginChange}
disabled={isLoading}
>
<option value="">Select a plugin...</option>
{availablePlugins.map((plugin, index) => (
<option key={index} value={plugin.id || plugin}>
{plugin.name || plugin}
</option>
))}
</Input>
</FormGroup>
<FormGroup className="text-center">
<Button
color="primary"
onClick={handleOpenPluginApp}
disabled={!selectedPlugin}
>
Open Plugin App
</Button>
</FormGroup>
<FormGroup>
<Label>Manage Plugins:</Label>
<div className="d-flex flex-column">
<a
href="#"
onClick={(e) => { e.preventDefault(); handleScanPlugins(); }}
className="mb-2"
style={{ color: '#007bff', textDecoration: 'none' }}
>
Scan for new or updated plugins
</a>
<a
href="#"
onClick={(e) => { e.preventDefault(); handleManageFolders(); }}
className="mb-2"
style={{ color: '#007bff', textDecoration: 'none' }}
>
Manage folders for plugin scans
</a>
<a
href="#"
onClick={(e) => { e.preventDefault(); handleClearPluginList(); }}
style={{ color: '#007bff', textDecoration: 'none' }}
>
Clear plugin list
</a>
</div>
</FormGroup>
</Form>
</ModalBody>
<ModalFooter>
<Button color="secondary" onClick={toggle}>
Close
</Button>
</ModalFooter>
</Modal>
);
};
export default JKSessionPluginModal;

View File

@ -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 (
<div>
<div id="slider-value"><small>{getMixer()?.volume_left || 0}db</small></div>
<div className={orientation === 'vertical' ? "vertical-slider-container" : "horizontal-slider-container"} onClick={handleTrackClick}>
<div id="slider-value">
<small>{displayedVolume}db</small>
</div>
<div
className={orientation === 'vertical' ? 'vertical-slider-container' : 'horizontal-slider-container'}
onClick={handleTrackClick}
>
<div className="slider-track" ref={sliderTrackRef}>
<div className={`slider-fill ${orientation === 'horizontal' ? 'horizontal' : ''}`} ref={sliderFillRef}></div>
<div className={`slider-fill ${orientation === 'horizontal' ? 'horizontal' : ''}`} ref={sliderFillRef} />
<div
className={`slider-thumb ${orientation === 'horizontal' ? 'horizontal' : ''}`}
ref={sliderThumbRef}
onMouseDown={handleMouseDown}
></div>
/>
</div>
</div>
</div>
);
};
SessionTrackGain.propTypes = {
mixers: PropTypes.object,
gainType: PropTypes.string,
controlGroup: PropTypes.string,
sessionController: PropTypes.object,
orientation: PropTypes.string
};
export default SessionTrackGain;