show tracks for audio inputs and session mix
This commit is contained in:
parent
baba15693b
commit
8676210d23
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue