func (b *Beatmaster) Stop() {
	if !b.beating {
		return
	}
	b.beating = false
	b.ticker.Stop()
	b.done <- true
	if IsDebug() {
		notify.Debugf("core.beatmaster: stopped")
	}
}
Stop will stop the beats. Any Loops will continue to run.
package core
import (
	"math"
	"time"
	"github.com/emicklei/melrose/notify"
)
type Beatmaster struct {
	context         Context
	beating         bool
	bpmChanges      chan float64
	ticker          *time.Ticker
	done            chan bool
	schedule        *BeatSchedule
	beats           int64    
	biab            int64    
	bpm             float64  
	settingNotifier func(LoopController)
}
func NewBeatmaster(ctx Context, bpm float64) *Beatmaster {
	return &Beatmaster{
		context:    ctx,
		beating:    false,
		done:       make(chan bool),
		bpmChanges: make(chan float64),
		schedule:   NewBeatSchedule(),
		beats:      0,
		biab:       4,
		bpm:        bpm}
}
func (b *Beatmaster) Reset() {
	b.Stop()
	go func() {
		select {
		case <-b.bpmChanges:
		default:
		}
	}()
	b.schedule.Reset()
	b.Start()
}
func (b *Beatmaster) BPM() float64 {
	return b.bpm
}
func (b *Beatmaster) BIAB() int {
	return int(b.biab)
}
func (b *Beatmaster) BeatsAndBars() (int64, int64) {
	return b.beats, b.beats / b.biab
}
func (b *Beatmaster) SettingNotifier(handler func(LoopController)) {
	b.settingNotifier = handler
}
func (b *Beatmaster) Plan(bars int64, seq Sequenceable) {
	atBeats := b.beatsAtNextBar() + (b.biab * bars)
	if IsDebug() {
		notify.Debugf("beat.schedule at beats: %d put: %s bars: %.2f", atBeats, Storex(seq), seq.S().Bars(int(b.biab)))
	}
	b.schedule.Schedule(atBeats, func(when time.Time) {
		b.context.Device().Play(NoCondition, seq, b.bpm, when)
	})
}
func (b *Beatmaster) beatsAtNextBar() int64 {
	if b.beats%b.biab == 0 {
		return b.beats
	}
	return (b.beats/b.biab + 1) * b.biab
}
func (b *Beatmaster) SetBPM(bpm float64) {
	if !b.beating {
		b.bpm = bpm
		b.notifySettingChanged()
		return
	}
	if b.bpm == bpm {
		return
	}
	if b.schedule.IsEmpty() {
		b.bpm = bpm
		b.notifySettingChanged()
		return
	}
	go func() { b.bpmChanges <- bpm }()
}
func (b *Beatmaster) SetBIAB(biab int) {
	if !b.beating {
		b.biab = int64(biab)
		return
	}
	if b.biab == int64(biab) {
		return
	}
	b.biab = int64(biab)
	b.notifySettingChanged()
}
func (b *Beatmaster) notifySettingChanged() {
	if b.settingNotifier == nil {
		return
	}
	b.settingNotifier(b)
}
func (b *Beatmaster) Start() {
	if b.beating {
		return
	}
	b.notifySettingChanged()
	b.beats = 0
	b.ticker = time.NewTicker(beatTickerDuration(b.bpm))
	b.beating = true
	go func() {
		if IsDebug() {
			notify.Debugf("core.beatmaster: started bpm=%v tick=%v", b.bpm, beatTickerDuration(b.bpm))
		}
		for {
			if b.beats%b.biab == 0 {
				select {
				case <-b.done:
					return
				case bpm := <-b.bpmChanges:
					if IsDebug() {
						notify.Debugf("core.beatmaster: changed bpm=%v tick=%v", bpm, beatTickerDuration(bpm))
					}
					b.bpm = bpm
					b.notifySettingChanged()
					b.ticker.Stop()
					b.ticker = time.NewTicker(beatTickerDuration(bpm))
				default:
				}
			}
			select {
			case <-b.done:
				return
			case now := <-b.ticker.C:
				if b.schedule.IsEmpty() {
					b.beats = 0
				} else {
					actions := b.schedule.Unschedule(b.beats)
					for _, each := range actions {
						each(now)
					}
					b.beats++
				}
			}
		}
	}()
}
func beatTickerDuration(bpm float64) time.Duration {
	return time.Duration(int(math.Round(float64(60*1000)/bpm))) * time.Millisecond
}