Skip to main content
Glama
emicklei

melrōse musical expression player

by emicklei
beatmaster_test.go6.7 kB
package core import ( "bytes" "testing" "time" "github.com/emicklei/melrose/notify" ) func Test_tickerDuration(t *testing.T) { d := beatTickerDuration(60.0) if got, want := d, 1*time.Second; got != want { t.Errorf("got [%v] want [%v]", got, want) } d2 := beatTickerDuration(300.0) if got, want := d2, 200*time.Millisecond; got != want { t.Errorf("got [%v] want [%v]", got, want) } d3 := beatTickerDuration(120.0) if got, want := d3, 500*time.Millisecond; got != want { t.Errorf("got [%v] want [%v]", got, want) } } func TestBeatmaster_beatsAtNextBar(t *testing.T) { ctx := PlayContext{} b := NewBeatmaster(ctx, 120.0) b.beats = 0 b.biab = 3 if got, want := b.beatsAtNextBar(), int64(0); got != want { t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want) } b.beats = 5 b.biab = 4 if got, want := b.beatsAtNextBar(), int64(8); got != want { t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want) } } func TestTrackBarTiming(t *testing.T) { ctx := PlayContext{} b := NewBeatmaster(ctx, 120.0) b.SetBPM(100.0) b.SetBIAB(3) ctx.LoopControl = b tr1 := NewTrack("1", 1) tr1.Add(NewSequenceOnTrack(On(1), MustParseSequence("c d e"))) tr2 := NewTrack("2", 1) tr1.Add(NewSequenceOnTrack(On(2), MustParseSequence("c"))) m := MultiTrack{Tracks: []HasValue{On(tr1), On(tr2)}} m.Play(ctx, time.Now()) t.Log(b.schedule.entries) _, ok := b.schedule.entries[0] if !ok { t.Fail() } _, ok = b.schedule.entries[3] if !ok { t.Fail() } } func TestBeatmaster_Getters(t *testing.T) { ctx := PlayContext{} b := NewBeatmaster(ctx, 120.0) if got, want := b.BPM(), 120.0; got != want { t.Errorf("got [%v] want [%v]", got, want) } if got, want := b.BIAB(), 4; got != want { t.Errorf("got [%v] want [%v]", got, want) } if g1, g2 := b.BeatsAndBars(); g1 != 0 || g2 != 0 { t.Errorf("got [%v,%v] want [%v,%v]", g1, g2, 0, 0) } } func TestBeatmaster_Setters(t *testing.T) { ctx := PlayContext{} b := NewBeatmaster(ctx, 120.0) b.SetBPM(140.0) b.SetBIAB(3) if got, want := b.BPM(), 140.0; got != want { t.Errorf("got [%v] want [%v]", got, want) } if got, want := b.BIAB(), 3; got != want { t.Errorf("got [%v] want [%v]", got, want) } } func TestBeatmaster_StartStop(t *testing.T) { ctx := PlayContext{} b := NewBeatmaster(ctx, 120.0) notify.ToggleDebug() b.Start() if !b.beating { t.Error("not beating") } b.Start() // should do nothing b.Stop() if b.beating { t.Error("still beating") } b.Stop() // should do nothing notify.ToggleDebug() } func TestBeatmaster_Reset(t *testing.T) { ctx := PlayContext{} b := NewBeatmaster(ctx, 120.0) b.Start() b.Reset() if !b.beating { t.Error("not beating") } } func TestBeatmaster_SetBPM_Beating(t *testing.T) { ctx := PlayContext{} b := NewBeatmaster(ctx, 120.0) b.Start() defer b.Stop() b.SetBPM(140.0) // same bpm b.SetBPM(140.0) } func TestBeatmaster_SetBIAB_Beating(t *testing.T) { ctx := PlayContext{} b := NewBeatmaster(ctx, 120.0) b.Start() defer b.Stop() b.SetBIAB(3) if got, want := b.BIAB(), 3; got != want { t.Errorf("got [%v] want [%v]", got, want) } // same biab b.SetBIAB(3) } func TestBeatmaster_Plan(t *testing.T) { ctx := PlayContext{} b := NewBeatmaster(ctx, 120.0) b.Plan(1, MustParseSequence("C")) if got, want := len(b.schedule.entries), 1; got != want { t.Errorf("got [%v] want [%v]", got, want) } // test debug notify.ToggleDebug() b.Plan(1, MustParseSequence("D")) notify.ToggleDebug() // test nil device b.context = Context(nil) b.Plan(1, MustParseSequence("E")) } func TestBeatmaster_SettingNotifier(t *testing.T) { ctx := PlayContext{} b := NewBeatmaster(ctx, 120.0) b.Start() defer b.Stop() notified := false b.SettingNotifier(func(LoopController) { notified = true }) b.SetBIAB(2) if !notified { t.Error("not notified") } } func TestBeatmaster_SettingNotifierBPM(t *testing.T) { ctx := PlayContext{} b := NewBeatmaster(ctx, 120.0) notified := false b.SettingNotifier(func(LoopController) { notified = true }) b.SetBPM(2) if !notified { t.Error("not notified") } } type mockDevice struct { played bool } func (d *mockDevice) DefaultDeviceIDs() (inputDeviceID, outputDeviceID int) { return 0, 0 } func (d *mockDevice) Command(args []string) notify.Message { return nil } func (d *mockDevice) HandleSetting(name string, values []any) error { return nil } func (d *mockDevice) Play(condition Condition, seq Sequenceable, bpm float64, beginAt time.Time) (endingAt time.Time) { d.played = true return time.Now() } func (d *mockDevice) HasInputCapability() bool { return false } func (d *mockDevice) Listen(deviceID int, who NoteListener, isStart bool) {} func (d *mockDevice) OnKey(ctx Context, deviceID int, channel int, note Note, fun HasValue) error { return nil } func (d *mockDevice) Schedule(event TimelineEvent, beginAt time.Time) {} func (d *mockDevice) ListDevices() []DeviceDescriptor { return nil } func (d *mockDevice) Reset() {} func (d *mockDevice) Close() error { return nil } func (d *mockDevice) Report() {} func TestBeatmaster_PlayPlan(t *testing.T) { dev := new(mockDevice) ctx := &PlayContext{ AudioDevice: dev, } b := NewBeatmaster(ctx, 120.0) ctx.LoopControl = b b.Plan(0, MustParseSequence("C")) b.Start() defer b.Stop() time.Sleep(1 * time.Second) if !dev.played { t.Error("device was not played") } } func TestBeatmaster_ResetDrain(t *testing.T) { ctx := PlayContext{} b := NewBeatmaster(&ctx, 120.0) go func() { b.bpmChanges <- 140.0 }() time.Sleep(10 * time.Millisecond) b.Reset() } func TestZeroBeat(t *testing.T) { z := zeroBeat{} z.Start() z.Stop() z.Reset() z.SetBPM(1) if got, want := z.BPM(), 120.0; got != want { t.Errorf("got [%v] want [%v]", got, want) } z.SetBIAB(1) if got, want := z.BIAB(), 4; got != want { t.Errorf("got [%v] want [%v]", got, want) } if g1, g2 := z.BeatsAndBars(); g1 != 0 || g2 != 0 { t.Errorf("got [%v,%v] want [%v,%v]", g1, g2, 0, 0) } z.Plan(1, MustParseSequence("C")) z.SettingNotifier(nil) } func TestBeatmaster_ScheduleEmpty(t *testing.T) { ctx := PlayContext{} b := NewBeatmaster(ctx, 120.0) b.Start() defer b.Stop() time.Sleep(2 * time.Second) if got, want := b.beats, int64(0); got != want { t.Errorf("got [%v] want [%v]", got, want) } } func TestBeatmaster_ChangeBPMOnBar(t *testing.T) { var buf bytes.Buffer notify.Console.StandardOut = &buf notify.Console.StandardError = &buf ctx := PlayContext{} b := NewBeatmaster(ctx, 120.0) b.schedule.Schedule(0, func(t time.Time) {}) b.Start() defer b.Stop() b.SetBPM(140) time.Sleep(2 * time.Second) if got, want := b.BPM(), 140.0; got != want { t.Errorf("got [%v] want [%v]", got, want) } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/emicklei/melrose'

If you have feedback or need assistance with the MCP directory API, please join our Discord server