// // rhlibrary // // The Radio Helsinki Rivendell Library // // // Copyright (C) 2016 Christian Pointner // // This file is part of rhlibrary. // // rhlibrary is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // any later version. // // rhlibrary is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with rhlibrary. If not, see . // package main import ( "time" "code.helsinki.at/rhrd-go/player" "github.com/gotk3/gotk3/glib" "github.com/gotk3/gotk3/gtk" ) const ( meterDBFloor float64 = 72 // -72 db is the lowest VU value meterSegments float64 = 30 // meter has 30 elements meterOffsetGreen float64 = meterSegments - 4 meterOffsetOrange float64 = meterSegments - 2 meterOffsetRed float64 = meterSegments - 1 ) var ( levelBarCss *gtk.CssProvider ) func init() { var err error if levelBarCss, err = gtk.CssProviderNew(); err != nil { panic(err.Error()) } err = levelBarCss.LoadFromData(` .level-bar { -GtkLevelBar-min-block-width: 13; -GtkLevelBar-min-block-height: 8; } .level-bar.fill-block.level-green { border-color: #00AE00; background-image: linear-gradient(to bottom, #36FF36, #00AE00); } .level-bar.fill-block.level-orange { border-color: #AE6600; background-image: linear-gradient(to bottom, #FFAC36, #AE6600); } .level-bar.fill-block.level-red { border-color: #AE0000; background-image: linear-gradient(to bottom, #FF3636, #AE0000); }`) if err != nil { panic(err.Error()) } } func addPlayButton(grid *gtk.Grid, p *player.PlayerChan) (err error) { var btn *gtk.Button if btn, err = gtk.ButtonNewFromIconName("media-playback-start", gtk.ICON_SIZE_DIALOG); err != nil { return } btn.Connect("clicked", func(_ *gtk.Button) { if err := p.Play(); err != nil { rhdl.Println(err) // TODO: feedback at GUI? } }) grid.Add(btn) return } func addPauseButton(grid *gtk.Grid, p *player.PlayerChan) (err error) { var btn *gtk.Button if btn, err = gtk.ButtonNewFromIconName("media-playback-pause", gtk.ICON_SIZE_DIALOG); err != nil { return } btn.Connect("clicked", func(_ *gtk.Button) { if err := p.Pause(); err != nil { rhdl.Println(err) // TODO: feedback at GUI? } }) grid.Add(btn) return } func addStopButton(grid *gtk.Grid, p *player.PlayerChan) (err error) { var btn *gtk.Button if btn, err = gtk.ButtonNewFromIconName("media-playback-stop", gtk.ICON_SIZE_DIALOG); err != nil { return } btn.Connect("clicked", func(_ *gtk.Button) { if err := p.Stop(); err != nil { rhdl.Println(err) // TODO: feedback at GUI? } }) grid.Add(btn) return } func calcMeterLevel(valdb float64) (val float64) { val = valdb + meterDBFloor if val < 0 { val = 0 } else { val = (val / meterDBFloor) * meterSegments } return } func createMeterBar() (bar *gtk.LevelBar, err error) { if bar, err = gtk.LevelBarNewForInterval(0, meterSegments); err != nil { return } bar.SetMode(gtk.LEVEL_BAR_MODE_DISCRETE) bar.RemoveOffsetValue(gtk.LEVEL_BAR_OFFSET_LOW) bar.RemoveOffsetValue(gtk.LEVEL_BAR_OFFSET_HIGH) var sc *gtk.StyleContext if sc, err = bar.GetStyleContext(); err != nil { return } sc.AddProvider(levelBarCss, 600) // TOOD: hardcoded value bar.AddOffsetValue("green", meterOffsetGreen) bar.AddOffsetValue("orange", meterOffsetOrange) bar.AddOffsetValue("red", meterOffsetRed) return } func addMeter(grid *gtk.Grid, p *player.PlayerChan) (err error) { var mgrid *gtk.Grid if mgrid, err = gtk.GridNew(); err != nil { return } mgrid.SetOrientation(gtk.ORIENTATION_VERTICAL) mgrid.SetColumnSpacing(3) var left, right *gtk.LevelBar if left, err = createMeterBar(); err != nil { return } if right, err = createMeterBar(); err != nil { return } p.AddUpdateHandler(func(length time.Duration, pos time.Duration, meter player.Meter, userdata interface{}) bool { glib.IdleAdd(func() { ch := len(meter) lval := float64(0) rval := float64(0) if ch >= 2 { lval = calcMeterLevel(meter[0].Peak) rval = calcMeterLevel(meter[1].Peak) } else if ch == 1 { lval = calcMeterLevel(meter[0].Peak) rval = lval } left.SetValue(lval) right.SetValue(rval) }) return true }, nil) mgrid.Add(left) mgrid.Add(right) grid.Add(mgrid) return } func getPlayerWidget(p *player.PlayerChan) (gtk.IWidget, error) { var err error var grid *gtk.Grid if grid, err = gtk.GridNew(); err != nil { return nil, err } grid.SetOrientation(gtk.ORIENTATION_HORIZONTAL) grid.SetColumnSpacing(3) if err = addPlayButton(grid, p); err != nil { return nil, err } if err = addPauseButton(grid, p); err != nil { return nil, err } if err = addStopButton(grid, p); err != nil { return nil, err } if err = addMeter(grid, p); err != nil { return nil, err } var frame *gtk.Frame if frame, err = gtk.FrameNew("player"); err != nil { return nil, err } frame.Add(grid) return frame, nil }