Neale Pickett
·
2026-02-17
midi.go
1package midi
2
3import "io"
4
5// Note is a MIDI note, between 0 and 127.
6// Any value <0 means no note.
7type Note int8
8
9// Writer sends note on/off events to a MIDI device,
10// and keeps track of what's currently playing.
11type Writer struct {
12 io.Writer
13 Channel byte
14 Playing *Polyphony
15}
16
17func NewWriter(w io.Writer, channel byte) (*Writer) {
18 return &Writer{
19 w,
20 channel,
21 &Polyphony{},
22 }
23}
24
25func (w *Writer) NoteOnVelocity(note byte, velocity byte) {
26 w.Write([]byte{0x90 + w.Channel, note, velocity})
27 w.Playing.On(note)
28}
29
30func (w *Writer) NoteOn(note byte) {
31 w.NoteOnVelocity(note, 127)
32}
33
34func (w *Writer) NoteOffVelocity(note byte, velocity byte) {
35 w.Write([]byte{0x80 + w.Channel, note, velocity})
36 w.Playing.Off(note)
37}
38
39func (w *Writer) NoteOff(note byte) {
40 w.NoteOffVelocity(note, 127)
41}
42
43func (w *Writer) SetPatch(patch byte) {
44 w.Write([]byte{0xC0 + w.Channel, patch})
45}
46
47// Polyphony tracks what notes are currently playing.
48type Polyphony struct {
49 playing [2]uint64
50}
51
52func (n *Polyphony) Equal(a Polyphony) bool {
53 return (n.playing[0] == a.playing[0]) && (n.playing[1] == a.playing[1])
54}
55
56func (n *Polyphony) On(note byte) {
57 n.playing[note/64] |= 1 << (note % 64)
58}
59
60func (n *Polyphony) Off(note byte) {
61 n.playing[note/64] &= ^(1 << (note % 64))
62}