Add ProMode (TM) VU meters to each channel. Closes #71

This commit is contained in:
Matthew Stratford 2020-09-20 01:34:07 +01:00
parent ddf701de14
commit 1132862d9a
3 changed files with 49 additions and 1 deletions

View file

@ -38,6 +38,10 @@ $number-of-channels: 3;
grid-template-rows: 100%; grid-template-rows: 100%;
grid-gap: 10px; grid-gap: 10px;
padding: 10px; padding: 10px;
.channel-vu {
width: 100%;
}
} }
#sidebar { #sidebar {
grid-area: sb; grid-area: sb;

View file

@ -144,6 +144,7 @@ class Player extends ((PlayerEmitter as unknown) as { new (): EventEmitter }) {
(wavesurfer as any).backend.gainNode.disconnect(); (wavesurfer as any).backend.gainNode.disconnect();
(wavesurfer as any).backend.gainNode.connect(engine.finalCompressor); (wavesurfer as any).backend.gainNode.connect(engine.finalCompressor);
(wavesurfer as any).backend.gainNode.connect(engine.playerAnalysers[player])
wavesurfer.load(url); wavesurfer.load(url);
@ -160,7 +161,7 @@ class Player extends ((PlayerEmitter as unknown) as { new (): EventEmitter }) {
} }
} }
export type LevelsSource = "mic-precomp" | "mic-final" | "master"; export type LevelsSource = "mic-precomp" | "mic-final" | "master" | "player-0" | "player-1" | "player-2";
const ANALYSIS_FFT_SIZE = 8192; const ANALYSIS_FFT_SIZE = 8192;
@ -190,6 +191,11 @@ export class AudioEngine extends ((EngineEmitter as unknown) as {
finalCompressor: DynamicsCompressorNode; finalCompressor: DynamicsCompressorNode;
streamingDestination: MediaStreamAudioDestinationNode; streamingDestination: MediaStreamAudioDestinationNode;
player0Analyser: AnalyserNode;
player1Analyser: AnalyserNode;
player2Analyser: AnalyserNode;
playerAnalysers: AnalyserNode[];
streamingAnalyser: AnalyserNode; streamingAnalyser: AnalyserNode;
newsStartCountdownEl: HTMLAudioElement; newsStartCountdownEl: HTMLAudioElement;
@ -214,6 +220,14 @@ export class AudioEngine extends ((EngineEmitter as unknown) as {
this.finalCompressor.release.value = 0.2; this.finalCompressor.release.value = 0.2;
this.finalCompressor.knee.value = 0; this.finalCompressor.knee.value = 0;
this.player0Analyser = this.audioContext.createAnalyser();
this.player0Analyser.fftSize = ANALYSIS_FFT_SIZE;
this.player1Analyser = this.audioContext.createAnalyser();
this.player1Analyser.fftSize = ANALYSIS_FFT_SIZE;
this.player2Analyser = this.audioContext.createAnalyser();
this.player2Analyser.fftSize = ANALYSIS_FFT_SIZE;
this.playerAnalysers = [this.player0Analyser, this.player1Analyser, this.player2Analyser];
this.streamingAnalyser = this.audioContext.createAnalyser(); this.streamingAnalyser = this.audioContext.createAnalyser();
this.streamingAnalyser.fftSize = ANALYSIS_FFT_SIZE; this.streamingAnalyser.fftSize = ANALYSIS_FFT_SIZE;
// this.streamingAnalyser.maxDecibels = 0; // this.streamingAnalyser.maxDecibels = 0;
@ -334,6 +348,15 @@ export class AudioEngine extends ((EngineEmitter as unknown) as {
case "master": case "master":
this.streamingAnalyser.getFloatTimeDomainData(this.analysisBuffer); this.streamingAnalyser.getFloatTimeDomainData(this.analysisBuffer);
break; break;
case "player-0":
this.player0Analyser.getFloatTimeDomainData(this.analysisBuffer);
break;
case "player-1":
this.player1Analyser.getFloatTimeDomainData(this.analysisBuffer);
break;
case "player-2":
this.player2Analyser.getFloatTimeDomainData(this.analysisBuffer);
break;
default: default:
throw new Error("can't getLevel " + source); throw new Error("can't getLevel " + source);
} }

View file

@ -13,6 +13,7 @@ import { RootState } from "../rootReducer";
import * as MixerState from "../mixer/state"; import * as MixerState from "../mixer/state";
import { secToHHMM, timestampToHHMM } from "../lib/utils"; import { secToHHMM, timestampToHHMM } from "../lib/utils";
import ProModeButtons from "./ProModeButtons"; import ProModeButtons from "./ProModeButtons";
import { VUMeter } from "../optionsMenu/helpers/VUMeter";
export const USE_REAL_GAIN_VALUE = false; export const USE_REAL_GAIN_VALUE = false;
@ -68,6 +69,19 @@ export function Player({ id }: { id: number }) {
const proMode = useSelector((state: RootState) => state.settings.proMode); const proMode = useSelector((state: RootState) => state.settings.proMode);
const dispatch = useDispatch(); const dispatch = useDispatch();
const VUsource =
(id: number) => {
switch(id) {
case 0:
return "player-0";
case 1:
return "player-1";
case 2:
return "player-2";
default:
return "master";
}
};
return ( return (
<div <div
className={ className={
@ -239,6 +253,13 @@ export function Player({ id }: { id: number }) {
Full Full
</button> </button>
</div> </div>
{ proMode && <VUMeter
className="channel-vu"
width={200}
height={40}
source={VUsource(id)}
range={[-40, 0]}
/>}
</div> </div>
); );
} }