diff --git a/src/ParseqUI.js b/src/ParseqUI.js index 3966e46..afdb45e 100644 --- a/src/ParseqUI.js +++ b/src/ParseqUI.js @@ -128,7 +128,7 @@ const ParseqUI = (props) => { // Resize grid to fit content and update graph view if last frame changes. // TODO: UI optimisation: only need to do this keyframes have changed, could store a prevKeyframes and deepEquals against it. if (keyframes) { - const gridContainer = document.querySelector(".ag-theme-alpine"); + const gridContainer = document.querySelector("#grid-container"); if (gridContainer) { if (gridHeight === 0) { // auto-size grid to fit content diff --git a/src/components/AudioWaveform.tsx b/src/components/AudioWaveform.tsx index b874ada..9eb6a86 100644 --- a/src/components/AudioWaveform.tsx +++ b/src/components/AudioWaveform.tsx @@ -19,6 +19,8 @@ import { SmallTextField } from "./SmallTextField"; import { TabPanel } from "./TabPanel"; import { BiquadFilter } from "./BiquadFilter"; import { useHotkeys } from 'react-hotkeys-hook' +import { SupportedColorScheme, experimental_extendTheme as extendTheme, useColorScheme } from "@mui/material/styles"; +import { themeFactory } from "../theme"; type AudioWaveformProps = { fps: number, @@ -54,6 +56,10 @@ export function AudioWaveform(props: AudioWaveformProps) { const [lastViewport, setLastViewport] = useState({ startFrame: 0, endFrame: 0 }); const [tab, setTab] = useState(1); + // if the user clicks on the waveform or pauses the track, we capture the position + // so that we can repeatedly play back from that position with ctrl+space + const [capturedPos, setCapturedPos] = useState(0); + const [audioBuffer, setAudioBuffer] = useState(); const [unfilteredAudioBuffer, setUnfilteredAudioBuffer] = useState(); const [isAnalysing, setIsAnalysing] = useState(false); @@ -77,7 +83,10 @@ export function AudioWaveform(props: AudioWaveformProps) { /* eslint-disable @typescript-eslint/no-unused-vars */ const [showSpectrogram, setShowSpectrogram] = useState(false); - + const theme = extendTheme(themeFactory()); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const {colorScheme, setColorScheme } = useColorScheme(); + const palette = theme.colorSchemes[(colorScheme||'light') as SupportedColorScheme].palette; // Triggered when user makes viewport changes outside of wavesurfer, and we need to update wavesurfer. const scrollToPosition = useCallback((startFrame: number) => { @@ -149,6 +158,11 @@ export function AudioWaveform(props: AudioWaveformProps) { setManualEvents(newMarkers); }, [manualEvents]); + const handleClick = useCallback((event: any) => { + const time = wavesurferRef.current?.getCurrentTime(); + setCapturedPos(time || 0); + }, []); + const handleMarkerDrop = useCallback((marker: Marker) => { //console.log("In handleMarkerDrop", marker); const draggedEventType = deduceMarkerType(marker); @@ -233,6 +247,15 @@ export function AudioWaveform(props: AudioWaveformProps) { } }, [handleDoubleClick]); + useEffect(() => { + //wavesurferRef.current?.on("dblclick", handleDoubleClick); + wavesurferRef.current?.drawer?.on("lick", handleClick); + return () => { + //wavesurferRef.current?.un("dblclick", handleDoubleClick); + wavesurferRef.current?.drawer?.un("click", handleClick); + } + }, [handleClick]); + useEffect(() => { wavesurferRef.current?.on("marker-drop", handleMarkerDrop); return () => { @@ -330,18 +353,20 @@ export function AudioWaveform(props: AudioWaveformProps) { } } - function playPause(fromStart:boolean = false) { - if (isPlaying) { + function playPause(from : number = -1, pauseIfPlaying = true) { + if (isPlaying && pauseIfPlaying) { wavesurferRef.current?.pause(); + setCapturedPos(wavesurferRef.current?.getCurrentTime() || 0); } else { - if (fromStart) { - wavesurferRef.current?.seekTo(0); + if (from>=0) { + wavesurferRef.current?.setCurrentTime(from); + } if (!isPlaying) { + wavesurferRef.current?.play(); } - wavesurferRef.current?.play(); } - setIsPlaying(!isPlaying); + setIsPlaying(wavesurferRef.current?.isPlaying() ?? false ); updatePlaybackPos(); - } + } const updateMarkers = useCallback(() => { wavesurferRef.current?.markers.clear(); @@ -477,6 +502,13 @@ export function AudioWaveform(props: AudioWaveformProps) { container: "#timeline", formatTimeCallback: formatTimeCallback, timeInterval: timeInterval, + unlabeledNotchColor: palette.graphBorder.main, + primaryColor: palette.graphBorder.dark, + secondaryColor: palette.graphBorder.light, + primaryFontColor: palette.graphFont.main, + secondaryFontColor: palette.graphFont.light, + fontFamily: 'Arial', + fontSize: 10, })); wavesurferRef.current.initPlugin('timeline'); // HACK to force the timeline position to update. @@ -488,7 +520,7 @@ export function AudioWaveform(props: AudioWaveformProps) { // Force the wavesurfer to redraw the timeline wavesurferRef.current.drawBuffer(); } - }, [formatTimeCallback, props.viewport.startFrame, scrollToPosition]); + }, [formatTimeCallback, props.viewport.startFrame, scrollToPosition, palette]); const waveSurferPlugins = [ { @@ -616,11 +648,15 @@ export function AudioWaveform(props: AudioWaveformProps) { useHotkeys('space', () => { playPause(); - }, {preventDefault:true}, [isPlaying, updatePlaybackPos]) + }, {preventDefault:true}, [playPause]); useHotkeys('shift+space', () => { - playPause(true); - }, {preventDefault:true}, [isPlaying, updatePlaybackPos]) + playPause(0, false); + }, {preventDefault:true}, [playPause]); + + useHotkeys('ctrl+space', () => { + playPause(capturedPos, false); + }, {preventDefault:true}, [playPause, capturedPos]); useHotkeys('shift+a', () => { const time = wavesurferRef.current?.getCurrentTime(); @@ -631,6 +667,8 @@ export function AudioWaveform(props: AudioWaveformProps) { }, {preventDefault:true}, [manualEvents]) + + return <> @@ -675,6 +713,8 @@ export function AudioWaveform(props: AudioWaveformProps) { minPxPerSec={10} autoCenter={false} interact={true} + cursorColor={palette.success.light} + cursorWidth={3} />
diff --git a/src/components/ParseqGrid.tsx b/src/components/ParseqGrid.tsx index 6206c96..5446188 100644 --- a/src/components/ParseqGrid.tsx +++ b/src/components/ParseqGrid.tsx @@ -144,12 +144,12 @@ export const ParseqGrid = forwardRef(({ rangeSelection, onSelectRange, onGridRea cellStyle: (params: any): any => { if (isInRangeSelection(params)) { return { - backgroundColor: theme.vars.palette.gridInfoField.light, + backgroundColor: theme.vars.palette.gridInfoField.dark, borderRight: isSameCellPosition(params, params.api.getFocusedCell()) ? '' : '1px solid ' + theme.vars.palette.gridColSeparatorMajor.main } } else { return { - backgroundColor: theme.vars.palette.gridInfoField.main, + backgroundColor: theme.vars.palette.gridInfoField.light, borderRight: isSameCellPosition(params, params.api.getFocusedCell()) ? '' : '1px solid '+ theme.vars.palette.gridColSeparatorMajor.main } } @@ -246,7 +246,7 @@ export const ParseqGrid = forwardRef(({ rangeSelection, onSelectRange, onGridRea }), [fps, bpm]); - return
+ return
{/* @ts-ignore */} { graphBorder: palette.augmentColor({ color: { main: 'rgba(0, 0, 0, 0.1)' } }), graphBackground: palette.augmentColor({ color: { main: 'rgba(0, 0, 0, 0.1)' } }), graphFont: palette.augmentColor({ color: { main: '#666' } }), - gridInfoField: palette.augmentColor({ color: { main: '#eee' } }), + gridInfoField: palette.augmentColor({ color: { main: '#e5e5e5' } }), gridColSeparatorMajor: palette.augmentColor({ color: { main: '#000' } }), gridColSeparatorMinor: palette.augmentColor({ color: { main: '#ccc' } }), codeBackground: palette.augmentColor({ color: { main: '#ccc' } }),