From 311f38d1f083f232622d85773fdee2c7409ee495 Mon Sep 17 00:00:00 2001 From: Matthew Stratford Date: Tue, 27 Oct 2020 23:20:29 +0000 Subject: [PATCH 1/5] Add auto player duck on mic live. Closes #70 --- src/mixer/state.ts | 33 ++++++++++++++++++++--- src/showplanner/ProModeButtons.tsx | 42 +++++++++++++++++++++--------- 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/src/mixer/state.ts b/src/mixer/state.ts index 9c4cf08..7b3df58 100644 --- a/src/mixer/state.ts +++ b/src/mixer/state.ts @@ -38,6 +38,7 @@ interface PlayerState { volume: number; gain: number; trim: number; + micAutoDuck: boolean; timeCurrent: number; timeRemaining: number; timeLength: number; @@ -66,6 +67,7 @@ const BasePlayerState: PlayerState = { state: "stopped", volume: 1, gain: 0, + micAutoDuck: false, trim: defaultTrimDB, timeCurrent: 0, timeRemaining: 0, @@ -161,6 +163,15 @@ const mixerState = createSlice({ ) { state.players[action.payload.player].trim = action.payload.trim; }, + setPlayerMicAutoDuck( + state, + action: PayloadAction<{ + player: number; + enabled: boolean; + }> + ) { + state.players[action.payload.player].micAutoDuck = action.payload.enabled; + }, setLoadedItemIntro( state, action: PayloadAction<{ @@ -612,6 +623,8 @@ export const { toggleAutoAdvance, togglePlayOnLoad, toggleRepeat, + setTracklistItemID, + setPlayerMicAutoDuck, } = mixerState.actions; export const redrawWavesurfers = (): AppThunk => () => { @@ -620,8 +633,6 @@ export const redrawWavesurfers = (): AppThunk => () => { }); }; -export const { setTracklistItemID } = mixerState.actions; - const FADE_TIME_SECONDS = 1; export const setVolume = ( player: number, @@ -742,15 +753,31 @@ export const openMicrophone = (micID: string): AppThunk => async ( }; export const setMicVolume = (level: MicVolumePresetEnum): AppThunk => ( - dispatch + dispatch, + getState ) => { + const players = getState().mixer.players; + // no tween fuckery here, just cut the level const levelVal = level === "full" ? 1 : 0; // actually, that's a lie - if we're turning it off we delay it a little to compensate for // processing latency + if (levelVal !== 0) { dispatch(mixerState.actions.setMicLevels({ volume: levelVal })); + for (let player = 0; player < players.length; player++) { + // If we have auto duck enabled on this channel player, tell it to fade down. + if (players[player].micAutoDuck) { + dispatch(setVolume(player, "bed")); + } + } } else { + for (let player = 0; player < players.length; player++) { + // If we have auto duck enabled on this channel player, tell it to fade back up. + if (players[player].micAutoDuck) { + dispatch(setVolume(player, "full")); + } + } window.setTimeout(() => { dispatch(mixerState.actions.setMicLevels({ volume: levelVal })); // latency, plus a little buffer diff --git a/src/showplanner/ProModeButtons.tsx b/src/showplanner/ProModeButtons.tsx index adadfbf..9bcb496 100644 --- a/src/showplanner/ProModeButtons.tsx +++ b/src/showplanner/ProModeButtons.tsx @@ -1,31 +1,42 @@ import React, { useState } from "react"; -import { FaTachometerAlt } from "react-icons/fa"; +import { FaMicrophoneAlt, FaTachometerAlt } from "react-icons/fa"; import { useDispatch, useSelector } from "react-redux"; import { RootState } from "../rootReducer"; -import { setChannelTrim } from "../mixer/state"; +import { setChannelTrim, setPlayerMicAutoDuck } from "../mixer/state"; -type ButtonIds = "trim"; +type ButtonIds = "trim" | "autoDuck"; export default function ProModeButtons({ channel }: { channel: number }) { const [activeButton, setActiveButton] = useState(null); const trimVal = useSelector( (state: RootState) => state.mixer.players[channel]?.trim ); + const micAutoDuck = useSelector( + (state: RootState) => state.mixer.players[channel]?.micAutoDuck + ); const dispatch = useDispatch(); return ( <>
Pro Mode™ - {(activeButton === null || activeButton === "trim") && ( - - )} + + {activeButton === "trim" && ( <> - {trimVal} dB + {trimVal} dB )} + {activeButton === "autoDuck" && ( + + Duck on Mic: {micAutoDuck ? "Yes" : "No"} + + )}
); From 19ca99fbd2fc9dc5864695a2fe7912eb63e51315 Mon Sep 17 00:00:00 2001 From: Matthew Stratford Date: Tue, 10 Nov 2020 21:22:43 +0000 Subject: [PATCH 2/5] Split loaded track info out with separate selectors. --- src/showplanner/Player.tsx | 65 ++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/src/showplanner/Player.tsx b/src/showplanner/Player.tsx index e1ed3e0..1d9d4e0 100644 --- a/src/showplanner/Player.tsx +++ b/src/showplanner/Player.tsx @@ -197,6 +197,46 @@ function TimingButtons({ id }: { id: number }) { ); } +function LoadedTrackInfo({ id }: { id: number }) { + const dispatch = useDispatch(); + const loadedItem = useSelector( + (state: RootState) => state.mixer.players[id].loadedItem + ); + const loading = useSelector( + (state: RootState) => state.mixer.players[id].loading + ); + const loadError = useSelector( + (state: RootState) => state.mixer.players[id].loadError + ); + + return ( + + + {loadedItem !== null && loading === -1 + ? loadedItem.title + : loading !== -1 + ? `LOADING` + : loadError + ? "LOAD FAILED" + : "No Media Selected"} + + + Explicit + + + ); +} + export function Player({ id }: { id: number }) { // Define time remaining (secs) when the play icon should flash. const SECS_REMAINING_WARNING = 20; @@ -307,30 +347,7 @@ export function Player({ id }: { id: number }) { {proMode && }
- - - {playerState.loadedItem !== null && playerState.loading === -1 - ? playerState.loadedItem.title - : playerState.loading !== -1 - ? `LOADING` - : playerState.loadError - ? "LOAD FAILED" - : "No Media Selected"} - - - Explicit - - +
{playerState.loadedItem !== null && playerState.loading === -1 From f1c637120caa54c5d913f9cd0973a5ff2ffb2ed6 Mon Sep 17 00:00:00 2001 From: Matthew Stratford Date: Sun, 24 Jan 2021 00:27:34 +0000 Subject: [PATCH 3/5] Rejig the main ShowPlanner component to reduce rerenders --- src/session/index.tsx | 2 +- src/showplanner/index.tsx | 50 ++++++++++++++++++++++++++------------- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/session/index.tsx b/src/session/index.tsx index 7e52f66..caf0136 100644 --- a/src/session/index.tsx +++ b/src/session/index.tsx @@ -78,7 +78,7 @@ const SessionHandler: React.FC = function() { ); } - return

; + return <>; }; export default SessionHandler; diff --git a/src/showplanner/index.tsx b/src/showplanner/index.tsx index 26c4e8e..cd5d0a7 100644 --- a/src/showplanner/index.tsx +++ b/src/showplanner/index.tsx @@ -23,7 +23,7 @@ import { ResponderProvided, } from "react-beautiful-dnd"; -import { useSelector, useDispatch } from "react-redux"; +import { useSelector, useDispatch, shallowEqual } from "react-redux"; import { RootState } from "../rootReducer"; import { PlanItem, @@ -292,8 +292,9 @@ function incrReducer(state: number, action: any) { } const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) { - const { plan: showplan, planLoadError, planLoading } = useSelector( - (state: RootState) => state.showplan + const isShowplan = useSelector( + (state: RootState) => state.showplan.plan !== null, + shallowEqual ); // Tell Modals that #root is the main page content, for accessability reasons. @@ -408,26 +409,15 @@ const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) { }; }, [dispatch, session.currentTimeslot]); - if (showplan === null) { - return ( - - ); + if (!isShowplan) { + return ; } return (
-
- - - -
+ = function({ timeslotId }) { ); }; +function GettingShowPlanScreen() { + const { planLoading, planLoadError } = useSelector( + (state: RootState) => state.showplan + ); + return ( + + ); +} + export function LoadingDialogue({ title, subtitle, @@ -509,4 +513,16 @@ export function LoadingDialogue({ ); } +function ChannelStrips() { + const showplan = useSelector((state: RootState) => state.showplan.plan!); + + return ( +
+ + + +
+ ); +} + export default Showplanner; From cd0956b21946ed3cf5f50d06ec70a56bd01db59b Mon Sep 17 00:00:00 2001 From: Matthew Stratford Date: Sun, 24 Jan 2021 00:56:13 +0000 Subject: [PATCH 4/5] Limit FPS of VU meters to help weedy CPUs. --- src/optionsMenu/helpers/VUMeter.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/optionsMenu/helpers/VUMeter.tsx b/src/optionsMenu/helpers/VUMeter.tsx index 97c07d1..0e7791b 100644 --- a/src/optionsMenu/helpers/VUMeter.tsx +++ b/src/optionsMenu/helpers/VUMeter.tsx @@ -27,6 +27,8 @@ export function VUMeter(props: VUMeterProps) { const isMic = props.source.substr(0, 3) === "mic"; + const FPS = 30; // Limit the FPS so that lower spec machines have a better time juggling CPU. + useEffect(() => { const animate = () => { if (!isMic || isMicOpen) { @@ -38,7 +40,9 @@ export function VUMeter(props: VUMeterProps) { if (props.stereo) { setPeakR(result[1]); } - rafRef.current = requestAnimationFrame(animate); + setTimeout((current = rafRef.current, a = animate) => { + current = requestAnimationFrame(a); + }, 1000 / FPS); } }; if (!isMic || isMicOpen) { From bc8a06729c3a25167d206faea024b62e506db89f Mon Sep 17 00:00:00 2001 From: Matthew Stratford Date: Sun, 24 Jan 2021 20:06:33 +0000 Subject: [PATCH 5/5] Store the volumeEnum per player So that autoduck knows when it's not supposed to meddle. --- src/mixer/state.ts | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/src/mixer/state.ts b/src/mixer/state.ts index 274995f..48c95b7 100644 --- a/src/mixer/state.ts +++ b/src/mixer/state.ts @@ -36,6 +36,7 @@ interface PlayerState { loadError: boolean; state: PlayerStateEnum; volume: number; + volumeEnum: VolumePresetEnum; gain: number; trim: number; micAutoDuck: boolean; @@ -68,6 +69,7 @@ const BasePlayerState: PlayerState = { loading: -1, state: "stopped", volume: 1, + volumeEnum: "full", gain: 0, micAutoDuck: false, trim: defaultTrimDB, @@ -89,6 +91,7 @@ const mixerState = createSlice({ mic: { open: false, volume: 1, + volumeEnum: "full", gain: 1, baseGain: 0, openError: null, @@ -149,9 +152,12 @@ const mixerState = createSlice({ action: PayloadAction<{ player: number; volume: number; + volumeEnum: VolumePresetEnum; }> ) { state.players[action.payload.player].volume = action.payload.volume; + state.players[action.payload.player].volumeEnum = + action.payload.volumeEnum; }, setPlayerGain( state, @@ -722,7 +728,13 @@ export const setVolume = ( playerGainTweens[player].tweens.forEach((tween) => tween.pause()); if (playerGainTweens[player].target === level) { delete playerGainTweens[player]; - dispatch(mixerState.actions.setPlayerVolume({ player, volume: uiLevel })); + dispatch( + mixerState.actions.setPlayerVolume({ + player, + volume: uiLevel, + volumeEnum: level, + }) + ); dispatch(mixerState.actions.setPlayerGain({ player, gain: volume })); return; } @@ -737,7 +749,13 @@ export const setVolume = ( // If not fading, just do it. if (!fade) { - dispatch(mixerState.actions.setPlayerVolume({ player, volume: uiLevel })); + dispatch( + mixerState.actions.setPlayerVolume({ + player, + volume: uiLevel, + volumeEnum: level, + }) + ); dispatch(mixerState.actions.setPlayerGain({ player, gain: volume })); return; } @@ -756,7 +774,13 @@ export const setVolume = ( const volumeTween = new Between(currentLevel, uiLevel) .time(FADE_TIME_SECONDS * 1000) .on("update", (val: number) => { - dispatch(mixerState.actions.setPlayerVolume({ player, volume: val })); + dispatch( + mixerState.actions.setPlayerVolume({ + player, + volume: val, + volumeEnum: level, + }) + ); }); const gainTween = new Between(currentGain, volume) .time(FADE_TIME_SECONDS * 1000) @@ -869,14 +893,17 @@ export const setMicVolume = (level: MicVolumePresetEnum): AppThunk => ( dispatch(mixerState.actions.setMicLevels({ volume: levelVal })); for (let player = 0; player < players.length; player++) { // If we have auto duck enabled on this channel player, tell it to fade down. - if (players[player].micAutoDuck) { + if ( + players[player].micAutoDuck && + players[player].volumeEnum === "full" + ) { dispatch(setVolume(player, "bed")); } } } else { for (let player = 0; player < players.length; player++) { // If we have auto duck enabled on this channel player, tell it to fade back up. - if (players[player].micAutoDuck) { + if (players[player].micAutoDuck && players[player].volumeEnum === "bed") { dispatch(setVolume(player, "full")); } }