142 lines
3.8 KiB
Go
142 lines
3.8 KiB
Go
package ui
|
|
|
|
import (
|
|
"image/color"
|
|
"math"
|
|
"time"
|
|
|
|
"fyne.io/fyne/v2"
|
|
"fyne.io/fyne/v2/canvas"
|
|
"fyne.io/fyne/v2/container"
|
|
"fyne.io/fyne/v2/theme"
|
|
"fyne.io/fyne/v2/widget"
|
|
)
|
|
|
|
const (
|
|
meterRangeDB = 60
|
|
rmsSmoothingFactor = 0.8
|
|
peakHoldFrames = 60
|
|
)
|
|
|
|
type PeakFN func() (float64, float64, float64, float64)
|
|
|
|
type PeakMeter struct {
|
|
widget.BaseWidget
|
|
peakFnDB PeakFN
|
|
lPeak float64
|
|
rPeak float64
|
|
lRMS float64
|
|
rRMS float64
|
|
lPeakHold float64
|
|
rPeakHold float64
|
|
lPeakHoldFrame uint64
|
|
rPeakHoldFrame uint64
|
|
frameCounter uint64
|
|
|
|
lPeakRect canvas.Rectangle
|
|
rPeakRect canvas.Rectangle
|
|
lRMSRect canvas.Rectangle
|
|
rRMSRect canvas.Rectangle
|
|
lPeakHoldRect canvas.Rectangle
|
|
rPeakHoldRect canvas.Rectangle
|
|
anim *fyne.Animation
|
|
}
|
|
|
|
func NewPeakMeter(peakFnDB PeakFN) *PeakMeter {
|
|
p := &PeakMeter{peakFnDB: peakFnDB}
|
|
p.ExtendBaseWidget(p)
|
|
return p
|
|
}
|
|
|
|
func (p *PeakMeter) Start() {
|
|
if p.anim != nil {
|
|
return
|
|
}
|
|
p.anim = fyne.NewAnimation(time.Duration(math.MaxInt64) /*until stopped*/, p.tick)
|
|
p.anim.Start()
|
|
}
|
|
|
|
func (p *PeakMeter) Stop() {
|
|
if p.anim != nil {
|
|
p.anim.Stop()
|
|
p.anim = nil
|
|
p.frameCounter = 0
|
|
p.lPeakHoldFrame = 0
|
|
p.rPeakHoldFrame = 0
|
|
}
|
|
}
|
|
|
|
func (p *PeakMeter) tick(_ float32) {
|
|
lPeak, rPeak, lRMS, rRMS := p.peakFnDB()
|
|
p.lPeak = lPeak
|
|
p.rPeak = rPeak
|
|
lRMS = math.Max(-96, lRMS)
|
|
rRMS = math.Max(-96, rRMS)
|
|
p.lRMS = rmsSmoothingFactor*p.lRMS + (1-rmsSmoothingFactor)*lRMS
|
|
p.rRMS = rmsSmoothingFactor*p.rRMS + (1-rmsSmoothingFactor)*rRMS
|
|
|
|
if lPeak > p.lPeakHold || p.frameCounter-p.lPeakHoldFrame > peakHoldFrames {
|
|
p.lPeakHold = lPeak
|
|
p.lPeakHoldFrame = p.frameCounter
|
|
}
|
|
if rPeak > p.rPeakHold || p.frameCounter-p.rPeakHoldFrame > peakHoldFrames {
|
|
p.rPeakHold = rPeak
|
|
p.rPeakHoldFrame = p.frameCounter
|
|
}
|
|
|
|
p.frameCounter++
|
|
p.Refresh()
|
|
}
|
|
|
|
func (p *PeakMeter) CreateRenderer() fyne.WidgetRenderer {
|
|
c := theme.PrimaryColor().(color.NRGBA)
|
|
c.A = 128
|
|
p.lPeakRect.FillColor = c
|
|
p.rPeakRect.FillColor = c
|
|
p.lRMSRect.FillColor = c
|
|
p.rRMSRect.FillColor = c
|
|
p.lPeakHoldRect.FillColor = theme.ForegroundColor()
|
|
p.rPeakHoldRect.FillColor = p.lPeakHoldRect.FillColor
|
|
return widget.NewSimpleRenderer(
|
|
container.New(
|
|
&peakMeterLayout{p},
|
|
&p.lPeakRect, &p.rPeakRect,
|
|
&p.lRMSRect, &p.rRMSRect,
|
|
&p.lPeakHoldRect, &p.rPeakHoldRect,
|
|
),
|
|
)
|
|
}
|
|
|
|
type peakMeterLayout struct {
|
|
p *PeakMeter
|
|
}
|
|
|
|
func (l *peakMeterLayout) MinSize(_ []fyne.CanvasObject) fyne.Size {
|
|
return fyne.NewSize(50, 150)
|
|
}
|
|
|
|
func (l *peakMeterLayout) Layout(objects []fyne.CanvasObject, size fyne.Size) {
|
|
lPeakH := float32(math.Max(0, meterRangeDB+l.p.lPeak)/meterRangeDB) * size.Height
|
|
rPeakH := float32(math.Max(0, meterRangeDB+l.p.rPeak)/meterRangeDB) * size.Height
|
|
lRMSH := float32(math.Max(0, meterRangeDB+l.p.lRMS)/meterRangeDB) * size.Height
|
|
rRMSH := float32(math.Max(0, meterRangeDB+l.p.rRMS)/meterRangeDB) * size.Height
|
|
lPeakHoldPos := float32(math.Max(0, meterRangeDB+l.p.lPeakHold)/meterRangeDB) * size.Height
|
|
rPeakHoldPos := float32(math.Max(0, meterRangeDB+l.p.rPeakHold)/meterRangeDB) * size.Height
|
|
|
|
halfW := size.Width / 2
|
|
objects[0].Move(fyne.NewPos(0, size.Height-lPeakH))
|
|
objects[0].Resize(fyne.NewSize(halfW, lPeakH))
|
|
objects[1].Move(fyne.NewPos(halfW, size.Height-rPeakH))
|
|
objects[1].Resize(fyne.NewSize(halfW, rPeakH))
|
|
objects[2].Move(fyne.NewPos(0, size.Height-lRMSH))
|
|
objects[2].Resize(fyne.NewSize(halfW, lRMSH))
|
|
objects[3].Move(fyne.NewPos(halfW, size.Height-rRMSH))
|
|
objects[3].Resize(fyne.NewSize(halfW, rRMSH))
|
|
|
|
peakHoldHt := theme.SeparatorThicknessSize() * 2
|
|
objects[4].Move(fyne.NewPos(0, size.Height-lPeakHoldPos-peakHoldHt))
|
|
objects[4].Resize(fyne.NewSize(halfW, peakHoldHt))
|
|
objects[5].Move(fyne.NewPos(halfW, size.Height-rPeakHoldPos-peakHoldHt))
|
|
objects[5].Resize(fyne.NewSize(halfW, peakHoldHt))
|
|
}
|