diff --git a/src/mixer/audio.ts b/src/mixer/audio.ts index a9567db..5107469 100644 --- a/src/mixer/audio.ts +++ b/src/mixer/audio.ts @@ -21,6 +21,8 @@ const PlayerEmitter: StrictEmitter< > = EventEmitter as any; class Player extends ((PlayerEmitter as unknown) as { new (): EventEmitter }) { + private volume = 1; + private trim = 1; private constructor( private readonly engine: AudioEngine, private wavesurfer: WaveSurfer, @@ -64,7 +66,13 @@ class Player extends ((PlayerEmitter as unknown) as { new (): EventEmitter }) { } setVolume(val: number) { - this.wavesurfer.setVolume(val); + this.volume = val; + this.wavesurfer.setVolume(this.volume * this.trim); + } + + setTrim(val: number) { + this.trim = val; + this.wavesurfer.setVolume(this.volume * this.trim); } public static create(engine: AudioEngine, player: number, url: string) { diff --git a/src/mixer/state.ts b/src/mixer/state.ts index 9bd395e..48dd9b9 100644 --- a/src/mixer/state.ts +++ b/src/mixer/state.ts @@ -35,6 +35,7 @@ interface PlayerState { state: PlayerStateEnum; volume: number; gain: number; + trim: number; timeCurrent: number; timeRemaining: number; timeLength: number; @@ -63,6 +64,7 @@ const BasePlayerState: PlayerState = { state: "stopped", volume: 1, gain: 1, + trim: 1, timeCurrent: 0, timeRemaining: 0, timeLength: 0, @@ -139,6 +141,15 @@ const mixerState = createSlice({ ) { state.players[action.payload.player].gain = action.payload.gain; }, + setPlayerTrim( + state, + action: PayloadAction<{ + player: number; + trim: number; + }> + ) { + state.players[action.payload.player].trim = action.payload.trim; + }, setMicError(state, action: PayloadAction) { state.mic.openError = action.payload; }, @@ -549,6 +560,11 @@ export const setVolume = ( }; }; +export const setChannelTrim = (player: number, val: number): AppThunk => async dispatch => { + dispatch(mixerState.actions.setPlayerTrim({ player, trim: val })); + audioEngine.players[player]?.setTrim(val); +}; + export const openMicrophone = (micID: string): AppThunk => async ( dispatch, getState diff --git a/src/optionsMenu/ProModeTab.tsx b/src/optionsMenu/ProModeTab.tsx new file mode 100644 index 0000000..2022253 --- /dev/null +++ b/src/optionsMenu/ProModeTab.tsx @@ -0,0 +1,35 @@ +import React, { useState, useRef, useEffect, useCallback } from "react"; +import { streamer } from "../broadcast/state"; +import { WebRTCStreamer } from "../broadcast/rtc_streamer"; +import {useDispatch, useSelector} from "react-redux"; +import {RootState} from "../rootReducer"; +import {changeSetting} from "./settingsState"; + +export function ProModeTab() { + const settings = useSelector((state: RootState) => state.settings); + const dispatch = useDispatch(); + return ( + <> +
+ + dispatch( + changeSetting({ + key: "proMode", + val: e.target.checked, + }) + ) + } + /> + +
+ + ); +} diff --git a/src/optionsMenu/index.tsx b/src/optionsMenu/index.tsx index 45a8aaa..b360a67 100644 --- a/src/optionsMenu/index.tsx +++ b/src/optionsMenu/index.tsx @@ -10,6 +10,7 @@ import { AboutTab } from "./AboutTab"; import { StatsTab } from "./StatsTab"; import { AdvancedTab } from "./AdvancedTab"; import { FaTimes } from "react-icons/fa"; +import {ProModeTab} from "./ProModeTab"; export function OptionsMenu() { const state = useSelector((state: RootState) => state.optionsMenu); @@ -36,6 +37,14 @@ export function OptionsMenu() { Stream Statistics + + dispatch(OptionsState.changeTab("pro"))} + > + Pro Mode™ + + + + + diff --git a/src/optionsMenu/settingsState.ts b/src/optionsMenu/settingsState.ts index f655eb2..ebeb8a1 100644 --- a/src/optionsMenu/settingsState.ts +++ b/src/optionsMenu/settingsState.ts @@ -5,6 +5,7 @@ interface Settings { enableRecording: boolean; tracklist: "always" | "while_live" | "never"; doTheNews: "always" | "while_live" | "never"; + proMode: boolean; } const settingsState = createSlice({ @@ -14,6 +15,7 @@ const settingsState = createSlice({ enableRecording: false, tracklist: "while_live", doTheNews: "while_live", + proMode: false } as Settings, reducers: { changeSetting( diff --git a/src/optionsMenu/state.ts b/src/optionsMenu/state.ts index 3450bfd..8b55059 100644 --- a/src/optionsMenu/state.ts +++ b/src/optionsMenu/state.ts @@ -1,6 +1,6 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -export type OptionsTabIDsEnum = "mic" | "about" | "advanced" | "stats"; +export type OptionsTabIDsEnum = "mic" | "about" | "pro" | "advanced" | "stats"; const optionsMenuState = createSlice({ name: "optionsMenu", diff --git a/src/showplanner/Player.tsx b/src/showplanner/Player.tsx index 34f1b06..1a43085 100644 --- a/src/showplanner/Player.tsx +++ b/src/showplanner/Player.tsx @@ -7,10 +7,12 @@ import { FaPlay, FaPause, FaStop, + FaTachometerAlt } from "react-icons/fa"; import { RootState } from "../rootReducer"; import * as MixerState from "../mixer/state"; import { secToHHMM, timestampToHHMM } from "../lib/utils"; +import ProModeButtons from "./ProModeButtons"; export const USE_REAL_GAIN_VALUE = false; @@ -18,6 +20,7 @@ export function Player({ id }: { id: number }) { const playerState = useSelector( (state: RootState) => state.mixer.players[id] ); + const proMode = useSelector((state: RootState) => state.settings.proMode); const dispatch = useDispatch(); const [now, setNow] = useState(new Date()); @@ -75,6 +78,9 @@ export function Player({ id }: { id: number }) {   Repeat {playerState.repeat} + {proMode && ( + + )}
diff --git a/src/showplanner/ProModeButtons.tsx b/src/showplanner/ProModeButtons.tsx new file mode 100644 index 0000000..400ed22 --- /dev/null +++ b/src/showplanner/ProModeButtons.tsx @@ -0,0 +1,32 @@ +import React, {useState} from "react"; +import {FaTachometerAlt} from "react-icons/fa"; +import {useDispatch, useSelector} from "react-redux"; +import {RootState} from "../rootReducer"; +import {setChannelTrim} from "../mixer/state"; + +type ButtonIds = "trim"; + +export default function ProModeButtons({channel}: { channel: number }) { + const [activeButton, setActiveButton] = useState(null); + const trimVal = useSelector((state: RootState) => state.mixer.players[channel]?.trim); + const dispatch = useDispatch(); + + return ( + <> +
+ {(activeButton === null || activeButton === "trim") && ( + + )} + {activeButton === "trim" && ( + <> + dispatch(setChannelTrim(channel, parseFloat(e.target.value)))}/> + {trimVal} + + )} +
+ + ) +}