22// package for talking to midi devices in Go.
33package midi
44
5+ // #include <alsa/asoundlib.h>
56// #include <stddef.h>
67// #include <stdlib.h>
78// #include "midi_linux.h"
@@ -11,25 +12,41 @@ import "C"
1112import (
1213 "fmt"
1314 "os"
15+ "unsafe"
1416
1517 "github.com/pkg/errors"
1618)
1719
1820// Packet is a MIDI packet.
1921type Packet [3 ]byte
2022
23+ // DeviceType is a flag that says if a device is an input, an output, or duplex.
24+ type DeviceType int
25+
26+ const (
27+ DeviceInput DeviceType = iota
28+ DeviceOutput
29+ DeviceDuplex
30+ )
31+
2132// Device provides an interface for MIDI devices.
2233type Device struct {
34+ ID string
2335 Name string
2436 QueueSize int
37+ Type DeviceType
2538
2639 conn C.Midi
27- buf []byte
2840}
2941
3042// Open opens a MIDI device.
3143func (d * Device ) Open () error {
32- result := C .Midi_open (C .CString (d .Name ))
44+ var (
45+ id = C .CString (d .ID )
46+ result = C .Midi_open (id )
47+ )
48+ defer C .free (unsafe .Pointer (id ))
49+
3350 if result .error != 0 {
3451 return errors .Errorf ("error opening device %d" , result .error )
3552 }
@@ -75,6 +92,160 @@ func (d *Device) Read(buf []byte) (int, error) {
7592
7693// Write writes data to a MIDI device.
7794func (d * Device ) Write (buf []byte ) (int , error ) {
78- n , err := C .Midi_write (d .conn , C .CString (string (buf )), C .size_t (len (buf )))
95+ cs := C .CString (string (buf ))
96+ n , err := C .Midi_write (d .conn , cs , C .size_t (len (buf )))
97+ C .free (unsafe .Pointer (cs ))
7998 return int (n ), err
8099}
100+
101+ type Stream struct {
102+ Name string
103+ }
104+
105+ func Devices () ([]* Device , error ) {
106+ var card C.int = - 1
107+
108+ if rc := C .snd_card_next (& card ); rc != 0 {
109+ return nil , alsaMidiError (rc )
110+ }
111+ if card < 0 {
112+ return nil , errors .New ("no sound card found" )
113+ }
114+ devices := []* Device {}
115+
116+ for {
117+ cardDevices , err := getCardDevices (card )
118+ if err != nil {
119+ return nil , err
120+ }
121+ if rc := C .snd_card_next (& card ); rc != 0 {
122+ return nil , alsaMidiError (rc )
123+ }
124+ if card < 0 {
125+ break
126+ }
127+ devices = append (devices , cardDevices ... )
128+ }
129+ return devices , nil
130+ }
131+
132+ func getCardDevices (card C.int ) ([]* Device , error ) {
133+ var (
134+ ctl * C.snd_ctl_t
135+ name = C .CString (fmt .Sprintf ("hw:%d" , card ))
136+ )
137+ defer C .free (unsafe .Pointer (name ))
138+
139+ if rc := C .snd_ctl_open (& ctl , name , 0 ); rc != 0 {
140+ return nil , alsaMidiError (rc )
141+ }
142+ var (
143+ cardDevices = []* Device {}
144+ device C.int = - 1
145+ )
146+ for {
147+ if rc := C .snd_ctl_rawmidi_next_device (ctl , & device ); rc != 0 {
148+ return nil , alsaMidiError (rc )
149+ }
150+ if device < 0 {
151+ break
152+ }
153+ deviceDevices , err := getDeviceDevices (ctl , card , C .uint (device ))
154+ if err != nil {
155+ return nil , err
156+ }
157+ cardDevices = append (cardDevices , deviceDevices ... )
158+ }
159+ if rc := C .snd_ctl_close (ctl ); rc != 0 {
160+ return nil , alsaMidiError (rc )
161+ }
162+ return cardDevices , nil
163+ }
164+
165+ func getDeviceDevices (ctl * C.snd_ctl_t , card C.int , device C.uint ) ([]* Device , error ) {
166+ var info * C.snd_rawmidi_info_t
167+ C .snd_rawmidi_info_malloc (& info )
168+ C .snd_rawmidi_info_set_device (info , device )
169+
170+ defer C .snd_rawmidi_info_free (info )
171+
172+ // Get inputs.
173+ var subsIn C.uint
174+ C .snd_rawmidi_info_set_stream (info , C .SND_RAWMIDI_STREAM_INPUT )
175+ if rc := C .snd_ctl_rawmidi_info (ctl , info ); rc != 0 {
176+ return nil , alsaMidiError (rc )
177+ }
178+ subsIn = C .snd_rawmidi_info_get_subdevices_count (info )
179+
180+ // Get outputs.
181+ var subsOut C.uint
182+ C .snd_rawmidi_info_set_stream (info , C .SND_RAWMIDI_STREAM_OUTPUT )
183+ if rc := C .snd_ctl_rawmidi_info (ctl , info ); rc != 0 {
184+ return nil , alsaMidiError (rc )
185+ }
186+ subsOut = C .snd_rawmidi_info_get_subdevices_count (info )
187+
188+ // List subdevices.
189+ var subs C.uint
190+ if subsIn > subsOut {
191+ subs = subsIn
192+ } else {
193+ subs = subsOut
194+ }
195+ if subs == C .uint (0 ) {
196+ return nil , errors .New ("no streams" )
197+ }
198+ devices := []* Device {}
199+
200+ for sub := C .uint (0 ); sub < subs ; sub ++ {
201+ subDevice , err := getSubdevice (ctl , info , card , device , sub , subsIn , subsOut )
202+ if err != nil {
203+ return nil , err
204+ }
205+ devices = append (devices , subDevice )
206+ }
207+ return devices , nil
208+ }
209+
210+ func getSubdevice (ctl * C.snd_ctl_t , info * C.snd_rawmidi_info_t , card C.int , device , sub , subsIn , subsOut C.uint ) (* Device , error ) {
211+ if sub < subsIn {
212+ C .snd_rawmidi_info_set_stream (info , C .SND_RAWMIDI_STREAM_INPUT )
213+ } else {
214+ C .snd_rawmidi_info_set_stream (info , C .SND_RAWMIDI_STREAM_OUTPUT )
215+ }
216+ C .snd_rawmidi_info_set_subdevice (info , sub )
217+ if rc := C .snd_ctl_rawmidi_info (ctl , info ); rc != 0 {
218+ return nil , alsaMidiError (rc )
219+ }
220+ var (
221+ name = C .GoString (C .snd_rawmidi_info_get_name (info ))
222+ subName = C .GoString (C .snd_rawmidi_info_get_subdevice_name (info ))
223+ )
224+ var dt DeviceType
225+ if sub < subsIn && sub >= subsOut {
226+ dt = DeviceInput
227+ } else if sub >= subsIn && sub < subsOut {
228+ dt = DeviceOutput
229+ } else {
230+ dt = DeviceDuplex
231+ }
232+ if sub == 0 && len (subName ) > 0 && subName [0 ] == 0 {
233+ return & Device {
234+ ID : fmt .Sprintf ("hw:%d,%d" , card , device ),
235+ Name : name ,
236+ Type : dt ,
237+ }, nil
238+ }
239+ return & Device {
240+ ID : fmt .Sprintf ("hw:%d,%d,%d" , card , device , sub ),
241+ Name : subName ,
242+ Type : dt ,
243+ }, nil
244+ }
245+
246+ func alsaMidiError (code C.int ) error {
247+ if code == C .int (0 ) {
248+ return nil
249+ }
250+ return errors .New (C .GoString (C .snd_strerror (code )))
251+ }
0 commit comments