diff --git a/backend/player/mpv/player.go b/backend/player/mpv/player.go index 1a6999b..a0b2c7e 100644 --- a/backend/player/mpv/player.go +++ b/backend/player/mpv/player.go @@ -460,11 +460,14 @@ func (p *Player) GetPeaks() (float64, float64, float64, float64) { return nInf, nInf, nInf, nInf } m := prop.(*mpv.Node).Data.(map[string]*mpv.Node) - lPeak, _ := strconv.ParseFloat(m["lavfi.astats.1.Peak_level"].Data.(string), 64) - rPeak, _ := strconv.ParseFloat(m["lavfi.astats.2.Peak_level"].Data.(string), 64) - lRMS, _ := strconv.ParseFloat(m["lavfi.astats.1.RMS_level"].Data.(string), 64) - rRMS, _ := strconv.ParseFloat(m["lavfi.astats.2.RMS_level"].Data.(string), 64) - return lPeak, rPeak, lRMS, rRMS + if lPeakNode, ok := m["lavfi.astats.1.Peak_level"]; ok { + lPeak, _ := strconv.ParseFloat(lPeakNode.Data.(string), 64) + rPeak, _ := strconv.ParseFloat(m["lavfi.astats.2.Peak_level"].Data.(string), 64) + lRMS, _ := strconv.ParseFloat(m["lavfi.astats.1.RMS_level"].Data.(string), 64) + rRMS, _ := strconv.ParseFloat(m["lavfi.astats.2.RMS_level"].Data.(string), 64) + return lPeak, rPeak, lRMS, rRMS + } + return nInf, nInf, nInf, nInf } // sets the state and invokes callbacks, if triggered diff --git a/main.go b/main.go index 40371ac..67cd2cb 100644 --- a/main.go +++ b/main.go @@ -74,15 +74,6 @@ func main() { } }() - - myApp.LocalPlayer.SetPeaksEnabled(true) - - peaks := fyneApp.NewWindow("Peak meter") - meter := ui.NewPeakMeter(myApp.LocalPlayer.GetPeaks) - peaks.SetContent(meter) - meter.Start() - peaks.Show() - mainWindow.ShowAndRun() log.Println("Running shutdown tasks...") diff --git a/ui/controller/controller.go b/ui/controller/controller.go index d6f7a06..63e9f64 100644 --- a/ui/controller/controller.go +++ b/ui/controller/controller.go @@ -36,6 +36,7 @@ type NavigationHandler func(Route) type CurPageFunc func() Route type Controller struct { + visualizationData AppVersion string App *backend.App MainWindow fyne.Window diff --git a/ui/controller/visualizations.go b/ui/controller/visualizations.go new file mode 100644 index 0000000..3ff12d6 --- /dev/null +++ b/ui/controller/visualizations.go @@ -0,0 +1,68 @@ +package controller + +import ( + "math" + "time" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/lang" + "github.com/dweymouth/supersonic/ui/visualizations" +) + +// embedded in parent controller struct +type visualizationData struct { + peakMeter *visualizations.PeakMeter + + visualizationAnim *fyne.Animation +} + +func (c *Controller) InitVisualizations() { + c.App.LocalPlayer.OnStopped(c.stopVisualizationAnim) + c.App.LocalPlayer.OnPaused(c.stopVisualizationAnim) + c.App.LocalPlayer.OnPlaying(func() { + if c.peakMeter != nil { + c.startVisualizationAnim() + } + }) +} + +func (c *Controller) ShowPeakMeter() { + if c.peakMeter != nil { + return + } + win := fyne.CurrentApp().NewWindow(lang.L("Peak Meter")) + win.SetCloseIntercept(func() { + c.stopVisualizationAnim() + c.peakMeter = nil + win.Close() + }) + c.peakMeter = visualizations.NewPeakMeter() + win.SetContent(c.peakMeter) + c.startVisualizationAnim() + win.Show() +} + +func (c *Controller) stopVisualizationAnim() { + if c.visualizationAnim != nil { + c.visualizationAnim.Stop() + c.visualizationAnim = nil + c.App.LocalPlayer.SetPeaksEnabled(false) + } +} + +func (c *Controller) startVisualizationAnim() { + if c.visualizationAnim == nil { + c.App.LocalPlayer.SetPeaksEnabled(true) + c.visualizationAnim = fyne.NewAnimation( + time.Duration(math.MaxInt64), /*until stopped*/ + c.tickVisualizations) + c.visualizationAnim.Start() + } +} + +func (c *Controller) tickVisualizations(_ float32) { + lP, rP, lRMS, rRMS := c.App.LocalPlayer.GetPeaks() + if c.visualizationData.peakMeter != nil { + c.visualizationData.peakMeter.UpdatePeaks(lP, rP, lRMS, rRMS) + } +} diff --git a/ui/mainwindow.go b/ui/mainwindow.go index 4dbfe84..9b1fbef 100644 --- a/ui/mainwindow.go +++ b/ui/mainwindow.go @@ -21,6 +21,7 @@ import ( "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/driver/desktop" + "fyne.io/fyne/v2/lang" "fyne.io/fyne/v2/widget" ) @@ -80,6 +81,7 @@ func NewMainWindow(fyneApp fyne.App, appName, displayAppName, appVersion string, MainWindow: m.Window, App: app, } + m.Controller.InitVisualizations() m.BrowsingPane = browsing.NewBrowsingPane(app, m.Controller, func() { m.Router.NavigateTo(m.StartupPage()) }) m.Router = browsing.NewRouter(app, m.Controller, m.BrowsingPane) // inject controller dependencies @@ -136,6 +138,7 @@ func NewMainWindow(fyneApp fyne.App, appName, displayAppName, appVersion string, m.BrowsingPane.AddSettingsMenuItem("Switch Servers", func() { app.ServerManager.Logout(false) }) m.BrowsingPane.AddSettingsMenuItem("Rescan Library", func() { app.ServerManager.Server.RescanLibrary() }) m.BrowsingPane.AddSettingsMenuSeparator() + m.BrowsingPane.AddSettingsMenuItem(lang.L("Show Peak Meter"), m.Controller.ShowPeakMeter) m.BrowsingPane.AddSettingsMenuItem("Check for Updates", func() { go func() { if t := app.UpdateChecker.CheckLatestVersionTag(); t != "" && t != app.VersionTag() { diff --git a/ui/peakmeter.go b/ui/visualizations/peakmeter.go similarity index 90% rename from ui/peakmeter.go rename to ui/visualizations/peakmeter.go index 524811c..9bf2ba8 100644 --- a/ui/peakmeter.go +++ b/ui/visualizations/peakmeter.go @@ -1,10 +1,9 @@ -package ui +package visualizations import ( "fmt" "image/color" "math" - "time" "fyne.io/fyne/v2" "fyne.io/fyne/v2/canvas" @@ -20,11 +19,8 @@ const ( peakHoldFrames = 60 ) -type PeakFN func() (float64, float64, float64, float64) - type PeakMeter struct { widget.BaseWidget - peakFnDB PeakFN lPeak float64 rPeak float64 lRMS float64 @@ -34,36 +30,18 @@ type PeakMeter struct { lPeakHoldFrame uint64 rPeakHoldFrame uint64 frameCounter uint64 - - anim *fyne.Animation } -func NewPeakMeter(peakFnDB PeakFN) *PeakMeter { - p := &PeakMeter{peakFnDB: peakFnDB} +func NewPeakMeter() *PeakMeter { + p := &PeakMeter{} 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() +// UpdatePeaks updates the peaks that are displayed in the meter. +// This function is expected to be called from a fyne.Animation callback, +// running at 60 Hz +func (p *PeakMeter) UpdatePeaks(lPeak, rPeak, lRMS, rRMS float64) { p.lPeak = lPeak p.rPeak = rPeak lRMS = math.Max(-96, lRMS) @@ -231,5 +209,4 @@ func (l *peakMeterRenderer) Objects() []fyne.CanvasObject { } func (l *peakMeterRenderer) Destroy() { - l.p.Stop() }