diff --git a/tracker/sequencer.go b/tracker/sequencer.go index ac6911d..507873b 100644 --- a/tracker/sequencer.go +++ b/tracker/sequencer.go @@ -4,6 +4,7 @@ import ( "fmt" "math" "sync" + "sync/atomic" "github.com/vsariola/sointu" ) @@ -21,9 +22,10 @@ const SEQUENCER_MAX_READ_TRIES = 1000 type Sequencer struct { // we use mutex to ensure that voices are not triggered during readaudio or // that the synth is not changed when audio is being read - mutex sync.Mutex - synth sointu.Synth - service sointu.SynthService + mutex sync.Mutex + synth sointu.Synth + validSynth int32 + service sointu.SynthService // this iterator is a bit unconventional in the sense that it might return // hasNext false, but might still return hasNext true in future attempts if // new rows become available. @@ -72,10 +74,10 @@ func (s *Sequencer) ReadAudio(buffer []float32) (int, error) { if !gotRow { rowTimeRemaining = math.MaxInt32 } - if s.synth != nil { + if s.Enabled() { rendered, timeAdvanced, err := s.synth.Render(buffer[totalRendered*2:], rowTimeRemaining) if err != nil { - s.synth = nil + s.Disable() } totalRendered += rendered s.rowTime += timeAdvanced @@ -98,14 +100,26 @@ func (s *Sequencer) ReadAudio(buffer []float32) (int, error) { // Updates the patch of the synth func (s *Sequencer) SetPatch(patch sointu.Patch) { s.mutex.Lock() - if s.synth != nil { - s.synth.Update(patch) + var err error + if s.Enabled() { + err = s.synth.Update(patch) } else { - s.synth, _ = s.service.Compile(patch) + s.synth, err = s.service.Compile(patch) + } + if err == nil { + atomic.StoreInt32(&s.validSynth, 1) } s.mutex.Unlock() } +func (s *Sequencer) Enabled() bool { + return atomic.LoadInt32(&s.validSynth) == 1 +} + +func (s *Sequencer) Disable() { + atomic.StoreInt32(&s.validSynth, 0) +} + func (s *Sequencer) SetRowLength(rowLength int) { s.mutex.Lock() s.rowLength = rowLength diff --git a/tracker/songpanel.go b/tracker/songpanel.go index 80666b7..c70acd3 100644 --- a/tracker/songpanel.go +++ b/tracker/songpanel.go @@ -17,7 +17,7 @@ import ( func (t *Tracker) layoutSongPanel(gtx C) D { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(t.layoutSongButtons), - layout.Flexed(1, t.layoutSongOptions), + layout.Rigid(t.layoutSongOptions), ) } @@ -100,6 +100,23 @@ func (t *Tracker) layoutSongOptions(gtx C) D { in := layout.UniformInset(unit.Dp(1)) + panicBtnStyle := material.Button(t.Theme, t.PanicBtn, "Panic") + if t.sequencer.Enabled() { + panicBtnStyle.Background = transparent + panicBtnStyle.Color = t.Theme.Palette.Fg + } else { + panicBtnStyle.Background = t.Theme.Palette.Fg + panicBtnStyle.Color = t.Theme.Palette.ContrastFg + } + + for t.PanicBtn.Clicked() { + if t.sequencer.Enabled() { + t.sequencer.Disable() + } else { + t.sequencer.SetPatch(t.song.Patch) + } + } + return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx C) D { return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, @@ -158,5 +175,9 @@ func (t *Tracker) layoutSongOptions(gtx C) D { }), ) }), + layout.Rigid(func(gtx C) D { + gtx.Constraints.Min = image.Pt(0, 0) + return panicBtnStyle.Layout(gtx) + }), ) } diff --git a/tracker/tracker.go b/tracker/tracker.go index 30292e3..fa026f2 100644 --- a/tracker/tracker.go +++ b/tracker/tracker.go @@ -60,6 +60,7 @@ type Tracker struct { SongLength *NumberInput SaveSongFileBtn *widget.Clickable FileMenuBtn *widget.Clickable + PanicBtn *widget.Clickable FileMenuVisible bool ParameterSliders []*widget.Float ParameterList *layout.List @@ -550,6 +551,7 @@ func New(audioContext sointu.AudioContext, synthService sointu.SynthService) *Tr AddUnitBtn: new(widget.Clickable), DeleteUnitBtn: new(widget.Clickable), ClearUnitBtn: new(widget.Clickable), + PanicBtn: new(widget.Clickable), UnitDragList: &DragList{List: &layout.List{Axis: layout.Vertical}}, setPlaying: make(chan bool), rowJump: make(chan int),