-
Notifications
You must be signed in to change notification settings - Fork 0
/
midilistener.go
116 lines (96 loc) · 2.69 KB
/
midilistener.go
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
115
116
package jlsampler
// #cgo pkg-config: alsa
// #include <alsa/asoundlib.h>
import "C"
import (
"encoding/binary"
"errors"
"fmt"
"os/exec"
)
// ----------------------------------------------------------------------------
type MidiListener struct {
sampler *Sampler
handle *C.snd_seq_t // Handle for midi sequencer.
}
/* NewMidiListener
* name : The name of the device.
* midiPort : An incoming midi port to connect to.
*/
func NewMidiListener(
sampler *Sampler, name, midiIn string) (*MidiListener, error) {
ml := new(MidiListener)
ml.sampler = sampler
// Open the midi device.
openIn := C.int(C.SND_SEQ_OPEN_INPUT)
status := int(C.snd_seq_open(&ml.handle, C.CString("default"), openIn, 0))
if status < 0 {
return nil, errors.New("Failed to open midi device.")
}
clientNum := int(C.snd_seq_client_id(ml.handle))
// Give the client a name.
status = int(C.snd_seq_set_client_name(ml.handle, C.CString(name)))
if status < 0 {
return nil, errors.New("Failed to set client name.")
}
// Create a port.
caps := C.uint(C.SND_SEQ_PORT_CAP_WRITE | C.SND_SEQ_PORT_CAP_SUBS_WRITE)
type_ := C.uint(C.SND_SEQ_PORT_TYPE_MIDI_GM)
portNum := int(
C.snd_seq_create_simple_port(ml.handle, C.CString(name), caps, type_))
if portNum < 0 {
return nil, errors.New("Failed to create port.")
}
// Call aconnect to connect midi port.
if len(midiIn) != 0 {
go func() {
midiPort := fmt.Sprintf("%d:%d", clientNum, portNum)
cmd := "aconnect " + midiIn + " " + midiPort
err := exec.Command("sh", "-c", cmd).Run()
if err != nil {
Println("Failed in call to aconnect:", cmd, err)
}
}()
}
return ml, nil
}
func noteAndValue(ev *C.snd_seq_event_t) (int8, float64) {
note := int8(ev.data[1])
value := float64(ev.data[2]) / 127.0
return note, value
}
/* Run
* Read incoming midi events and send them to the sampler.
*/
func (ml *MidiListener) Run() {
var ev *C.snd_seq_event_t
var status int
var note int8
var value float64
for {
status = int(C.snd_seq_event_input(ml.handle, &ev))
if status < 0 {
Println("Error reading midi event. Ignoring.")
continue
}
switch ev._type {
case C.SND_SEQ_EVENT_NOTEON:
note, value = noteAndValue(ev)
if ev.data[2] != 0 {
ml.sampler.NoteOnEvent(note, value)
} else {
ml.sampler.NoteOffEvent(note, value)
}
case C.SND_SEQ_EVENT_NOTEOFF:
note, value = noteAndValue(ev)
ml.sampler.NoteOffEvent(note, value)
case C.SND_SEQ_EVENT_CONTROLLER:
ml.sampler.ControllerEvent(
int32(binary.LittleEndian.Uint32(ev.data[4:8])),
float64(binary.LittleEndian.Uint32(ev.data[8:12]))/127)
case C.SND_SEQ_EVENT_PITCHBEND:
ml.sampler.PitchBendEvent(
float64(binary.LittleEndian.Uint32(ev.data[8:12])) / 8192.0)
}
}
}