mirror of
https://github.com/vsariola/sointu.git
synced 2025-05-28 03:10:24 -04:00
79 lines
1.7 KiB
Go
79 lines
1.7 KiB
Go
package tracker
|
|
|
|
import (
|
|
"image"
|
|
"math"
|
|
|
|
"gioui.org/f32"
|
|
"gioui.org/op"
|
|
"gioui.org/op/clip"
|
|
"gioui.org/op/paint"
|
|
"gioui.org/unit"
|
|
)
|
|
|
|
type VuMeter struct {
|
|
avg [2]float32
|
|
max [2]float32
|
|
speed [2]float32
|
|
FallOff float32
|
|
Decay float32
|
|
RangeDb float32
|
|
}
|
|
|
|
func (v *VuMeter) Update(buffer []float32) {
|
|
for j := 0; j < 2; j++ {
|
|
for i := 0; i < len(buffer); i += 2 {
|
|
sample2 := buffer[i+j] * buffer[i+j]
|
|
db := float32(10*math.Log10(float64(sample2))) + v.RangeDb
|
|
v.speed[j] += v.FallOff
|
|
v.max[j] -= v.speed[j]
|
|
if v.max[j] < 0 {
|
|
v.max[j] = 0
|
|
}
|
|
if v.max[j] < db {
|
|
v.max[j] = db
|
|
v.speed[j] = 0
|
|
}
|
|
v.avg[j] += (sample2 - v.avg[j]) * v.Decay
|
|
if math.IsNaN(float64(v.avg[j])) {
|
|
v.avg[j] = 0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (v *VuMeter) Reset() {
|
|
v.avg = [2]float32{}
|
|
v.max = [2]float32{}
|
|
}
|
|
|
|
func (v *VuMeter) Layout(gtx C) D {
|
|
defer op.Save(gtx.Ops).Load()
|
|
gtx.Constraints.Max.Y = gtx.Px(unit.Dp(12))
|
|
height := gtx.Px(unit.Dp(6))
|
|
for j := 0; j < 2; j++ {
|
|
value := float32(10*math.Log10(float64(v.avg[j]))) + v.RangeDb
|
|
if value > 0 {
|
|
x := int(value/v.RangeDb*float32(gtx.Constraints.Max.X) + 0.5)
|
|
if x > gtx.Constraints.Max.X {
|
|
x = gtx.Constraints.Max.X
|
|
}
|
|
paint.FillShape(gtx.Ops, mediumEmphasisTextColor, clip.Rect(image.Rect(0, 0, x, height)).Op())
|
|
}
|
|
valueMax := v.max[j]
|
|
if valueMax > 0 {
|
|
color := white
|
|
if valueMax >= v.RangeDb {
|
|
color = errorColor
|
|
}
|
|
x := int(valueMax/v.RangeDb*float32(gtx.Constraints.Max.X) + 0.5)
|
|
if x > gtx.Constraints.Max.X {
|
|
x = gtx.Constraints.Max.X
|
|
}
|
|
paint.FillShape(gtx.Ops, color, clip.Rect(image.Rect(x-1, 0, x, height)).Op())
|
|
}
|
|
op.Offset(f32.Pt(0, float32(height))).Add(gtx.Ops)
|
|
}
|
|
return D{Size: gtx.Constraints.Max}
|
|
}
|