initial commit
This commit is contained in:
commit
0024f44c80
34 changed files with 1991 additions and 0 deletions
254
frontend/src/meter.jsx
Normal file
254
frontend/src/meter.jsx
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
import React, { useState, useEffect } from 'react'
|
||||
|
||||
const SCALE_MIN = -60
|
||||
const SCALE_MAX = 0
|
||||
|
||||
function toPx (val) {
|
||||
return value2px(val, SCALE_MIN, SCALE_MAX, 0, 100)
|
||||
}
|
||||
|
||||
function toY (val) {
|
||||
return 100 - value2px(val)
|
||||
}
|
||||
|
||||
function MeterDefs (props) {
|
||||
const { width } = props
|
||||
let green = [0, toPx(-8)]
|
||||
let yellow = [toPx(-8), toPx(-5)]
|
||||
let red = [toPx(-5), toPx(0)]
|
||||
const common = { x: 0, width }
|
||||
|
||||
return (
|
||||
<defs>
|
||||
<g id='peakbg'>
|
||||
<rect
|
||||
y={100 - red[1]}
|
||||
height={100 - red[0]}
|
||||
{...common}
|
||||
{...style('#ff2244')}
|
||||
/>
|
||||
<rect
|
||||
y={100 - yellow[1]}
|
||||
height={100 - yellow[0]}
|
||||
{...common}
|
||||
{...style('#eeee00')}
|
||||
/>
|
||||
<rect
|
||||
y={100 - green[1]}
|
||||
height={100 - green[0]}
|
||||
{...common}
|
||||
{...style('#88ff00')}
|
||||
/>
|
||||
</g>
|
||||
|
||||
<linearGradient id='fadeGrad' y2='0' x2='1'>
|
||||
<stop offset='0' stopColor='white' stopOpacity='0.2' />
|
||||
<stop offset='0.5' stopColor='white' stopOpacity='0.5' />
|
||||
<stop offset='1' stopColor='white' stopOpacity='0.2' />
|
||||
</linearGradient>
|
||||
|
||||
<mask id='bgfade' maskContentUnits='objectBoundingBox'>
|
||||
<rect width='1' height='1' fill='url(#fadeGrad)' />
|
||||
</mask>
|
||||
</defs>
|
||||
)
|
||||
}
|
||||
|
||||
let max = 0
|
||||
let last = 0
|
||||
function getMax (time, val) {
|
||||
// let newmax = Math.max(val, max)
|
||||
if (val > max) {
|
||||
max = val
|
||||
last = time
|
||||
} else if (time - last > 3) {
|
||||
max = val
|
||||
} else if (time - last > 1) {
|
||||
max = max - 1
|
||||
// max = val
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
export function EbuMeter (props) {
|
||||
const { value } = props
|
||||
const val = value
|
||||
// let [max, setMax] = useState(0)
|
||||
|
||||
const width = 40
|
||||
const gutter = 1
|
||||
|
||||
let min = SCALE_MIN
|
||||
|
||||
// let min = -30 // mic
|
||||
// let peakMax = -2
|
||||
// let ebuMax = -5
|
||||
let peakMax = SCALE_MAX
|
||||
let ebuMax = SCALE_MAX
|
||||
|
||||
// Momentary peak
|
||||
// let peakM = value2px(val.M, min, ebuMax, 0, 100)
|
||||
// Short-term peak
|
||||
let peakS = value2px(val.S, min, ebuMax, 0, 100)
|
||||
let peakL = 0
|
||||
let peakR = 0
|
||||
|
||||
if (val.FTPK) {
|
||||
peakL = value2px(val.FTPK[0], min, peakMax, 0, 100)
|
||||
peakR = value2px(val.FTPK[1], min, peakMax, 0, 100)
|
||||
}
|
||||
|
||||
if (peakL > 100) peakL = 0
|
||||
if (peakR > 100) peakR = 0
|
||||
if (peakS > 100) peakS = 0
|
||||
|
||||
let max = getMax(val.t, Math.max(peakL, peakR))
|
||||
|
||||
const meterWidth = width / 2 - gutter / 2
|
||||
const peakMeterL = {
|
||||
id: 'peakL',
|
||||
peak: peakL,
|
||||
x: 0,
|
||||
width: meterWidth
|
||||
}
|
||||
const peakMeterR = {
|
||||
id: 'peakR',
|
||||
peak: peakR,
|
||||
x: meterWidth + gutter,
|
||||
width: meterWidth
|
||||
}
|
||||
return (
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
width='100%'
|
||||
heigh='100%'
|
||||
viewBox={`0 0 ${width} 100`}
|
||||
preserveAspectRatio='xMaxYMax meet'
|
||||
>
|
||||
<MeterDefs width={width} />
|
||||
|
||||
<PeakMeter {...peakMeterL} />
|
||||
<PeakMeter {...peakMeterR} />
|
||||
|
||||
{/* <rect y={100 - max} x='0' width={width} height='0.8' fill='#a9f' /> */}
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function PeakMeter (props = {}) {
|
||||
const { peak, width, x, id } = props
|
||||
const mask = {
|
||||
y: 100 - peak,
|
||||
height: peak,
|
||||
x,
|
||||
width
|
||||
}
|
||||
|
||||
// const range = -60
|
||||
// const gutter = 0.5
|
||||
// const steps = 24
|
||||
// const dbPerStep = range / steps
|
||||
// const scale = new Array(steps).fill(null).map((_, i) => {
|
||||
// const bottom = toPx(dbPerStep * (i)) - gutter
|
||||
// const top = toPx(dbPerStep * (i + 1))
|
||||
// let fill = '#fff'
|
||||
// // if (bottom > peak) fill = '#444'
|
||||
// return {
|
||||
// fill,
|
||||
// x,
|
||||
// width,
|
||||
// y: 100 - top,
|
||||
// height: top - bottom
|
||||
// }
|
||||
// })
|
||||
|
||||
const height = 100
|
||||
const gutter = 0.8
|
||||
const steps = 24
|
||||
const heightPerStep = height / steps
|
||||
// const filledSteps = Math.ceil(peak / steps)
|
||||
const scale = new Array(steps).fill(null).map((_, i) => {
|
||||
const bottom = heightPerStep * i
|
||||
const top = bottom + heightPerStep - gutter
|
||||
let fill = 'white'
|
||||
if (((top + bottom) / 2) > peak) fill = '#444'
|
||||
// if ((steps * i) > peak) return
|
||||
// const y = 100 - heightPerStep * i
|
||||
return {
|
||||
fill,
|
||||
x,
|
||||
width,
|
||||
y: 100 - top,
|
||||
height: top - bottom
|
||||
}
|
||||
}).filter(x => x)
|
||||
return (
|
||||
<>
|
||||
<defs>
|
||||
<mask id={id}>
|
||||
<rect fill='#000' {...mask} />
|
||||
{scale.map((rect, i) => <rect key={i} {...rect} />)}
|
||||
</mask>
|
||||
</defs>
|
||||
<g
|
||||
mask={`url(#${id})`}
|
||||
style={{ opacity: 0.9 }}
|
||||
>
|
||||
<use xlinkHref='#peakbg' />
|
||||
</g>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function sigLog (n) {
|
||||
return Math.log(Math.abs(n) + 1) / Math.log(10) * sig(n)
|
||||
}
|
||||
|
||||
function sig (n) {
|
||||
return n === 0 ? 0 : Math.abs(n) / n
|
||||
}
|
||||
|
||||
function sigExp (n) {
|
||||
return (Math.pow(10, Math.abs(n)) - 1) * sig(n)
|
||||
}
|
||||
|
||||
function value2px (value, valueMin, valueMax, pxMin, pxMax) {
|
||||
if (value < valueMin) value = valueMin
|
||||
if (value > valueMax) value = valueMax
|
||||
if (isNaN(value) || value === null) value = valueMin
|
||||
|
||||
const valueWidth = valueMax - valueMin
|
||||
const pixelWidth = pxMax - pxMin
|
||||
const ratio = pixelWidth / valueWidth
|
||||
return ratio * (value - valueMin) + pxMin
|
||||
|
||||
// const ratio = value / (valueMax - valueMin)
|
||||
// const scaled = ratio * (pxMax - pxMin)
|
||||
// return pxMin + scaled
|
||||
}
|
||||
|
||||
// This is the original function that display the meter in a log scale.
|
||||
function value2pxLog (value, valueMin, valueMax, pxMin, pxMax) {
|
||||
var valueWidth = sigLog(valueMax) - sigLog(valueMin)
|
||||
var pixelWidth = pxMax - pxMin
|
||||
var ratio = pixelWidth / valueWidth
|
||||
|
||||
return ratio * (sigLog(value) - sigLog(valueMin)) + pxMin
|
||||
}
|
||||
|
||||
function px2value (px, valueMin, valueMax, pxMin, pxMax) {
|
||||
var valueWidth = sigLog(valueMax) - sigLog(valueMin)
|
||||
var pixelWidth = pxMax - pxMin
|
||||
var ratio = pixelWidth / valueWidth
|
||||
|
||||
return sigExp((px - pxMin) / ratio + sigLog(valueMin))
|
||||
}
|
||||
|
||||
function prettify (n) {
|
||||
var exp = Math.round(Math.pow(10, Math.log(Math.abs(n)) / Math.log(10)))
|
||||
return exp === 0 ? 0 : Math.round(n / exp) * exp
|
||||
}
|
||||
|
||||
function style (color) {
|
||||
return { style: { fill: color } }
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue