concertina

Elecronic concertina
git clone https://git.woozle.org/neale/concertina.git

concertina / internal / midi
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}