Skip to content

Commit

Permalink
feat: change VU meter rendering to canvas-based
Browse files Browse the repository at this point in the history
  • Loading branch information
jstarpl committed Jun 11, 2019
1 parent 39e0824 commit ee829d7
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 21 deletions.
1 change: 1 addition & 0 deletions src/assets/css/Channels.css
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
width: 60px;
border-radius: 7px;
border-color: rgb(99, 99, 99);
white-space: nowrap;
}

.channels-snap-mix-line:last-of-type {
Expand Down
9 changes: 8 additions & 1 deletion src/assets/css/VuMeter.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,17 @@
margin-left: 14px;
background-color: black;
border-radius: 7px;
transform: rotate(180deg);
height: 430px;
}

.vumeter-canvas {
position: absolute;
width: 10px;
margin-left: 10px;
bottom: 0;
height: 100%;
}

.vumeter-lower-part {
position: absolute;
width: 10px;
Expand Down
103 changes: 83 additions & 20 deletions src/components/VuMeter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ interface IVuMeterProps {

export class VuMeter extends React.PureComponent<IVuMeterInjectedProps> {
mixerProtocol: IMixerProtocolGeneric;
canvas: HTMLCanvasElement | undefined;

totalPeak: number = 0;
windowPeak: number = 0;
windowLast: number = 0;
WINDOW: number = 2000;

constructor(props: any) {
super(props);
Expand All @@ -30,6 +36,21 @@ export class VuMeter extends React.PureComponent<IVuMeterInjectedProps> {
return (this.props.showSnaps ? 1 : 2) * 200 / (this.mixerProtocol.meter.max - this.mixerProtocol.meter.min);
}

getTotalPeak = () => {
if (this.props.vuVal > this.totalPeak) {
this.totalPeak = this.props.vuVal;
}
return this.totalHeight()*this.totalPeak;
}

getWindowPeak = () => {
if (this.props.vuVal > this.windowPeak || (Date.now() - this.windowLast) > this.WINDOW) {
this.windowPeak = this.props.vuVal;
this.windowLast = Date.now()
}
return this.totalHeight()*this.windowPeak
}

calcLower = () => {
let val = this.props.vuVal;
if (val >= this.mixerProtocol.meter.test) {
Expand All @@ -56,40 +77,82 @@ export class VuMeter extends React.PureComponent<IVuMeterInjectedProps> {
return this.totalHeight()*(val-this.mixerProtocol.meter.zero)+1;
}

setRef = (el: HTMLCanvasElement) => {
this.canvas = el;

this.paintVuMeter();
}

resetTotalPeak = () => {
this.totalPeak = 0;
}

paintVuMeter = () => {
if (!this.canvas) return

const context = this.canvas.getContext("2d", {
antialias: false,
stencil: false,
preserveDrawingBuffer: true
})

if (!context) return

const range = (this.mixerProtocol.meter.max - this.mixerProtocol.meter.min)
context.clearRect(0, 0, this.canvas.clientWidth, this.canvas.clientHeight);

// lower part
context.fillStyle = 'rgb(0, 122, 37)';
context.fillRect(0, (this.totalHeight() - this.calcLower()), this.canvas.clientWidth, this.calcLower());

// middle part
context.fillStyle = 'rgb(53, 167, 0)';
context.fillRect(0, (this.totalHeight() * (range - this.mixerProtocol.meter.test) - this.calcMiddle()), this.canvas.clientWidth, this.calcMiddle());

// upper part (too high/clip)
context.fillStyle = 'rgb(206, 0, 0)';
context.fillRect(0, (this.totalHeight() * (range - this.mixerProtocol.meter.zero) - this.calcUpper()), this.canvas.clientWidth, this.calcUpper());

// windowed peak
const windowPeak = this.getWindowPeak();
if (this.windowPeak < this.mixerProtocol.meter.zero) {
context.fillStyle = 'rgb(16, 56, 0)';
} else {
context.fillStyle = 'rgb(100, 100, 100)';
}
context.fillRect(0, (this.totalHeight() - windowPeak), this.canvas.clientWidth, 2);

// absolute peak
if (this.totalPeak < this.mixerProtocol.meter.zero) {
context.fillStyle = 'rgb(64, 64, 64)';
} else {
context.fillStyle = 'rgb(255, 0, 0)';
}
context.fillRect(0, (this.totalHeight() - this.getTotalPeak()), this.canvas.clientWidth, 2);
}

render() {
this.paintVuMeter()

return (
<div className="vumeter-body"
style={{
"height" : this.totalHeight() + 30
}}
onClick={this.resetTotalPeak}
>
<canvas
className="vumeter-lower-part"
style={
{
"height": this.calcLower(),
"top": "5px"
}
}
></canvas>
<canvas
className="vumeter-middle-part"
className="vumeter-canvas"
style={
{
"height": this.calcMiddle(),
"top": this.totalHeight()*this.mixerProtocol.meter.test+5
"height": this.totalHeight(),
"top": "10px"
}
}
height={this.totalHeight()}
width={10}
ref={this.setRef}
></canvas>
<canvas
className="vumeter-upper-part"
style={
{
"height": this.calcUpper(),
"top": this.totalHeight()*this.mixerProtocol.meter.zero+5
}
}></canvas>

</div>
)
Expand Down

0 comments on commit ee829d7

Please sign in to comment.