Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
406 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,355 @@ | ||
package rtmidi | ||
|
||
/* | ||
#cgo CXXFLAGS: -g | ||
#cgo LDFLAGS: -g | ||
#cgo linux CXXFLAGS: -D__LINUX_ALSA__ | ||
#cgo linux LDFLAGS: -lasound -pthread | ||
#cgo windows CXXFLAGS: -D__WINDOWS_MM__ | ||
#cgo windows LDFLAGS: -luuid -lksuser -lwinmm -lole32 | ||
#cgo darwin CXXFLAGS: -D__MACOSX_CORE__ | ||
#cgo darwin LDFLAGS: -framework CoreServices -framework CoreAudio -framework CoreMIDI -framework CoreFoundation | ||
#include <stdlib.h> | ||
#include <stdint.h> | ||
#include "rtmidi_stub.h" | ||
extern void goMIDIInCallback(double ts, unsigned char *msg, size_t msgsz, void *arg); | ||
static inline void midiInCallback(double ts, const unsigned char *msg, size_t msgsz, void *arg) { | ||
goMIDIInCallback(ts, (unsigned char*) msg, msgsz, arg); | ||
} | ||
static inline void cgoSetCallback(RtMidiPtr in, int cb_id) { | ||
rtmidi_in_set_callback(in, midiInCallback, (void*)(uintptr_t) cb_id); | ||
} | ||
*/ | ||
import "C" | ||
import ( | ||
"errors" | ||
"sync" | ||
"unsafe" | ||
) | ||
|
||
// API is an enumeration of possible MIDI API specifiers. | ||
type API C.enum_RtMidiApi | ||
|
||
const ( | ||
// APIUnspecified searches for a working compiled API. | ||
APIUnspecified API = C.RT_MIDI_API_UNSPECIFIED | ||
// APIMacOSXCore uses Macintosh OS-X Core Midi API. | ||
APIMacOSXCore = C.RT_MIDI_API_MACOSX_CORE | ||
// APILinuxALSA uses the Advanced Linux Sound Architecture API. | ||
APILinuxALSA = C.RT_MIDI_API_LINUX_ALSA | ||
// APIUnixJack uses the JACK Low-Latency MIDI Server API. | ||
APIUnixJack = C.RT_MIDI_API_UNIX_JACK | ||
// APIWindowsMM uses the Microsoft Multimedia MIDI API. | ||
APIWindowsMM = C.RT_MIDI_API_WINDOWS_MM | ||
// APIDummy is a compilable but non-functional API. | ||
APIDummy = C.RT_MIDI_API_RTMIDI_DUMMY | ||
) | ||
|
||
func (api API) String() string { | ||
switch api { | ||
case APIUnspecified: | ||
return "unspecified" | ||
case APILinuxALSA: | ||
return "alsa" | ||
case APIUnixJack: | ||
return "jack" | ||
case APIMacOSXCore: | ||
return "coreaudio" | ||
case APIWindowsMM: | ||
return "winmm" | ||
case APIDummy: | ||
return "dummy" | ||
} | ||
return "?" | ||
} | ||
|
||
// CompiledAPI determines the available compiled MIDI APIs. | ||
func CompiledAPI() (apis []API) { | ||
n := C.rtmidi_get_compiled_api(nil) | ||
capis := make([]C.enum_RtMidiApi, n, n) | ||
C.rtmidi_get_compiled_api(&capis[0]) | ||
for _, capi := range capis { | ||
apis = append(apis, API(capi)) | ||
} | ||
return apis | ||
} | ||
|
||
// MIDI interface provides a common, platform-independent API for realtime MIDI | ||
// device enumeration and handling MIDI ports. | ||
type MIDI interface { | ||
OpenPort(port int, name string) error | ||
OpenVirtualPort(name string) error | ||
Close() error | ||
PortCount() (int, error) | ||
PortName(port int) (string, error) | ||
} | ||
|
||
// MIDIIn interface provides a common, platform-independent API for realtime | ||
// MIDI input. It allows access to a single MIDI input port. Incoming MIDI | ||
// messages are either saved to a queue for retrieval using the Message() | ||
// method or immediately passed to a user-specified callback function. Create | ||
// multiple instances of this class to connect to more than one MIDI device at | ||
// the same time. | ||
type MIDIIn interface { | ||
MIDI | ||
API() (API, error) | ||
IgnoreTypes(midiSysex bool, midiTime bool, midiSense bool) error | ||
SetCallback(func(MIDIIn, []byte, float64)) error | ||
CancelCallback() error | ||
Message() ([]byte, float64, error) | ||
Destroy() | ||
} | ||
|
||
// MIDIOut interface provides a common, platform-independent API for MIDI | ||
// output. It allows one to probe available MIDI output ports, to connect to | ||
// one such port, and to send MIDI bytes immediately over the connection. | ||
// Create multiple instances of this class to connect to more than one MIDI | ||
// device at the same time. | ||
type MIDIOut interface { | ||
MIDI | ||
API() (API, error) | ||
SendMessage([]byte) error | ||
Destroy() | ||
} | ||
|
||
type midi struct { | ||
midi C.RtMidiPtr | ||
} | ||
|
||
func (m *midi) OpenPort(port int, name string) error { | ||
p := C.CString(name) | ||
defer C.free(unsafe.Pointer(p)) | ||
C.rtmidi_open_port(m.midi, C.uint(port), p) | ||
if !m.midi.ok { | ||
return errors.New(C.GoString(m.midi.msg)) | ||
} | ||
return nil | ||
} | ||
|
||
func (m *midi) OpenVirtualPort(name string) error { | ||
p := C.CString(name) | ||
defer C.free(unsafe.Pointer(p)) | ||
C.rtmidi_open_virtual_port(m.midi, p) | ||
if !m.midi.ok { | ||
return errors.New(C.GoString(m.midi.msg)) | ||
} | ||
return nil | ||
} | ||
|
||
func (m *midi) PortName(port int) (string, error) { | ||
p := C.rtmidi_get_port_name(m.midi, C.uint(port)) | ||
if !m.midi.ok { | ||
return "", errors.New(C.GoString(m.midi.msg)) | ||
} | ||
defer C.free(unsafe.Pointer(p)) | ||
return C.GoString(p), nil | ||
} | ||
|
||
func (m *midi) PortCount() (int, error) { | ||
n := C.rtmidi_get_port_count(m.midi) | ||
if !m.midi.ok { | ||
return 0, errors.New(C.GoString(m.midi.msg)) | ||
} | ||
return int(n), nil | ||
} | ||
|
||
func (m *midi) Close() error { | ||
C.rtmidi_close_port(C.RtMidiPtr(m.midi)) | ||
if !m.midi.ok { | ||
return errors.New(C.GoString(m.midi.msg)) | ||
} | ||
return nil | ||
} | ||
|
||
type midiIn struct { | ||
midi | ||
in C.RtMidiInPtr | ||
cb func(MIDIIn, []byte, float64) | ||
} | ||
|
||
type midiOut struct { | ||
midi | ||
out C.RtMidiOutPtr | ||
} | ||
|
||
// NewMIDIInDefault opens a default MIDIIn port. | ||
func NewMIDIInDefault() (MIDIIn, error) { | ||
in := C.rtmidi_in_create_default() | ||
if !in.ok { | ||
defer C.rtmidi_in_free(in) | ||
return nil, errors.New(C.GoString(in.msg)) | ||
} | ||
return &midiIn{in: in, midi: midi{midi: C.RtMidiPtr(in)}}, nil | ||
} | ||
|
||
// NewMIDIIn opens a single MIDIIn port using the given API. One can provide a | ||
// custom port name and a desired queue size for the incomming MIDI messages. | ||
func NewMIDIIn(api API, name string, queueSize int) (MIDIIn, error) { | ||
p := C.CString(name) | ||
defer C.free(unsafe.Pointer(p)) | ||
in := C.rtmidi_in_create(C.enum_RtMidiApi(api), p, C.uint(queueSize)) | ||
if !in.ok { | ||
defer C.rtmidi_in_free(in) | ||
return nil, errors.New(C.GoString(in.msg)) | ||
} | ||
return &midiIn{in: in, midi: midi{midi: C.RtMidiPtr(in)}}, nil | ||
} | ||
|
||
func (m *midiIn) API() (API, error) { | ||
api := C.rtmidi_in_get_current_api(m.in) | ||
if !m.in.ok { | ||
return APIUnspecified, errors.New(C.GoString(m.in.msg)) | ||
} | ||
return API(api), nil | ||
} | ||
|
||
func (m *midiIn) Close() error { | ||
unregisterMIDIIn(m) | ||
if err := m.midi.Close(); err != nil { | ||
return err | ||
} | ||
C.rtmidi_in_free(m.in) | ||
return nil | ||
} | ||
|
||
func (m *midiIn) IgnoreTypes(midiSysex bool, midiTime bool, midiSense bool) error { | ||
C.rtmidi_in_ignore_types(m.in, C._Bool(midiSysex), C._Bool(midiTime), C._Bool(midiSense)) | ||
if !m.in.ok { | ||
return errors.New(C.GoString(m.in.msg)) | ||
} | ||
return nil | ||
} | ||
|
||
var ( | ||
mu sync.Mutex | ||
inputs = map[int]*midiIn{} | ||
) | ||
|
||
func registerMIDIIn(m *midiIn) int { | ||
mu.Lock() | ||
defer mu.Unlock() | ||
for i := 0; ; i++ { | ||
if _, ok := inputs[i]; !ok { | ||
inputs[i] = m | ||
return i | ||
} | ||
} | ||
} | ||
|
||
func unregisterMIDIIn(m *midiIn) { | ||
mu.Lock() | ||
defer mu.Unlock() | ||
for i := 0; i < len(inputs); i++ { | ||
if inputs[i] == m { | ||
delete(inputs, i) | ||
return | ||
} | ||
} | ||
} | ||
|
||
func findMIDIIn(k int) *midiIn { | ||
mu.Lock() | ||
defer mu.Unlock() | ||
return inputs[k] | ||
} | ||
|
||
//export goMIDIInCallback | ||
func goMIDIInCallback(ts C.double, msg *C.uchar, msgsz C.size_t, arg unsafe.Pointer) { | ||
k := int(uintptr(arg)) | ||
m := findMIDIIn(k) | ||
m.cb(m, C.GoBytes(unsafe.Pointer(msg), C.int(msgsz)), float64(ts)) | ||
} | ||
|
||
func (m *midiIn) SetCallback(cb func(MIDIIn, []byte, float64)) error { | ||
k := registerMIDIIn(m) | ||
m.cb = cb | ||
C.cgoSetCallback(m.in, C.int(k)) | ||
if !m.in.ok { | ||
return errors.New(C.GoString(m.in.msg)) | ||
} | ||
return nil | ||
} | ||
|
||
func (m *midiIn) CancelCallback() error { | ||
unregisterMIDIIn(m) | ||
C.rtmidi_in_cancel_callback(m.in) | ||
if !m.in.ok { | ||
return errors.New(C.GoString(m.in.msg)) | ||
} | ||
return nil | ||
} | ||
|
||
func (m *midiIn) Message() ([]byte, float64, error) { | ||
msg := make([]C.uchar, 64*1024, 64*1024) | ||
sz := C.size_t(len(msg)) | ||
r := C.rtmidi_in_get_message(m.in, &msg[0], &sz) | ||
if !m.in.ok { | ||
return nil, 0, errors.New(C.GoString(m.in.msg)) | ||
} | ||
b := make([]byte, int(sz), int(sz)) | ||
for i, c := range msg[:sz] { | ||
b[i] = byte(c) | ||
} | ||
return b, float64(r), nil | ||
} | ||
|
||
func (m *midiIn) Destroy() { | ||
C.rtmidi_in_free(m.in) | ||
} | ||
|
||
// NewMIDIOutDefault opens a default MIDIOut port. | ||
func NewMIDIOutDefault() (MIDIOut, error) { | ||
out := C.rtmidi_out_create_default() | ||
if !out.ok { | ||
defer C.rtmidi_out_free(out) | ||
return nil, errors.New(C.GoString(out.msg)) | ||
} | ||
return &midiOut{out: out, midi: midi{midi: C.RtMidiPtr(out)}}, nil | ||
} | ||
|
||
// NewMIDIOut opens a single MIDIIn port using the given API with the given port name. | ||
func NewMIDIOut(api API, name string) (MIDIOut, error) { | ||
p := C.CString(name) | ||
defer C.free(unsafe.Pointer(p)) | ||
out := C.rtmidi_out_create(C.enum_RtMidiApi(api), p) | ||
if !out.ok { | ||
defer C.rtmidi_out_free(out) | ||
return nil, errors.New(C.GoString(out.msg)) | ||
} | ||
return &midiOut{out: out, midi: midi{midi: C.RtMidiPtr(out)}}, nil | ||
} | ||
|
||
func (m *midiOut) API() (API, error) { | ||
api := C.rtmidi_out_get_current_api(m.out) | ||
if !m.out.ok { | ||
return APIUnspecified, errors.New(C.GoString(m.out.msg)) | ||
} | ||
return API(api), nil | ||
} | ||
|
||
func (m *midiOut) Close() error { | ||
if err := m.midi.Close(); err != nil { | ||
return err | ||
} | ||
C.rtmidi_out_free(m.out) | ||
return nil | ||
} | ||
|
||
func (m *midiOut) SendMessage(b []byte) error { | ||
p := C.CBytes(b) | ||
defer C.free(unsafe.Pointer(p)) | ||
C.rtmidi_out_send_message(m.out, (*C.uchar)(p), C.int(len(b))) | ||
if !m.out.ok { | ||
return errors.New(C.GoString(m.out.msg)) | ||
} | ||
return nil | ||
} | ||
|
||
func (m *midiOut) Destroy() { | ||
C.rtmidi_out_free(m.out) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
#include "../../../RtMidi.h" | ||
|
||
#include "../../../RtMidi.cpp" | ||
#include "../../../rtmidi_c.cpp" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
#include "../../../rtmidi_c.h" |
Oops, something went wrong.