summaryrefslogtreecommitdiff
path: root/cmd/playsmf
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/playsmf')
-rw-r--r--cmd/playsmf/main.go114
-rw-r--r--cmd/playsmf/main_test.go48
-rw-r--r--cmd/playsmf/testdata/ceottk-output.golden16
-rw-r--r--cmd/playsmf/testdata/ceottk-serial.golden.binbin0 -> 32 bytes
-rw-r--r--cmd/playsmf/testdata/ceottk.midbin0 -> 209 bytes
5 files changed, 178 insertions, 0 deletions
diff --git a/cmd/playsmf/main.go b/cmd/playsmf/main.go
new file mode 100644
index 0000000..7b55ff3
--- /dev/null
+++ b/cmd/playsmf/main.go
@@ -0,0 +1,114 @@
+package main
+
+import (
+ "container/list"
+ "flag"
+ "fmt"
+ "os"
+ "time"
+
+ "gitlab.com/gomidi/midi"
+ "gitlab.com/gomidi/midi/mid"
+ "gitlab.com/gomidi/midi/midimessage"
+ "gitlab.com/gomidi/midi/smf"
+ "go.terinstock.com/midifi/pkg/ttymididrv"
+)
+
+var (
+ drv mid.Driver
+ midiSerial string
+)
+
+func init() {
+ flag.StringVar(&midiSerial, "com", "", "The serial port of the MIDI synth")
+}
+
+func main() {
+ flag.Parse()
+ drv = ttymididrv.New(midiSerial, 38400)
+ defer drv.Close()
+
+ outs, err := drv.Outs()
+ if err != nil {
+ fmt.Printf("err: %v\n", err)
+ os.Exit(-1)
+ }
+ out := outs[0]
+
+ err = out.Open()
+ if err != nil {
+ fmt.Printf("err: %v\n", err)
+ os.Exit(-1)
+ }
+ defer out.Close()
+
+ if flag.NArg() == 0 {
+ flag.Usage()
+ os.Exit(-1)
+ }
+
+ f, err := os.Open(flag.Arg(0))
+ if err != nil {
+ fmt.Println("unable to load SMF file")
+ os.Exit(-2)
+ }
+ defer f.Close()
+
+ fmt.Printf("file: %s\n", flag.Arg(0))
+
+ mf := mid.NewReader(mid.NoLogger())
+ mf.SMFHeader = func(header smf.Header) {
+ fmt.Printf("file format: %s\n", header.Format.String())
+ fmt.Printf("number of tracks: %d\n", header.NumTracks)
+ fmt.Printf("time format: %s\n", header.TimeFormat.String())
+ fmt.Println("===========")
+ }
+ events := &list.List{}
+ mf.Msg.Each = func(pos *mid.Position, msg midi.Message) {
+ addEvent(events, Event{track: pos.Track, abs: pos.AbsoluteTicks, msg: msg})
+ }
+ mf.ReadSMF(f)
+
+ w := mid.WriteTo(out)
+
+ var lastDur time.Duration
+ for event := events.Front(); event != nil; event = event.Next() {
+ dur := *mf.TimeAt(event.Value.(Event).abs)
+ if lastDur != dur {
+ <-time.After(dur - lastDur)
+ lastDur = dur
+ }
+
+ msg := event.Value.(Event).msg
+ if midimessage.IsLive(msg) {
+ fmt.Printf("%s\n", msg.String())
+ w.Write(msg)
+ }
+ }
+}
+
+type Event struct {
+ track int16
+ abs uint64
+ msg midi.Message
+}
+
+func addEvent(l *list.List, evt Event) {
+ if l.Front() == nil {
+ l.PushBack(evt)
+ return
+ }
+
+ for cur := l.Front(); cur != nil; cur = cur.Next() {
+ peek := cur.Next()
+ if peek == nil {
+ l.InsertAfter(evt, cur)
+ return
+ }
+
+ if peek.Value.(Event).abs >= evt.abs {
+ l.InsertAfter(evt, cur)
+ return
+ }
+ }
+}
diff --git a/cmd/playsmf/main_test.go b/cmd/playsmf/main_test.go
new file mode 100644
index 0000000..68ee28f
--- /dev/null
+++ b/cmd/playsmf/main_test.go
@@ -0,0 +1,48 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "testing"
+
+ "github.com/pkg/term/termios"
+ "gotest.tools/golden"
+)
+
+func TestMain(m *testing.M) {
+ switch os.Getenv("GOTESTMODE") {
+ case "main":
+ // run the normal main function
+ main()
+ default:
+ os.Exit(m.Run())
+ }
+}
+
+func TestCLI(t *testing.T) {
+ pty, tty, err := termios.Pty()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer pty.Close()
+
+ cmd := exec.Command(os.Args[0], "-com", tty.Name(), "./testdata/ceottk.mid")
+ cmd.Env = append(os.Environ(), "GOTESTMODE=main")
+
+ output, err := cmd.Output()
+ if err != nil {
+ fmt.Printf("output: %s\n", string(output))
+ t.Fatal(err)
+ }
+
+ tty.Close()
+ b, err := ioutil.ReadAll(pty)
+ if err != nil && err.Error() != "read ptm: input/output error" {
+ t.Errorf("error reading pty: %+v", err)
+ }
+
+ golden.AssertBytes(t, b, "ceottk-serial.golden.bin")
+ golden.Assert(t, string(output), "ceottk-output.golden")
+}
diff --git a/cmd/playsmf/testdata/ceottk-output.golden b/cmd/playsmf/testdata/ceottk-output.golden
new file mode 100644
index 0000000..4cac21b
--- /dev/null
+++ b/cmd/playsmf/testdata/ceottk-output.golden
@@ -0,0 +1,16 @@
+file: ./testdata/ceottk.mid
+file format: SMF1 (multitrack)
+number of tracks: 2
+time format: 480 MetricTicks
+===========
+channel.NoteOn channel 1 key 79 velocity 81
+channel.ProgramChange channel 1 program 19
+channel.NoteOn channel 1 key 81 velocity 81
+channel.NoteOff channel 1 key 79
+channel.NoteOn channel 1 key 77 velocity 81
+channel.NoteOff channel 1 key 81
+channel.NoteOn channel 1 key 65 velocity 81
+channel.NoteOff channel 1 key 77
+channel.NoteOn channel 1 key 72 velocity 81
+channel.NoteOff channel 1 key 65
+channel.NoteOff channel 1 key 72
diff --git a/cmd/playsmf/testdata/ceottk-serial.golden.bin b/cmd/playsmf/testdata/ceottk-serial.golden.bin
new file mode 100644
index 0000000..a505536
--- /dev/null
+++ b/cmd/playsmf/testdata/ceottk-serial.golden.bin
Binary files differ
diff --git a/cmd/playsmf/testdata/ceottk.mid b/cmd/playsmf/testdata/ceottk.mid
new file mode 100644
index 0000000..4d0877e
--- /dev/null
+++ b/cmd/playsmf/testdata/ceottk.mid
Binary files differ