mirror of
https://github.com/vsariola/sointu.git
synced 2025-06-04 01:28:45 -04:00
feat(tracker): Add a matrix showing track sequences
This commit is contained in:
parent
c68d9d3bf5
commit
06c006086b
@ -6,11 +6,13 @@ var defaultSong = sointu.Song{
|
||||
BPM: 100,
|
||||
Patterns: [][]byte{
|
||||
{64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0},
|
||||
{64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 75, 0, 75, 0, 80, 0},
|
||||
{0, 0, 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0},
|
||||
{32, 0, 64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 68, 0, 68, 0},
|
||||
},
|
||||
Tracks: []sointu.Track{
|
||||
{NumVoices: 2, Sequence: []byte{0}},
|
||||
{NumVoices: 2, Sequence: []byte{1}},
|
||||
{NumVoices: 2, Sequence: []byte{0, 0, 0, 1}},
|
||||
{NumVoices: 2, Sequence: []byte{2, 2, 2, 3}},
|
||||
},
|
||||
Patch: sointu.Patch{
|
||||
Instruments: []sointu.Instrument{{NumVoices: 4, Units: []sointu.Unit{
|
||||
|
@ -80,9 +80,11 @@ func (t *Tracker) KeyEvent(e key.Event) bool {
|
||||
}
|
||||
case key.NameUpArrow:
|
||||
t.CursorRow = (t.CursorRow + t.song.PatternRows() - 1) % t.song.PatternRows()
|
||||
t.NoteTracking = false
|
||||
return true
|
||||
case key.NameDownArrow:
|
||||
t.CursorRow = (t.CursorRow + 1) % t.song.PatternRows()
|
||||
t.NoteTracking = false
|
||||
return true
|
||||
case key.NameLeftArrow:
|
||||
if t.CursorColumn == 0 {
|
||||
|
@ -9,6 +9,13 @@ import (
|
||||
func (t *Tracker) Layout(gtx layout.Context) {
|
||||
layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(t.layoutControls),
|
||||
layout.Rigid(Lowered(t.layoutPatterns(
|
||||
t.song.Tracks,
|
||||
t.ActiveTrack,
|
||||
t.DisplayPattern,
|
||||
t.CursorColumn,
|
||||
t.PlayPattern,
|
||||
))),
|
||||
layout.Flexed(1, Lowered(t.layoutTracker)),
|
||||
)
|
||||
}
|
||||
@ -17,13 +24,17 @@ func (t *Tracker) layoutTracker(gtx layout.Context) layout.Dimensions {
|
||||
flexTracks := make([]layout.FlexChild, len(t.song.Tracks))
|
||||
t.playRowPatMutex.RLock()
|
||||
defer t.playRowPatMutex.RUnlock()
|
||||
playRow := int(t.PlayRow)
|
||||
if t.DisplayPattern != t.PlayPattern {
|
||||
playRow = -1
|
||||
}
|
||||
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,
|
||||
int(t.PlayRow),
|
||||
playRow,
|
||||
)))
|
||||
}
|
||||
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
|
||||
@ -32,8 +43,8 @@ func (t *Tracker) layoutTracker(gtx layout.Context) layout.Dimensions {
|
||||
}
|
||||
|
||||
func (t *Tracker) layoutControls(gtx layout.Context) layout.Dimensions {
|
||||
gtx.Constraints.Min.Y = 400
|
||||
gtx.Constraints.Max.Y = 400
|
||||
gtx.Constraints.Min.Y = 200
|
||||
gtx.Constraints.Max.Y = 200
|
||||
return layout.Stack{Alignment: layout.NW}.Layout(gtx,
|
||||
layout.Expanded(t.QuitButton.Layout),
|
||||
layout.Stacked(Raised(Label(fmt.Sprintf("Current octave: %v", t.CurrentOctave), white))),
|
||||
|
57
tracker/patterns.go
Normal file
57
tracker/patterns.go
Normal file
@ -0,0 +1,57 @@
|
||||
package tracker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
|
||||
"gioui.org/f32"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op"
|
||||
"gioui.org/op/clip"
|
||||
"gioui.org/op/paint"
|
||||
"gioui.org/widget"
|
||||
"github.com/vsariola/sointu"
|
||||
)
|
||||
|
||||
const patternCellHeight = 12
|
||||
const patternCellWidth = 16
|
||||
|
||||
func (t *Tracker) layoutPatterns(tracks []sointu.Track, activeTrack, cursorPattern, cursorCol, playingPattern int) layout.Widget {
|
||||
return func(gtx layout.Context) layout.Dimensions {
|
||||
gtx.Constraints.Min.X = patternCellWidth * len(tracks)
|
||||
gtx.Constraints.Max.X = patternCellWidth * len(tracks)
|
||||
gtx.Constraints.Max.Y = 50
|
||||
defer op.Push(gtx.Ops).Pop()
|
||||
clip.Rect{Max: gtx.Constraints.Max}.Add(gtx.Ops)
|
||||
paint.FillShape(gtx.Ops, panelColor, clip.Rect{Max: image.Pt(gtx.Constraints.Max.X, trackRowHeight)}.Op())
|
||||
for i, track := range tracks {
|
||||
pop := op.Push(gtx.Ops)
|
||||
gtx.Constraints.Max.X = patternCellWidth
|
||||
clip.Rect{Max: gtx.Constraints.Max}.Add(gtx.Ops)
|
||||
if activeTrack == i {
|
||||
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())
|
||||
}
|
||||
for j, p := range track.Sequence {
|
||||
if j == playingPattern {
|
||||
paint.FillShape(gtx.Ops, patternPlayColor, clip.Rect{Max: image.Pt(trackWidth, trackRowHeight)}.Op())
|
||||
}
|
||||
if j == cursorPattern {
|
||||
paint.ColorOp{Color: trackerActiveTextColor}.Add(gtx.Ops)
|
||||
} else {
|
||||
paint.ColorOp{Color: trackerTextColor}.Add(gtx.Ops)
|
||||
}
|
||||
widget.Label{}.Layout(gtx, textShaper, trackerFont, trackerFontSize, fmt.Sprintf("%d", p))
|
||||
op.Offset(f32.Pt(0, patternCellHeight)).Add(gtx.Ops)
|
||||
}
|
||||
pop.Pop()
|
||||
op.Offset(f32.Pt(patternCellWidth, 0)).Add(gtx.Ops)
|
||||
}
|
||||
return layout.Dimensions{Size: gtx.Constraints.Max}
|
||||
}
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
package tracker
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
"gioui.org/font/gofont"
|
||||
"gioui.org/text"
|
||||
"gioui.org/unit"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
var fontCollection []text.FontFace = gofont.Collection()
|
||||
@ -33,3 +34,10 @@ var trackerFontSize = unit.Px(16)
|
||||
var trackerTextColor = white
|
||||
var trackerActiveTextColor = yellow
|
||||
var trackerPlayColor = red
|
||||
|
||||
var patternBgColor = black
|
||||
var patternPlayColor = red
|
||||
var patternTextColor = white
|
||||
var patternActiveTextColor = yellow
|
||||
var patternFont = fontCollection[6].Font
|
||||
var patternFontSize = unit.Px(12)
|
||||
|
@ -23,6 +23,7 @@ type Tracker struct {
|
||||
DisplayPattern int
|
||||
ActiveTrack int
|
||||
CurrentOctave byte
|
||||
NoteTracking bool
|
||||
|
||||
ticked chan struct{}
|
||||
setPlaying chan bool
|
||||
@ -59,6 +60,9 @@ func (t *Tracker) TogglePlay() {
|
||||
t.songPlayMutex.Lock()
|
||||
defer t.songPlayMutex.Unlock()
|
||||
t.Playing = !t.Playing
|
||||
if t.Playing {
|
||||
t.NoteTracking = true
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tracker) sequencerLoop(closer <-chan struct{}) {
|
||||
@ -85,6 +89,10 @@ func (t *Tracker) sequencerLoop(closer <-chan struct{}) {
|
||||
if t.PlayPattern >= t.song.SequenceLength() {
|
||||
t.PlayPattern = 0
|
||||
}
|
||||
if t.NoteTracking {
|
||||
t.DisplayPattern = t.PlayPattern
|
||||
t.CursorRow = t.PlayRow
|
||||
}
|
||||
notes := make([]Note, 0, 32)
|
||||
for track := range t.song.Tracks {
|
||||
patternIndex := t.song.Tracks[track].Sequence[t.PlayPattern]
|
||||
|
Loading…
x
Reference in New Issue
Block a user