From 77949bdc17372c411c9a54da15188874056cd990 Mon Sep 17 00:00:00 2001 From: Matias Lahti Date: Sun, 8 Nov 2020 02:24:27 +0200 Subject: [PATCH] feat(tracker): implement basic track display --- go4k/tracker/defaultsong.go | 27 +++++++++++++++++ go4k/tracker/layout.go | 31 +++++++++++++++---- go4k/tracker/music.go | 29 ++++++++++++++++++ go4k/tracker/panels.go | 3 -- go4k/tracker/theme.go | 9 ++++++ go4k/tracker/track.go | 60 +++++++++++++++++++++++++++++++++++++ go4k/tracker/tracker.go | 13 ++++++-- 7 files changed, 161 insertions(+), 11 deletions(-) create mode 100644 go4k/tracker/defaultsong.go create mode 100644 go4k/tracker/music.go create mode 100644 go4k/tracker/track.go diff --git a/go4k/tracker/defaultsong.go b/go4k/tracker/defaultsong.go new file mode 100644 index 0000000..ec4de66 --- /dev/null +++ b/go4k/tracker/defaultsong.go @@ -0,0 +1,27 @@ +package tracker + +import "github.com/vsariola/sointu/go4k" + +var defaultSong = go4k.Song{ + BPM: 100, + Patterns: [][]byte{ + {64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0}, + {0, 0, 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0}, + }, + Tracks: []go4k.Track{ + {NumVoices: 1, Sequence: []byte{0}}, + {NumVoices: 1, Sequence: []byte{1}}, + }, + SongLength: 0, + Patch: go4k.Patch{ + go4k.Instrument{NumVoices: 2, Units: []go4k.Unit{ + {"envelope", false, map[string]int{"attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}}, + {"oscillator", false, map[string]int{"transpose": 64, "detune": 64, "phase": 0, "color": 96, "shape": 64, "gain": 128, "flags": 0x40}}, + {"mulp", false, map[string]int{}}, + {"envelope", false, map[string]int{"attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}}, + {"oscillator", false, map[string]int{"transpose": 72, "detune": 64, "phase": 64, "color": 64, "shape": 96, "gain": 128, "flags": 0x40}}, + {"mulp", false, map[string]int{}}, + {"out", true, map[string]int{"gain": 128}}, + }}, + }, +} diff --git a/go4k/tracker/layout.go b/go4k/tracker/layout.go index dc4d1b8..fdf66fa 100644 --- a/go4k/tracker/layout.go +++ b/go4k/tracker/layout.go @@ -2,15 +2,34 @@ package tracker import ( "gioui.org/layout" - "gioui.org/op/paint" ) func (t *Tracker) Layout(gtx layout.Context) { - layout.Stack{Alignment: layout.NW}.Layout(gtx, - layout.Expanded(func(gtx layout.Context) layout.Dimensions { - paint.Fill(gtx.Ops, black) - return layout.Dimensions{Size: gtx.Constraints.Max} - }), + layout.Flex{Axis: layout.Vertical}.Layout(gtx, + layout.Rigid(t.layoutControls), + layout.Flexed(1, Lowered(t.layoutTracker)), + ) +} + +func (t *Tracker) layoutTracker(gtx layout.Context) layout.Dimensions { + flexTracks := make([]layout.FlexChild, len(t.song.Tracks)) + for i, trk := range t.song.Tracks { + flexTracks[i] = layout.Rigid(Lowered(t.layoutTrack( + t.song.Patterns[trk.Sequence[t.DisplayPattern]], + t.ActiveTrack == i, + t.CursorRow, + t.CursorColumn, + ))) + } + return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, + flexTracks..., + ) +} + +func (t *Tracker) layoutControls(gtx layout.Context) layout.Dimensions { + gtx.Constraints.Min.Y = 400 + gtx.Constraints.Max.Y = 400 + return layout.Stack{Alignment: layout.NW}.Layout(gtx, layout.Expanded(t.QuitButton.Layout), layout.Stacked(Raised(Label("Hello", white))), ) diff --git a/go4k/tracker/music.go b/go4k/tracker/music.go new file mode 100644 index 0000000..4a30d6b --- /dev/null +++ b/go4k/tracker/music.go @@ -0,0 +1,29 @@ +package tracker + +import "fmt" + +const baseNote = 20 + +var notes = []string{ + "C-", + "C#", + "D-", + "D#", + "E-", + "F-", + "F#", + "G-", + "G#", + "A-", + "A#", + "B-", +} + +func valueAsNote(val byte) string { + octave := (val - baseNote) / 12 + oNote := (val - baseNote) % 12 + if octave < 0 || oNote < 0 || octave > 10 { + return "..." + } + return fmt.Sprintf("%s%d", notes[oNote], octave) +} diff --git a/go4k/tracker/panels.go b/go4k/tracker/panels.go index da45f43..3e2b079 100644 --- a/go4k/tracker/panels.go +++ b/go4k/tracker/panels.go @@ -1,7 +1,6 @@ package tracker import ( - "fmt" "gioui.org/f32" "gioui.org/layout" "gioui.org/op" @@ -21,13 +20,11 @@ func Lowered(w layout.Widget) layout.Widget { func Beveled(w layout.Widget, base, light, shade color.RGBA) layout.Widget { return func(gtx layout.Context) layout.Dimensions { - fmt.Println("BR", gtx.Constraints) paint.FillShape(gtx.Ops, light, clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, 1)).Op()) paint.FillShape(gtx.Ops, light, clip.Rect(image.Rect(0, 0, 1, gtx.Constraints.Max.Y)).Op()) paint.FillShape(gtx.Ops, base, clip.Rect(image.Rect(1, 1, gtx.Constraints.Max.X-1, gtx.Constraints.Max.Y-1)).Op()) paint.FillShape(gtx.Ops, shade, clip.Rect(image.Rect(0, gtx.Constraints.Max.Y-1, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Op()) paint.FillShape(gtx.Ops, shade, clip.Rect(image.Rect(gtx.Constraints.Max.X-1, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Op()) - fmt.Println("drawing sub..", gtx.Constraints) stack := op.Push(gtx.Ops) mcs := gtx.Constraints mcs.Max.X -= 2 diff --git a/go4k/tracker/theme.go b/go4k/tracker/theme.go index 8b0bcc1..d3ca2df 100644 --- a/go4k/tracker/theme.go +++ b/go4k/tracker/theme.go @@ -15,6 +15,7 @@ var light = color.RGBA{R: 138, G: 219, B: 243, A: 255} var dark = color.RGBA{R: 24, G: 40, B: 44, A: 255} var white = color.RGBA{R: 255, G: 255, B: 255, A: 255} var black = color.RGBA{R: 0, G: 0, B: 0, A: 255} +var yellow = color.RGBA{R: 255, G: 255, B: 130, A: 255} var panelColor = neutral var panelShadeColor = dark @@ -22,3 +23,11 @@ var panelLightColor = light var labelFont = fontCollection[6].Font var labelFontSize = unit.Px(18) + +var activeTrackColor = color.RGBA{0, 0, 50, 255} +var inactiveTrackColor = black + +var trackerFont = fontCollection[6].Font +var trackerFontSize = unit.Px(16) +var trackerTextColor = white +var trackerActiveTextColor = yellow diff --git a/go4k/tracker/track.go b/go4k/tracker/track.go new file mode 100644 index 0000000..03139cf --- /dev/null +++ b/go4k/tracker/track.go @@ -0,0 +1,60 @@ +package tracker + +import ( + "fmt" + "gioui.org/f32" + "gioui.org/layout" + "gioui.org/op" + "gioui.org/op/clip" + "gioui.org/op/paint" + "gioui.org/widget" + "image" + "strings" +) + +const trackRowHeight = 16 +const trackWidth = 100 + +func (t *Tracker) layoutTrack(notes []byte, active bool, cursorRow, cursorCol int) layout.Widget { + return func(gtx layout.Context) layout.Dimensions { + gtx.Constraints.Min.X = trackWidth + gtx.Constraints.Max.X = trackWidth + if active { + paint.FillShape(gtx.Ops, activeTrackColor, clip.Rect{ + Max: gtx.Constraints.Max, + }.Op()) + } else { + paint.FillShape(gtx.Ops, inactiveTrackColor, clip.Rect{ + Max: gtx.Constraints.Max, + }.Op()) + } + defer op.Push(gtx.Ops).Pop() + // clip.Rect{Max:gtx.Constraints.Max}.Add(gtx.Ops) + op.Offset(f32.Pt(0, float32(gtx.Constraints.Max.Y/2)-trackRowHeight)).Add(gtx.Ops) + paint.FillShape(gtx.Ops, panelColor, clip.Rect{Max: image.Pt(gtx.Constraints.Max.X, trackRowHeight)}.Op()) + if active { + switch cursorCol { + case 0: + paint.FillShape(gtx.Ops, panelShadeColor, clip.Rect{Max: image.Pt(36, trackRowHeight)}.Op()) + case 1, 2: + s := op.Push(gtx.Ops) + op.Offset(f32.Pt(trackWidth/2+float32(cursorCol-1)*10, 0)).Add(gtx.Ops) + paint.FillShape(gtx.Ops, panelShadeColor, clip.Rect{Max: image.Pt(10, trackRowHeight)}.Op()) + s.Pop() + } + } + op.Offset(f32.Pt(0, (-1*trackRowHeight)*float32(cursorRow))).Add(gtx.Ops) + for i, c := range notes { + if i == cursorRow { + paint.ColorOp{Color: trackerActiveTextColor}.Add(gtx.Ops) + } else { + paint.ColorOp{Color: trackerTextColor}.Add(gtx.Ops) + } + widget.Label{}.Layout(gtx, textShaper, trackerFont, trackerFontSize, valueAsNote(c)) + op.Offset(f32.Pt(trackWidth/2, 0)).Add(gtx.Ops) + widget.Label{}.Layout(gtx, textShaper, trackerFont, trackerFontSize, strings.ToUpper(fmt.Sprintf("%02x", c))) + op.Offset(f32.Pt(-trackWidth/2, trackRowHeight)).Add(gtx.Ops) + } + return layout.Dimensions{Size: gtx.Constraints.Max} + } +} diff --git a/go4k/tracker/tracker.go b/go4k/tracker/tracker.go index 736e680..83eb644 100644 --- a/go4k/tracker/tracker.go +++ b/go4k/tracker/tracker.go @@ -1,13 +1,22 @@ package tracker -import "gioui.org/widget" +import ( + "gioui.org/widget" + "github.com/vsariola/sointu/go4k" +) type Tracker struct { - QuitButton *widget.Clickable + QuitButton *widget.Clickable + song go4k.Song + CursorRow int + CursorColumn int + DisplayPattern int + ActiveTrack int } func New() *Tracker { return &Tracker{ QuitButton: new(widget.Clickable), + song: defaultSong, } }