mirror of
https://github.com/vsariola/sointu.git
synced 2025-05-28 03:10:24 -04:00
feat(tracker): add simple BPM & octave buttons
This commit is contained in:
parent
1d524b5815
commit
82d26b79a4
@ -70,16 +70,9 @@ func (t *Tracker) KeyEvent(e key.Event) bool {
|
|||||||
return true
|
return true
|
||||||
case `\`:
|
case `\`:
|
||||||
if e.Modifiers.Contain(key.ModShift) {
|
if e.Modifiers.Contain(key.ModShift) {
|
||||||
if t.CurrentOctave < 9 {
|
return t.ChangeBPM(1)
|
||||||
t.CurrentOctave++
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if t.CurrentOctave > 0 {
|
|
||||||
t.CurrentOctave--
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return t.ChangeBPM(-1)
|
||||||
case key.NameUpArrow:
|
case key.NameUpArrow:
|
||||||
delta := -1
|
delta := -1
|
||||||
if e.Modifiers.Contain(key.ModCtrl) {
|
if e.Modifiers.Contain(key.ModCtrl) {
|
||||||
|
@ -3,14 +3,34 @@ package tracker
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
|
"log"
|
||||||
|
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
"gioui.org/op"
|
"gioui.org/op"
|
||||||
"gioui.org/op/clip"
|
"gioui.org/op/clip"
|
||||||
"gioui.org/op/paint"
|
"gioui.org/op/paint"
|
||||||
"gioui.org/unit"
|
"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) {
|
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())
|
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 {
|
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 {
|
if !t.Playing {
|
||||||
playPat = -1
|
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,
|
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
|
||||||
layout.Rigid(Raised(t.layoutPatterns(
|
layout.Rigid(Raised(t.layoutPatterns(
|
||||||
@ -66,7 +100,25 @@ func (t *Tracker) layoutControls(gtx layout.Context) layout.Dimensions {
|
|||||||
playPat,
|
playPat,
|
||||||
))),
|
))),
|
||||||
layout.Rigid(t.darkLine(false)),
|
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)
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"gioui.org/font/gofont"
|
||||||
"gioui.org/widget"
|
"gioui.org/widget"
|
||||||
|
"gioui.org/widget/material"
|
||||||
"github.com/vsariola/sointu"
|
"github.com/vsariola/sointu"
|
||||||
"github.com/vsariola/sointu/bridge"
|
"github.com/vsariola/sointu/bridge"
|
||||||
)
|
)
|
||||||
@ -24,7 +26,13 @@ type Tracker struct {
|
|||||||
ActiveTrack int
|
ActiveTrack int
|
||||||
CurrentOctave byte
|
CurrentOctave byte
|
||||||
NoteTracking bool
|
NoteTracking bool
|
||||||
|
Theme *material.Theme
|
||||||
|
OctaveUpBtn *widget.Clickable
|
||||||
|
OctaveDownBtn *widget.Clickable
|
||||||
|
BPMUpBtn *widget.Clickable
|
||||||
|
BPMDownBtn *widget.Clickable
|
||||||
|
|
||||||
|
sequencer *Sequencer
|
||||||
ticked chan struct{}
|
ticked chan struct{}
|
||||||
setPlaying chan bool
|
setPlaying chan bool
|
||||||
rowJump chan int
|
rowJump chan int
|
||||||
@ -75,9 +83,7 @@ func (t *Tracker) sequencerLoop(closer <-chan struct{}) {
|
|||||||
panic("cannot create a synth with the default patch")
|
panic("cannot create a synth with the default patch")
|
||||||
}
|
}
|
||||||
curVoices := make([]int, 32)
|
curVoices := make([]int, 32)
|
||||||
sequencer := NewSequencer(synth, 44100*60/(4*t.song.BPM), func() ([]Note, bool) {
|
t.sequencer = NewSequencer(synth, 44100*60/(4*t.song.BPM), func() ([]Note, bool) {
|
||||||
t.songPlayMutex.RLock()
|
|
||||||
defer t.songPlayMutex.RUnlock()
|
|
||||||
if !t.Playing {
|
if !t.Playing {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
@ -121,17 +127,53 @@ func (t *Tracker) sequencerLoop(closer <-chan struct{}) {
|
|||||||
case <-closer:
|
case <-closer:
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
sequencer.ReadAudio(buffer)
|
t.sequencer.ReadAudio(buffer)
|
||||||
output.WriteAudio(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 {
|
func New(audioContext sointu.AudioContext) *Tracker {
|
||||||
t := &Tracker{
|
t := &Tracker{
|
||||||
|
Theme: material.NewTheme(gofont.Collection()),
|
||||||
QuitButton: new(widget.Clickable),
|
QuitButton: new(widget.Clickable),
|
||||||
CurrentOctave: 4,
|
CurrentOctave: 4,
|
||||||
audioContext: audioContext,
|
audioContext: audioContext,
|
||||||
|
OctaveUpBtn: new(widget.Clickable),
|
||||||
|
OctaveDownBtn: new(widget.Clickable),
|
||||||
|
BPMUpBtn: new(widget.Clickable),
|
||||||
|
BPMDownBtn: new(widget.Clickable),
|
||||||
setPlaying: make(chan bool),
|
setPlaying: make(chan bool),
|
||||||
rowJump: make(chan int),
|
rowJump: make(chan int),
|
||||||
patternJump: make(chan int),
|
patternJump: make(chan int),
|
||||||
|
Loading…
Reference in New Issue
Block a user