summaryrefslogtreecommitdiff
path: root/cmd/playsmf/main.go
blob: 7b55ff3e6f0b61bd2a443f26749b3a17f75531bf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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
		}
	}
}