mirror of
https://github.com/vsariola/sointu.git
synced 2025-05-28 03:10:24 -04:00
feat(tracker): add simple instrument editor
This commit is contained in:
parent
e62fe85867
commit
fa893c94f1
121
tracker/instruments.go
Normal file
121
tracker/instruments.go
Normal file
@ -0,0 +1,121 @@
|
||||
package tracker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"gioui.org/layout"
|
||||
"gioui.org/widget"
|
||||
"gioui.org/widget/material"
|
||||
)
|
||||
|
||||
type C = layout.Context
|
||||
type D = layout.Dimensions
|
||||
|
||||
func (t *Tracker) updateInstrumentScroll() {
|
||||
if t.CurrentInstrument > 7 {
|
||||
t.InstrumentList.Position.First = t.CurrentInstrument - 7
|
||||
} else {
|
||||
t.InstrumentList.Position.First = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tracker) layoutInstruments() layout.Widget {
|
||||
return func(gtx C) D {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(t.layoutInstrumentNames()),
|
||||
layout.Flexed(1, t.layoutInstrumentEditor()))
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tracker) layoutInstrumentNames() layout.Widget {
|
||||
return func(gtx C) D {
|
||||
count := len(t.song.Patch.Instruments)
|
||||
if len(t.InstrumentBtns) < count {
|
||||
tail := make([]*widget.Clickable, count-len(t.InstrumentBtns))
|
||||
for t := range tail {
|
||||
tail[t] = new(widget.Clickable)
|
||||
}
|
||||
t.InstrumentBtns = append(t.InstrumentBtns, tail...)
|
||||
}
|
||||
return t.InstrumentList.Layout(gtx, count, func(gtx C, index int) D {
|
||||
for t.InstrumentBtns[index].Clicked() {
|
||||
t.CurrentInstrument = index
|
||||
}
|
||||
btnStyle := material.Button(t.Theme, t.InstrumentBtns[index], fmt.Sprintf("%v", index))
|
||||
btnStyle.Background = transparent
|
||||
return btnStyle.Layout(gtx)
|
||||
})
|
||||
}
|
||||
}
|
||||
func (t *Tracker) layoutInstrumentEditor() layout.Widget {
|
||||
return func(gtx C) D {
|
||||
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
|
||||
layout.Rigid(t.layoutUnitList()),
|
||||
layout.Rigid(t.layoutUnitControls()))
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tracker) layoutUnitList() layout.Widget {
|
||||
return func(gtx C) D {
|
||||
units := t.song.Patch.Instruments[t.CurrentInstrument].Units
|
||||
count := len(units)
|
||||
if len(t.UnitBtns) < count {
|
||||
tail := make([]*widget.Clickable, count-len(t.UnitBtns))
|
||||
for t := range tail {
|
||||
tail[t] = new(widget.Clickable)
|
||||
}
|
||||
t.UnitBtns = append(t.UnitBtns, tail...)
|
||||
}
|
||||
children := make([]layout.FlexChild, len(t.song.Patch.Instruments[t.CurrentInstrument].Units))
|
||||
for i, u := range t.song.Patch.Instruments[t.CurrentInstrument].Units {
|
||||
for t.UnitBtns[i].Clicked() {
|
||||
t.CurrentUnit = i
|
||||
}
|
||||
btnStyle := material.Button(t.Theme, t.UnitBtns[i], u.Type)
|
||||
btnStyle.Background = transparent
|
||||
children[i] = layout.Rigid(btnStyle.Layout)
|
||||
}
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, children...)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tracker) layoutUnitControls() layout.Widget {
|
||||
return func(gtx C) D {
|
||||
params := t.song.Patch.Instruments[t.CurrentInstrument].Units[t.CurrentUnit].Parameters
|
||||
count := len(params)
|
||||
children := make([]layout.FlexChild, 0, count)
|
||||
if len(t.ParameterSliders) < count {
|
||||
tail := make([]*widget.Float, count-len(t.ParameterSliders))
|
||||
for t := range tail {
|
||||
tail[t] = new(widget.Float)
|
||||
}
|
||||
t.ParameterSliders = append(t.ParameterSliders, tail...)
|
||||
}
|
||||
keys := make([]string, 0, len(params))
|
||||
for k := range params {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for i, k := range keys {
|
||||
for t.ParameterSliders[i].Changed() {
|
||||
params[k] = int(t.ParameterSliders[i].Value)
|
||||
// TODO: tracker should have functions to update parameters and
|
||||
// to do this efficiently i.e. not compile the whole patch again
|
||||
t.LoadSong(t.song)
|
||||
}
|
||||
t.ParameterSliders[i].Value = float32(params[k])
|
||||
sliderStyle := material.Slider(t.Theme, t.ParameterSliders[i], 0, 128)
|
||||
k2 := k // avoid k changing in the closure
|
||||
children = append(children, layout.Rigid(func(gtx C) D {
|
||||
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
|
||||
layout.Rigid(Label(k2, white)),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
gtx.Constraints.Min.X = 200
|
||||
return sliderStyle.Layout(gtx)
|
||||
}))
|
||||
}))
|
||||
}
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, children...)
|
||||
}
|
||||
}
|
@ -59,6 +59,7 @@ func (t *Tracker) Layout(gtx layout.Context) {
|
||||
layout.Rigid(t.line(true, separatorLineColor)),
|
||||
layout.Flexed(1, t.layoutTracker))
|
||||
})
|
||||
t.updateInstrumentScroll()
|
||||
}
|
||||
|
||||
func (t *Tracker) layoutTracker(gtx layout.Context) layout.Dimensions {
|
||||
@ -184,6 +185,7 @@ func (t *Tracker) layoutControls(gtx layout.Context) layout.Dimensions {
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return in.Layout(gtx, enableButton(smallButton(material.IconButton(t.Theme, t.BPMDownBtn, downIcon)), t.song.BPM > 1).Layout)
|
||||
}),
|
||||
layout.Flexed(1, t.layoutInstruments()),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
iconBtn := enableButton(material.IconButton(t.Theme, t.NewInstrumentBtn, addIcon), t.song.Patch.TotalVoices() < 32)
|
||||
return in.Layout(gtx, iconBtn.Layout)
|
||||
|
@ -22,6 +22,8 @@ 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 red = color.RGBA{R: 255, G: 0, B: 0, A: 255}
|
||||
|
||||
var transparent = color.RGBA{A: 0}
|
||||
|
||||
var primaryColorLight = color.RGBA{R: 243, G: 229, B: 245, A: 255}
|
||||
var primaryColor = color.RGBA{R: 206, G: 147, B: 216, A: 255}
|
||||
var primaryColorDark = color.RGBA{R: 123, G: 31, B: 162, A: 255}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"gioui.org/font/gofont"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/widget"
|
||||
"gioui.org/widget/material"
|
||||
"github.com/vsariola/sointu"
|
||||
@ -17,22 +18,28 @@ type Tracker struct {
|
||||
song sointu.Song
|
||||
Playing bool
|
||||
// protects PlayPattern and PlayRow
|
||||
playRowPatMutex sync.RWMutex // protects song and playing
|
||||
PlayPattern int
|
||||
PlayRow int
|
||||
CursorRow int
|
||||
CursorColumn int
|
||||
DisplayPattern int
|
||||
ActiveTrack int
|
||||
CurrentOctave byte
|
||||
NoteTracking bool
|
||||
Theme *material.Theme
|
||||
OctaveUpBtn *widget.Clickable
|
||||
OctaveDownBtn *widget.Clickable
|
||||
BPMUpBtn *widget.Clickable
|
||||
BPMDownBtn *widget.Clickable
|
||||
NewTrackBtn *widget.Clickable
|
||||
NewInstrumentBtn *widget.Clickable
|
||||
playRowPatMutex sync.RWMutex // protects song and playing
|
||||
PlayPattern int
|
||||
PlayRow int
|
||||
CursorRow int
|
||||
CursorColumn int
|
||||
DisplayPattern int
|
||||
ActiveTrack int
|
||||
CurrentInstrument int
|
||||
CurrentUnit int
|
||||
CurrentOctave byte
|
||||
NoteTracking bool
|
||||
Theme *material.Theme
|
||||
OctaveUpBtn *widget.Clickable
|
||||
OctaveDownBtn *widget.Clickable
|
||||
BPMUpBtn *widget.Clickable
|
||||
BPMDownBtn *widget.Clickable
|
||||
NewTrackBtn *widget.Clickable
|
||||
NewInstrumentBtn *widget.Clickable
|
||||
ParameterSliders []*widget.Float
|
||||
UnitBtns []*widget.Clickable
|
||||
InstrumentBtns []*widget.Clickable
|
||||
InstrumentList *layout.List
|
||||
|
||||
sequencer *Sequencer
|
||||
ticked chan struct{}
|
||||
@ -75,6 +82,9 @@ func (t *Tracker) LoadSong(song sointu.Song) error {
|
||||
if t.ActiveTrack >= len(song.Tracks) {
|
||||
t.ActiveTrack = len(song.Tracks) - 1
|
||||
}
|
||||
if t.sequencer != nil {
|
||||
t.sequencer.SetSynth(t.synth)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -241,6 +251,7 @@ func New(audioContext sointu.AudioContext) *Tracker {
|
||||
closer: make(chan struct{}),
|
||||
undoStack: []sointu.Song{},
|
||||
redoStack: []sointu.Song{},
|
||||
InstrumentList: &layout.List{Axis: layout.Horizontal},
|
||||
}
|
||||
t.Theme.Color.Primary = primaryColor
|
||||
t.Theme.Color.InvText = black
|
||||
|
Loading…
Reference in New Issue
Block a user