feat(tracker): add simple BPM & octave buttons

This commit is contained in:
vsariola 2021-01-07 19:25:16 +02:00
parent 1d524b5815
commit 82d26b79a4
3 changed files with 101 additions and 14 deletions

View File

@ -70,16 +70,9 @@ func (t *Tracker) KeyEvent(e key.Event) bool {
return true
case `\`:
if e.Modifiers.Contain(key.ModShift) {
if t.CurrentOctave < 9 {
t.CurrentOctave++
return true
}
} else {
if t.CurrentOctave > 0 {
t.CurrentOctave--
return true
}
return t.ChangeBPM(1)
}
return t.ChangeBPM(-1)
case key.NameUpArrow:
delta := -1
if e.Modifiers.Contain(key.ModCtrl) {

View File

@ -3,14 +3,34 @@ package tracker
import (
"fmt"
"image"
"log"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/op/clip"
"gioui.org/op/paint"
"gioui.org/unit"
"gioui.org/widget"
"gioui.org/widget/material"
"golang.org/x/exp/shiny/materialdesign/icons"
)
var upIcon *widget.Icon
var downIcon *widget.Icon
func init() {
var err error
upIcon, err = widget.NewIcon(icons.NavigationArrowUpward)
if err != nil {
log.Fatal(err)
}
downIcon, err = widget.NewIcon(icons.NavigationArrowDownward)
if err != nil {
log.Fatal(err)
}
}
func (t *Tracker) Layout(gtx layout.Context) {
paint.FillShape(gtx.Ops, black, clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Op())
layout.UniformInset(unit.Dp(2)).Layout(gtx, func(gtx2 layout.Context) layout.Dimensions {
@ -56,6 +76,20 @@ func (t *Tracker) layoutControls(gtx layout.Context) layout.Dimensions {
if !t.Playing {
playPat = -1
}
in := layout.UniformInset(unit.Dp(8))
for t.OctaveUpBtn.Clicked() {
t.ChangeOctave(1)
}
for t.OctaveDownBtn.Clicked() {
t.ChangeOctave(-1)
}
for t.BPMUpBtn.Clicked() {
t.ChangeBPM(1)
}
for t.BPMDownBtn.Clicked() {
t.ChangeBPM(-1)
}
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
layout.Rigid(Raised(t.layoutPatterns(
@ -66,7 +100,25 @@ func (t *Tracker) layoutControls(gtx layout.Context) layout.Dimensions {
playPat,
))),
layout.Rigid(t.darkLine(false)),
layout.Flexed(1, Raised(Label(fmt.Sprintf("Current octave: %v", t.CurrentOctave), white))),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return in.Layout(gtx, material.IconButton(t.Theme, t.OctaveUpBtn, upIcon).Layout)
}),
layout.Rigid(t.darkLine(false)),
layout.Rigid(Raised(Label(fmt.Sprintf("OCT: %v", t.CurrentOctave), white))),
layout.Rigid(t.darkLine(false)),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return in.Layout(gtx, material.IconButton(t.Theme, t.OctaveDownBtn, downIcon).Layout)
}),
layout.Rigid(t.darkLine(false)),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return in.Layout(gtx, material.IconButton(t.Theme, t.BPMUpBtn, upIcon).Layout)
}),
layout.Rigid(t.darkLine(false)),
layout.Rigid(Raised(Label(fmt.Sprintf("BPM: %3v", t.song.BPM), white))),
layout.Rigid(t.darkLine(false)),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return in.Layout(gtx, material.IconButton(t.Theme, t.BPMDownBtn, downIcon).Layout)
}),
)
}

View File

@ -4,7 +4,9 @@ import (
"fmt"
"sync"
"gioui.org/font/gofont"
"gioui.org/widget"
"gioui.org/widget/material"
"github.com/vsariola/sointu"
"github.com/vsariola/sointu/bridge"
)
@ -24,7 +26,13 @@ type Tracker struct {
ActiveTrack int
CurrentOctave byte
NoteTracking bool
Theme *material.Theme
OctaveUpBtn *widget.Clickable
OctaveDownBtn *widget.Clickable
BPMUpBtn *widget.Clickable
BPMDownBtn *widget.Clickable
sequencer *Sequencer
ticked chan struct{}
setPlaying chan bool
rowJump chan int
@ -75,9 +83,7 @@ func (t *Tracker) sequencerLoop(closer <-chan struct{}) {
panic("cannot create a synth with the default patch")
}
curVoices := make([]int, 32)
sequencer := NewSequencer(synth, 44100*60/(4*t.song.BPM), func() ([]Note, bool) {
t.songPlayMutex.RLock()
defer t.songPlayMutex.RUnlock()
t.sequencer = NewSequencer(synth, 44100*60/(4*t.song.BPM), func() ([]Note, bool) {
if !t.Playing {
return nil, false
}
@ -121,17 +127,53 @@ func (t *Tracker) sequencerLoop(closer <-chan struct{}) {
case <-closer:
return
default:
sequencer.ReadAudio(buffer)
t.sequencer.ReadAudio(buffer)
output.WriteAudio(buffer)
}
}
}
func (t *Tracker) ChangeOctave(delta int) bool {
newOctave := int(t.CurrentOctave) + delta
if newOctave < 0 {
newOctave = 0
}
if newOctave > 9 {
newOctave = 9
}
if newOctave != int(t.CurrentOctave) {
t.CurrentOctave = byte(newOctave)
return true
}
return false
}
func (t *Tracker) ChangeBPM(delta int) bool {
newBPM := t.song.BPM + delta
if newBPM < 1 {
newBPM = 1
}
if newBPM > 999 {
newBPM = 999
}
if newBPM != int(t.song.BPM) {
t.song.BPM = newBPM
t.sequencer.SetRowLength(44100 * 60 / (4 * t.song.BPM))
return true
}
return false
}
func New(audioContext sointu.AudioContext) *Tracker {
t := &Tracker{
Theme: material.NewTheme(gofont.Collection()),
QuitButton: new(widget.Clickable),
CurrentOctave: 4,
audioContext: audioContext,
OctaveUpBtn: new(widget.Clickable),
OctaveDownBtn: new(widget.Clickable),
BPMUpBtn: new(widget.Clickable),
BPMDownBtn: new(widget.Clickable),
setPlaying: make(chan bool),
rowJump: make(chan int),
patternJump: make(chan int),