mirror of
https://github.com/vsariola/sointu.git
synced 2025-06-04 01:28:45 -04:00
feat(tracker): implement basic track display
This commit is contained in:
parent
90c3536f3e
commit
77949bdc17
27
go4k/tracker/defaultsong.go
Normal file
27
go4k/tracker/defaultsong.go
Normal file
@ -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}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
@ -2,15 +2,34 @@ package tracker
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
"gioui.org/op/paint"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t *Tracker) Layout(gtx layout.Context) {
|
func (t *Tracker) Layout(gtx layout.Context) {
|
||||||
layout.Stack{Alignment: layout.NW}.Layout(gtx,
|
layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||||
layout.Expanded(func(gtx layout.Context) layout.Dimensions {
|
layout.Rigid(t.layoutControls),
|
||||||
paint.Fill(gtx.Ops, black)
|
layout.Flexed(1, Lowered(t.layoutTracker)),
|
||||||
return layout.Dimensions{Size: gtx.Constraints.Max}
|
)
|
||||||
}),
|
}
|
||||||
|
|
||||||
|
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.Expanded(t.QuitButton.Layout),
|
||||||
layout.Stacked(Raised(Label("Hello", white))),
|
layout.Stacked(Raised(Label("Hello", white))),
|
||||||
)
|
)
|
||||||
|
29
go4k/tracker/music.go
Normal file
29
go4k/tracker/music.go
Normal file
@ -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)
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package tracker
|
package tracker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"gioui.org/f32"
|
"gioui.org/f32"
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
"gioui.org/op"
|
"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 {
|
func Beveled(w layout.Widget, base, light, shade color.RGBA) layout.Widget {
|
||||||
return func(gtx layout.Context) layout.Dimensions {
|
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, 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, 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, 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(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())
|
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)
|
stack := op.Push(gtx.Ops)
|
||||||
mcs := gtx.Constraints
|
mcs := gtx.Constraints
|
||||||
mcs.Max.X -= 2
|
mcs.Max.X -= 2
|
||||||
|
@ -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 dark = color.RGBA{R: 24, G: 40, B: 44, A: 255}
|
||||||
var white = color.RGBA{R: 255, G: 255, B: 255, 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 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 panelColor = neutral
|
||||||
var panelShadeColor = dark
|
var panelShadeColor = dark
|
||||||
@ -22,3 +23,11 @@ var panelLightColor = light
|
|||||||
|
|
||||||
var labelFont = fontCollection[6].Font
|
var labelFont = fontCollection[6].Font
|
||||||
var labelFontSize = unit.Px(18)
|
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
|
||||||
|
60
go4k/tracker/track.go
Normal file
60
go4k/tracker/track.go
Normal file
@ -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}
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,22 @@
|
|||||||
package tracker
|
package tracker
|
||||||
|
|
||||||
import "gioui.org/widget"
|
import (
|
||||||
|
"gioui.org/widget"
|
||||||
|
"github.com/vsariola/sointu/go4k"
|
||||||
|
)
|
||||||
|
|
||||||
type Tracker struct {
|
type Tracker struct {
|
||||||
QuitButton *widget.Clickable
|
QuitButton *widget.Clickable
|
||||||
|
song go4k.Song
|
||||||
|
CursorRow int
|
||||||
|
CursorColumn int
|
||||||
|
DisplayPattern int
|
||||||
|
ActiveTrack int
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Tracker {
|
func New() *Tracker {
|
||||||
return &Tracker{
|
return &Tracker{
|
||||||
QuitButton: new(widget.Clickable),
|
QuitButton: new(widget.Clickable),
|
||||||
|
song: defaultSong,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user