This commit is contained in:
5684185+vsariola@users.noreply.github.com
2026-01-01 18:19:48 +02:00
parent c424d2b847
commit 179ebb7cc3
5 changed files with 279 additions and 173 deletions

View File

@ -1,11 +1,9 @@
package gioui
import (
"image"
"math"
"gioui.org/layout"
"gioui.org/op/clip"
"gioui.org/op/paint"
"gioui.org/unit"
"github.com/vsariola/sointu/tracker"
)
@ -15,11 +13,13 @@ type (
resolutionNumber *NumericUpDownState
smoothingBtn *Clickable
chnModeBtn *Clickable
plot *Plot
}
)
func NewSpectrumState() *SpectrumState {
return &SpectrumState{
plot: NewPlot(plotRange{0, 1}, plotRange{-1, 0}),
resolutionNumber: NewNumericUpDownState(),
smoothingBtn: new(Clickable),
chnModeBtn: new(Clickable),
@ -56,8 +56,64 @@ func (s *SpectrumState) Layout(gtx C) D {
chnModeBtn := Btn(t.Theme, &t.Theme.Button.Filled, s.chnModeBtn, chnModeTxt, "Channel mode")
smoothBtn := Btn(t.Theme, &t.Theme.Button.Filled, s.smoothingBtn, smoothTxt, "Smoothing")
numchns := 0
speclen := len(t.Model.Spectrum()[0])
if speclen > 0 {
numchns = 1
if len(t.Model.Spectrum()[1]) == speclen {
numchns = 2
}
}
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Flexed(1, s.drawSpectrum),
layout.Flexed(1, func(gtx C) D {
data := func(chn int, xr plotRange) (yr plotRange) {
xr.a = softplus(xr.a*10) / 10
xr.b = softplus(xr.b*10) / 10
xr.a = float32(math.Log(float64(xr.a))) + 1
xr.b = float32(math.Log(float64(xr.b))) + 1
w1, f1 := math.Modf(float64(xr.a) * float64(speclen))
w2, f2 := math.Modf(float64(xr.b) * float64(speclen))
x1 := max(int(w1), 0)
x2 := min(int(w2), speclen-1)
if x1 > x2 {
return plotRange{1, 0}
}
y1 := float32(math.Inf(-1))
y2 := float32(math.Inf(+1))
switch {
case x2 <= x1+1 && x2 < speclen-1: // perform smoothstep interpolation when we are overlapping only a few bins
l := t.Model.Spectrum()[chn][x1]
r := t.Model.Spectrum()[chn][x1+1]
y1 = smoothInterpolate(l, r, float32(f1))
l = t.Model.Spectrum()[chn][x2]
r = t.Model.Spectrum()[chn][x2+1]
y2 = smoothInterpolate(l, r, float32(f2))
y1, y2 = max(y1, y2), min(y1, y2)
default:
for i := x1; i <= x2; i++ {
sample := t.Model.Spectrum()[chn][i]
y1 = max(y1, sample)
y2 = min(y2, sample)
}
}
y1 = (y1 / 80) + 1
y2 = (y2 / 80) + 1
y1 = softplus(y1*10) / 10
y2 = softplus(y2*10) / 10
return plotRange{-y1, -y2}
}
xticks := func(r plotRange, yield func(pos float32, label string)) {
yield(0, "")
yield(1, "")
}
yticks := func(r plotRange, yield func(pos float32, label string)) {
yield(-1, "")
yield(0, "")
}
return s.plot.Layout(gtx, data, xticks, yticks, 0, numchns)
}),
layout.Rigid(func(gtx C) D {
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
layout.Rigid(leftSpacer),
@ -71,6 +127,15 @@ func (s *SpectrumState) Layout(gtx C) D {
)
}
func softplus(f float32) float32 {
return float32(math.Log(1 + math.Exp(float64(f))))
}
func smoothInterpolate(a, b float32, t float32) float32 {
t = t * t * (3 - 2*t)
return (1-t)*a + t*b
}
func (s *SpectrumState) Update(gtx C) {
t := TrackerFromContext(gtx)
for s.chnModeBtn.Clicked(gtx) {
@ -81,22 +146,3 @@ func (s *SpectrumState) Update(gtx C) {
t.Model.SpecAnSmoothing().SetValue((t.SpecAnSmoothing().Value()+1)%(r.Max-r.Min+1) + r.Min)
}
}
func (s *SpectrumState) drawSpectrum(gtx C) D {
t := TrackerFromContext(gtx)
for chn := range 2 {
paint.ColorOp{Color: t.Theme.Oscilloscope.CurveColors[chn]}.Add(gtx.Ops)
p := t.Spectrum()[chn]
if len(p) <= 0 {
continue
}
fillRect(gtx, clip.Rect{Min: image.Pt(0, 0), Max: image.Pt(gtx.Constraints.Max.X, 1)})
fillRect(gtx, clip.Rect{Min: image.Pt(0, gtx.Constraints.Min.Y-1), Max: image.Pt(gtx.Constraints.Max.X, gtx.Constraints.Max.Y)})
for px := range gtx.Constraints.Max.X {
y2 := gtx.Constraints.Max.Y - 1
y1 := int(-p[px*len(p)/gtx.Constraints.Max.X] / 80 * float32(y2))
fillRect(gtx, clip.Rect{Min: image.Pt(px, y1), Max: image.Pt(px+1, y2+1)})
}
}
return D{Size: image.Pt(gtx.Constraints.Max.X, gtx.Constraints.Max.Y)}
}