ffi-coremidi 0.2.3 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/coremidi/{map.rb → api.rb} +103 -13
- data/lib/coremidi/destination.rb +10 -29
- data/lib/coremidi/device.rb +8 -3
- data/lib/coremidi/endpoint.rb +28 -9
- data/lib/coremidi/entity.rb +26 -30
- data/lib/coremidi/source.rb +10 -24
- data/lib/coremidi/type_conversion.rb +21 -0
- data/lib/coremidi.rb +3 -2
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4191e1b978dd6f7b928a1c1822850ded9034c0ae
|
4
|
+
data.tar.gz: 780525a088dde5901dd1c9326d483ebe356e9c59
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 87ca289e7f5edb44619c6503c611828ad60c937e48952c0c99eb65665c75f869079cb1313cf25794fe47db0c944cd8849b764dada1e5c3df1ee82ff448ac9299
|
7
|
+
data.tar.gz: 9c48b781b0b7ab4df324ec15c8f3d18c2131d4776849fcd98c75233d5f59297074a7485b2ef47c3d3d551d3886aa706a18586c87822f5b0556f38864e72270cf
|
@@ -1,12 +1,13 @@
|
|
1
1
|
module CoreMIDI
|
2
2
|
|
3
3
|
# Coremidi C binding
|
4
|
-
module
|
4
|
+
module API
|
5
5
|
|
6
6
|
extend FFI::Library
|
7
7
|
ffi_lib '/System/Library/Frameworks/CoreMIDI.framework/Versions/Current/CoreMIDI'
|
8
8
|
|
9
|
-
|
9
|
+
# if osx is 10.6 or higher, there are some differences with 32 vs 64 bit handling
|
10
|
+
X86_64 = `uname -r`.scan(/\d*\.\d*/).first.to_f >= 10.6
|
10
11
|
|
11
12
|
typedef :pointer, :CFStringRef
|
12
13
|
typedef :int32, :ItemCount
|
@@ -46,41 +47,118 @@ module CoreMIDI
|
|
46
47
|
|
47
48
|
end
|
48
49
|
|
50
|
+
def self.get_callback(*args, &block)
|
51
|
+
FFI::Function.new(:void, *args, &block)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Pack the given data into a coremidi MIDI packet (used by Destination)
|
55
|
+
def self.get_midi_packet(data)
|
56
|
+
format = "C" * data.size
|
57
|
+
packed_data = data.pack(format)
|
58
|
+
char_size = FFI.type_size(:char) * data.size
|
59
|
+
bytes = FFI::MemoryPointer.new(char_size)
|
60
|
+
bytes.write_string(packed_data)
|
61
|
+
bytes
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.create_midi_client(resource_id, name)
|
65
|
+
client_name = API::CF.CFStringCreateWithCString(nil, "Client #{resource_id} #{name}", 0)
|
66
|
+
client_pointer = FFI::MemoryPointer.new(:pointer)
|
67
|
+
error = API.MIDIClientCreate(client_name, nil, nil, client_pointer)
|
68
|
+
client = client_pointer.read_pointer
|
69
|
+
{
|
70
|
+
:error => error,
|
71
|
+
:resource => client
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.create_midi_input_port(client, resource_id, name, callback)
|
76
|
+
port_name = API::CF.CFStringCreateWithCString(nil, "Port #{resource_id}: #{name}", 0)
|
77
|
+
handle_ptr = FFI::MemoryPointer.new(:pointer)
|
78
|
+
error = API.MIDIInputPortCreate(client, port_name, callback, nil, handle_ptr)
|
79
|
+
handle = handle_ptr.read_pointer
|
80
|
+
{
|
81
|
+
:error => error,
|
82
|
+
:handle => handle
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.create_midi_output_port(client, resource_id, name)
|
87
|
+
port_name = CF.CFStringCreateWithCString(nil, "Port #{resource_id}: #{name}", 0)
|
88
|
+
port_pointer = FFI::MemoryPointer.new(:pointer)
|
89
|
+
error = API.MIDIOutputPortCreate(client, port_name, port_pointer)
|
90
|
+
handle = port_pointer.read_pointer
|
91
|
+
{
|
92
|
+
:error => error,
|
93
|
+
:handle => handle
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
# (used by Destination)
|
98
|
+
def self.get_midi_packet_list(bytes, size)
|
99
|
+
packet_list = FFI::MemoryPointer.new(256)
|
100
|
+
packet_ptr = API.MIDIPacketListInit(packet_list)
|
101
|
+
packet_ptr = if X86_64
|
102
|
+
API.MIDIPacketListAdd(packet_list, 256, packet_ptr, 0, size, bytes)
|
103
|
+
else
|
104
|
+
# Pass in two 32-bit 0s for the 64 bit time
|
105
|
+
API.MIDIPacketListAdd(packet_list, 256, packet_ptr, 0, 0, size, bytes)
|
106
|
+
end
|
107
|
+
packet_list
|
108
|
+
end
|
109
|
+
|
110
|
+
# @param [FFI::Pointer] resource A pointer to an underlying struct
|
111
|
+
# @param [String, Symbol] name The property name to get
|
112
|
+
# @return [Fixnum]
|
113
|
+
def self.get_int(resource, name)
|
114
|
+
property = API::CF.CFStringCreateWithCString(nil, name.to_s, 0)
|
115
|
+
value = FFI::MemoryPointer.new(:pointer, 32)
|
116
|
+
API::MIDIObjectGetIntegerProperty(resource, property, value)
|
117
|
+
value.read_int
|
118
|
+
end
|
119
|
+
|
49
120
|
# @param [FFI::Pointer] resource A pointer to an underlying struct
|
50
121
|
# @param [String, Symbol] name The property name to get
|
51
122
|
# @return [String]
|
52
123
|
def self.get_string(resource, name)
|
53
|
-
property =
|
124
|
+
property = CF.CFStringCreateWithCString(nil, name.to_s, 0)
|
54
125
|
begin
|
55
126
|
pointer = FFI::MemoryPointer.new(:pointer)
|
56
|
-
|
127
|
+
MIDIObjectGetStringProperty(resource, property, pointer)
|
57
128
|
string = pointer.read_pointer
|
58
|
-
length =
|
129
|
+
length = CF.CFStringGetMaximumSizeForEncoding(CF.CFStringGetLength(string), :kCFStringEncodingUTF8)
|
59
130
|
|
60
131
|
bytes = FFI::MemoryPointer.new(length + 1)
|
61
132
|
|
62
|
-
if
|
133
|
+
if CF.CFStringGetCString(string, bytes, length + 1, :kCFStringEncodingUTF8)
|
63
134
|
bytes.read_string.force_encoding("utf-8")
|
64
135
|
end
|
65
136
|
ensure
|
66
|
-
|
67
|
-
|
137
|
+
CF.CFRelease(string) unless string.nil? || string.null?
|
138
|
+
CF.CFRelease(property) unless property.null?
|
68
139
|
end
|
69
140
|
end
|
70
141
|
|
142
|
+
# Called when the system has one or more incoming MIDI messages to deliver to your app.
|
143
|
+
# typedef void (*MIDIReadProc) (const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon);
|
71
144
|
callback :MIDIReadProc, [MIDIPacketList.by_ref, :pointer, :pointer], :pointer
|
72
145
|
|
146
|
+
# OSStatus MIDIClientCreate(CFStringRef name, MIDINotifyProc notifyProc, void *notifyRefCon, MIDIClientRef *outClient);
|
73
147
|
attach_function :MIDIClientCreate, [:pointer, :pointer, :pointer, :pointer], :int
|
74
148
|
|
149
|
+
# OSStatus MIDIClientDispose(MIDIClientRef client);
|
75
150
|
attach_function :MIDIClientDispose, [:pointer], :int
|
76
151
|
|
77
|
-
# MIDIEntityRef MIDIDeviceGetEntity
|
152
|
+
# MIDIEntityRef MIDIDeviceGetEntity(MIDIDeviceRef device, ItemCount entityIndex0);
|
78
153
|
attach_function :MIDIDeviceGetEntity, [:MIDIDeviceRef, :ItemCount], :MIDIEntityRef
|
79
154
|
|
155
|
+
# MIDIEndpointRef MIDIGetDestination(ItemCount destIndex0);
|
80
156
|
attach_function :MIDIGetNumberOfDestinations, [], :ItemCount
|
81
157
|
|
158
|
+
# ItemCount MIDIGetNumberOfDevices();
|
82
159
|
attach_function :MIDIGetNumberOfDevices, [], :ItemCount
|
83
160
|
|
161
|
+
# MIDIEndpointRef MIDIEntityGetDestination(MIDIEntityRef entity, ItemCount destIndex0);
|
84
162
|
attach_function :MIDIGetDestination, [:int], :pointer
|
85
163
|
|
86
164
|
#extern OSStatus MIDIEndpointDispose( MIDIEndpointRef endpt );
|
@@ -98,19 +176,23 @@ module CoreMIDI
|
|
98
176
|
# MIDIEndpointRef MIDIEntityGetSource (MIDIEntityRef entity, ItemCount sourceIndex0);
|
99
177
|
attach_function :MIDIEntityGetSource, [:MIDIEntityRef, :ItemCount], :MIDIEndpointRef
|
100
178
|
|
179
|
+
# MIDIDeviceRef MIDIGetDevice(ItemCount deviceIndex0);
|
101
180
|
attach_function :MIDIGetDevice, [:ItemCount], :MIDIDeviceRef
|
102
181
|
|
103
|
-
# extern OSStatus MIDIInputPortCreate( MIDIClientRef client, CFStringRef portName,
|
182
|
+
# extern OSStatus MIDIInputPortCreate( MIDIClientRef client, CFStringRef portName,
|
183
|
+
# MIDIReadProc readProc, void * refCon, MIDIPortRef * outPort );
|
104
184
|
attach_function :MIDIInputPortCreate, [:MIDIClientRef, :CFStringRef, :MIDIReadProc, :pointer, :MIDIPortRef], :OSStatus
|
105
185
|
|
106
186
|
# extern OSStatus MIDIObjectGetIntegerProperty( MIDIObjectRef obj, CFStringRef propertyID, SInt32 * outValue );
|
107
187
|
attach_function :MIDIObjectGetIntegerProperty, [:MIDIObjectRef, :CFStringRef, :pointer], :OSStatus
|
188
|
+
|
108
189
|
# OSStatus MIDIObjectGetStringProperty (MIDIObjectRef obj, CFStringRef propertyID, CFStringRef *str);
|
109
190
|
attach_function :MIDIObjectGetStringProperty, [:MIDIObjectRef, :CFStringRef, :pointer], :OSStatus
|
110
|
-
|
191
|
+
|
111
192
|
# extern OSStatus MIDIOutputPortCreate( MIDIClientRef client, CFStringRef portName, MIDIPortRef * outPort );
|
112
193
|
attach_function :MIDIOutputPortCreate, [:MIDIClientRef, :CFStringRef, :pointer], :int
|
113
194
|
|
195
|
+
# (MIDIPacket*) MIDIPacketListInit(MIDIPacketList *pktlist);
|
114
196
|
attach_function :MIDIPacketListInit, [:pointer], :pointer
|
115
197
|
|
116
198
|
#extern OSStatus MIDIPortConnectSource( MIDIPortRef port, MIDIEndpointRef source, void * connRefCon )
|
@@ -125,12 +207,15 @@ module CoreMIDI
|
|
125
207
|
#extern OSStatus MIDISend(MIDIPortRef port,MIDIEndpointRef dest,const MIDIPacketList *pktlist);
|
126
208
|
attach_function :MIDISend, [:MIDIPortRef, :MIDIEndpointRef, :pointer], :int
|
127
209
|
|
210
|
+
#OSStatus MIDISendSysex(MIDISysexSendRequest *request);
|
128
211
|
attach_function :MIDISendSysex, [:pointer], :int
|
129
212
|
|
130
|
-
|
213
|
+
# extern MIDIPacket * MIDIPacketListAdd( MIDIPacketList * pktlist, ByteCount listSize,
|
214
|
+
# MIDIPacket * curPacket, MIDITimeStamp time,
|
215
|
+
# ByteCount nData, const Byte * data)
|
216
|
+
if X86_64
|
131
217
|
attach_function :MIDIPacketListAdd, [:pointer, :int, :pointer, :int, :int, :pointer], :pointer
|
132
218
|
else
|
133
|
-
# extern MIDIPacket * MIDIPacketListAdd( MIDIPacketList * pktlist, ByteCount listSize, MIDIPacket * curPacket, MIDITimeStamp time, ByteCount nData, const Byte * data)
|
134
219
|
attach_function :MIDIPacketListAdd, [:pointer, :int, :pointer, :int, :int, :int, :pointer], :pointer
|
135
220
|
end
|
136
221
|
|
@@ -148,12 +233,16 @@ module CoreMIDI
|
|
148
233
|
# CString* CFStringGetCStringPtr(CFString*, encoding)
|
149
234
|
attach_function :CFStringGetCStringPtr, [:pointer, :int], :pointer
|
150
235
|
|
236
|
+
# CFIndex CFStringGetLength(CFStringRef theString);
|
151
237
|
attach_function :CFStringGetLength, [ :CFStringRef ], :CFIndex
|
152
238
|
|
239
|
+
# CFIndex CFStringGetMaximumSizeForEncoding(CFIndex length, CFStringEncoding encoding);
|
153
240
|
attach_function :CFStringGetMaximumSizeForEncoding, [ :CFIndex, :CFStringEncoding ], :long
|
154
241
|
|
242
|
+
# Boolean CFStringGetCString(CFStringRef theString, char *buffer, CFIndex bufferSize, CFStringEncoding encoding);
|
155
243
|
attach_function :CFStringGetCString, [ :CFStringRef, :pointer, :CFIndex, :CFStringEncoding ], :bool
|
156
244
|
|
245
|
+
# void CFRelease (CFTypeRef cf);
|
157
246
|
attach_function :CFRelease, [ :pointer ], :void
|
158
247
|
|
159
248
|
end
|
@@ -162,6 +251,7 @@ module CoreMIDI
|
|
162
251
|
extend FFI::Library
|
163
252
|
ffi_lib '/System/Library/Frameworks/CoreAudio.framework/Versions/Current/CoreAudio'
|
164
253
|
|
254
|
+
# UInt64 AudioConvertHostTimeToNanos(UInt64 IO)
|
165
255
|
attach_function :AudioConvertHostTimeToNanos, [:uint64], :uint64
|
166
256
|
end
|
167
257
|
|
data/lib/coremidi/destination.rb
CHANGED
@@ -38,7 +38,7 @@ module CoreMIDI
|
|
38
38
|
# @return [Boolean]
|
39
39
|
def puts_bytes(*data)
|
40
40
|
type = sysex?(data) ? :sysex : :small
|
41
|
-
bytes =
|
41
|
+
bytes = API.get_midi_packet(data)
|
42
42
|
send("puts_#{type.to_s}", bytes, data.size)
|
43
43
|
true
|
44
44
|
end
|
@@ -99,7 +99,7 @@ module CoreMIDI
|
|
99
99
|
def connect
|
100
100
|
client_error = enable_client
|
101
101
|
port_error = initialize_port
|
102
|
-
@resource =
|
102
|
+
@resource = API.MIDIEntityGetDestination( @entity.resource, @resource_id )
|
103
103
|
!@resource.address.zero? && client_error.zero? && port_error.zero?
|
104
104
|
end
|
105
105
|
alias_method :connect?, :connect
|
@@ -108,53 +108,34 @@ module CoreMIDI
|
|
108
108
|
|
109
109
|
# Output a short MIDI message
|
110
110
|
def puts_small(bytes, size)
|
111
|
-
packet_list =
|
112
|
-
|
113
|
-
if Map::SnowLeopard
|
114
|
-
packet_ptr = Map.MIDIPacketListAdd(packet_list, 256, packet_ptr, 0, size, bytes)
|
115
|
-
else
|
116
|
-
# Pass in two 32-bit 0s for the 64 bit time
|
117
|
-
packet_ptr = Map.MIDIPacketListAdd(packet_list, 256, packet_ptr, 0, 0, size, bytes)
|
118
|
-
end
|
119
|
-
Map.MIDISend( @handle, @resource, packet_list )
|
111
|
+
packet_list = API.get_midi_packet_list(bytes, size)
|
112
|
+
API.MIDISend(@handle, @resource, packet_list)
|
120
113
|
true
|
121
114
|
end
|
122
115
|
|
123
116
|
# Output a System Exclusive MIDI message
|
124
117
|
def puts_sysex(bytes, size)
|
125
|
-
request =
|
118
|
+
request = API::MIDISysexSendRequest.new
|
126
119
|
request[:destination] = @resource
|
127
120
|
request[:data] = bytes
|
128
121
|
request[:bytes_to_send] = size
|
129
122
|
request[:complete] = 0
|
130
123
|
request[:completion_proc] = SysexCompletionCallback
|
131
124
|
request[:completion_ref_con] = request
|
132
|
-
|
125
|
+
API.MIDISendSysex(request)
|
133
126
|
true
|
134
127
|
end
|
135
128
|
|
136
129
|
SysexCompletionCallback =
|
137
|
-
|
130
|
+
API.get_callback([:pointer]) do |sysex_request_ptr|
|
138
131
|
# this isn't working for some reason. as of now, it's not needed though
|
139
132
|
end
|
140
133
|
|
141
134
|
# Initialize a coremidi port for this endpoint
|
142
135
|
def initialize_port
|
143
|
-
|
144
|
-
|
145
|
-
error
|
146
|
-
@handle = outport_ptr.read_pointer
|
147
|
-
error
|
148
|
-
end
|
149
|
-
|
150
|
-
# Pack the given data into a coremidi MIDI packet
|
151
|
-
def pack_data(data)
|
152
|
-
format = "C" * data.size
|
153
|
-
packed_data = data.pack(format)
|
154
|
-
char_size = FFI.type_size(:char) * data.size
|
155
|
-
bytes = FFI::MemoryPointer.new(char_size)
|
156
|
-
bytes.write_string(packed_data)
|
157
|
-
bytes
|
136
|
+
port = API.create_midi_output_port(@client, @resource_id, @name)
|
137
|
+
@handle = port[:handle]
|
138
|
+
port[:error]
|
158
139
|
end
|
159
140
|
|
160
141
|
# Is the given data a MIDI sysex message?
|
data/lib/coremidi/device.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
module CoreMIDI
|
2
2
|
|
3
|
+
# A MIDI device may have multiple logically distinct sub-components. For example, one device may
|
4
|
+
# encompass a MIDI synthesizer and a pair of MIDI ports, both addressable via a USB port. Each
|
5
|
+
# such element of a device is called a MIDI entity.
|
6
|
+
#
|
7
|
+
# https://developer.apple.com/library/ios/documentation/CoreMidi/Reference/MIDIServices_Reference/Reference/reference.html
|
3
8
|
class Device
|
4
9
|
|
5
10
|
attr_reader :entities,
|
@@ -48,7 +53,7 @@ module CoreMIDI
|
|
48
53
|
if !populated? || !use_cache
|
49
54
|
@devices = []
|
50
55
|
counter = 0
|
51
|
-
while !(device_pointer =
|
56
|
+
while !(device_pointer = API.MIDIGetDevice(counter)).null?
|
52
57
|
device = new(counter, device_pointer, :include_offline => include_offline)
|
53
58
|
@devices << device
|
54
59
|
counter += 1
|
@@ -74,7 +79,7 @@ module CoreMIDI
|
|
74
79
|
|
75
80
|
# Populate the device name
|
76
81
|
def populate_name
|
77
|
-
@name =
|
82
|
+
@name = API.get_string(@resource, "name")
|
78
83
|
raise RuntimeError.new("Can't get device name") unless @name
|
79
84
|
end
|
80
85
|
|
@@ -92,7 +97,7 @@ module CoreMIDI
|
|
92
97
|
def populate_entities(options = {})
|
93
98
|
include_if_offline = options[:include_offline] || false
|
94
99
|
i = 0
|
95
|
-
while !(entity_pointer =
|
100
|
+
while !(entity_pointer = API.MIDIDeviceGetEntity(@resource, i)).null?
|
96
101
|
@entities << Entity.new(entity_pointer, :include_offline => include_if_offline)
|
97
102
|
i += 1
|
98
103
|
end
|
data/lib/coremidi/endpoint.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module CoreMIDI
|
2
2
|
|
3
|
+
# A MIDI source or destination, owned by an entity.
|
3
4
|
module Endpoint
|
4
5
|
|
5
6
|
extend Forwardable
|
@@ -14,10 +15,12 @@ module CoreMIDI
|
|
14
15
|
|
15
16
|
alias_method :enabled?, :enabled
|
16
17
|
|
17
|
-
|
18
|
+
# @param [Fixnum] resource_id
|
19
|
+
# @param [Entity] entity
|
20
|
+
def initialize(resource_id, entity)
|
18
21
|
@entity = entity
|
19
22
|
@resource_id = resource_id
|
20
|
-
@type =
|
23
|
+
@type = get_type
|
21
24
|
@enabled = false
|
22
25
|
end
|
23
26
|
|
@@ -46,12 +49,24 @@ module CoreMIDI
|
|
46
49
|
all_by_type[type].last
|
47
50
|
end
|
48
51
|
|
52
|
+
# All source endpoints
|
53
|
+
# @return [Array<Source>]
|
54
|
+
def self.sources
|
55
|
+
Device.all.map { |d| d.endpoints[:source] }.flatten
|
56
|
+
end
|
57
|
+
|
58
|
+
# All destination endpoints
|
59
|
+
# @return [Array<Destination>]
|
60
|
+
def self.destinations
|
61
|
+
Device.all.map { |d| d.endpoints[:destination] }.flatten
|
62
|
+
end
|
63
|
+
|
49
64
|
# A Hash of :source and :destination endpoints
|
50
65
|
# @return [Hash]
|
51
66
|
def self.all_by_type
|
52
67
|
{
|
53
|
-
:source =>
|
54
|
-
:destination =>
|
68
|
+
:source => sources,
|
69
|
+
:destination => destinations
|
55
70
|
}
|
56
71
|
end
|
57
72
|
|
@@ -72,14 +87,18 @@ module CoreMIDI
|
|
72
87
|
end
|
73
88
|
|
74
89
|
protected
|
90
|
+
|
91
|
+
# Constructs the endpoint type (eg source, destination) for easy consumption
|
92
|
+
def get_type
|
93
|
+
class_name = self.class.name.split('::').last
|
94
|
+
class_name.downcase.to_sym
|
95
|
+
end
|
75
96
|
|
76
97
|
# Enables the coremidi MIDI client that will go with this endpoint
|
77
98
|
def enable_client
|
78
|
-
|
79
|
-
|
80
|
-
error
|
81
|
-
@client = client_ptr.read_pointer
|
82
|
-
error
|
99
|
+
client = API.create_midi_client(@resource_id, @name)
|
100
|
+
@client = client[:resource]
|
101
|
+
client[:error]
|
83
102
|
end
|
84
103
|
|
85
104
|
end
|
data/lib/coremidi/entity.rb
CHANGED
@@ -1,9 +1,15 @@
|
|
1
1
|
module CoreMIDI
|
2
2
|
|
3
|
+
# A MIDI entity can have any number of MIDI endpoints, each of which is a source or destination
|
4
|
+
# of a 16-channel MIDI stream. By grouping a device's endpoints into entities, the system has
|
5
|
+
# enough information for an application to make reasonable default assumptions about how to
|
6
|
+
# communicate in a bi-directional manner with each entity, as is necessary in MIDI librarian
|
7
|
+
# applications.
|
8
|
+
#
|
9
|
+
# https://developer.apple.com/library/ios/documentation/CoreMidi/Reference/MIDIServices_Reference/Reference/reference.html
|
3
10
|
class Entity
|
4
11
|
|
5
12
|
attr_reader :endpoints,
|
6
|
-
:is_online,
|
7
13
|
:manufacturer,
|
8
14
|
:model,
|
9
15
|
:name,
|
@@ -11,13 +17,14 @@ module CoreMIDI
|
|
11
17
|
|
12
18
|
# @param [FFI::Pointer] resource A pointer to the underlying entity
|
13
19
|
# @param [Hash] options
|
14
|
-
|
20
|
+
# @option options [Boolean] :include_offline Include offline endpoints in the list
|
21
|
+
def initialize(resource, options = {})
|
15
22
|
@endpoints = {
|
16
23
|
:source => [],
|
17
24
|
:destination => []
|
18
25
|
}
|
19
26
|
@resource = resource
|
20
|
-
populate
|
27
|
+
populate(options)
|
21
28
|
end
|
22
29
|
|
23
30
|
# Assign all of this Entity's endpoints an consecutive local id
|
@@ -25,7 +32,7 @@ module CoreMIDI
|
|
25
32
|
# @return [Fixnum]
|
26
33
|
def populate_endpoint_ids(starting_id)
|
27
34
|
counter = 0
|
28
|
-
@endpoints.values.flatten.each do |endpoint|
|
35
|
+
@endpoints.values.flatten.each do |endpoint|
|
29
36
|
endpoint.id = counter + starting_id
|
30
37
|
counter += 1
|
31
38
|
end
|
@@ -35,7 +42,7 @@ module CoreMIDI
|
|
35
42
|
# Is the entity online?
|
36
43
|
# @return [Boolean]
|
37
44
|
def online?
|
38
|
-
|
45
|
+
get_int(:offline) == 0
|
39
46
|
end
|
40
47
|
|
41
48
|
private
|
@@ -64,17 +71,19 @@ module CoreMIDI
|
|
64
71
|
end
|
65
72
|
|
66
73
|
# Populate the endpoints for this entity
|
74
|
+
# @param [Hash] options
|
75
|
+
# @option options [Boolean] :include_offline Include offline endpoints in the list
|
67
76
|
# @return [Fixnum]
|
68
|
-
def populate_endpoints
|
69
|
-
@endpoints.keys.map { |type| populate_endpoints_by_type(type) }.reduce(&:+)
|
77
|
+
def populate_endpoints(options = {})
|
78
|
+
@endpoints.keys.map { |type| populate_endpoints_by_type(type, options) }.reduce(&:+)
|
70
79
|
end
|
71
80
|
|
72
81
|
# The number of endpoints for this entity
|
73
82
|
# @param [Symbol] type The endpoint type eg :source, :destination
|
74
83
|
def number_of_endpoints(type)
|
75
84
|
case type
|
76
|
-
when :source then
|
77
|
-
when :destination then
|
85
|
+
when :source then API.MIDIEntityGetNumberOfSources(@resource)
|
86
|
+
when :destination then API.MIDIEntityGetNumberOfDestinations(@resource)
|
78
87
|
end
|
79
88
|
end
|
80
89
|
|
@@ -82,37 +91,24 @@ module CoreMIDI
|
|
82
91
|
# @param [Symbol, String] name The property name
|
83
92
|
# @return [String, nil]
|
84
93
|
def get_string(name)
|
85
|
-
|
94
|
+
API.get_string(@resource, name)
|
86
95
|
end
|
87
96
|
|
88
97
|
# An Integer property from the underlying entity
|
89
98
|
# @param [Symbol, String] name The property name
|
90
99
|
# @return [Fixnum, nil]
|
91
100
|
def get_int(name)
|
92
|
-
|
93
|
-
value = FFI::MemoryPointer.new(:pointer, 32)
|
94
|
-
Map::MIDIObjectGetIntegerProperty(@resource, property, value)
|
95
|
-
value.read_int
|
101
|
+
API.get_int(@resource, name)
|
96
102
|
end
|
97
103
|
|
98
|
-
# A CString or Integer property from the underlying entity
|
99
|
-
# @param [Symbol, String] name The property name
|
100
|
-
# @param [Hash] options
|
101
|
-
# @option options [Symbol] :type The property type eg :int, :string (default :string)
|
102
|
-
# @return [Fixnum, String, nil]
|
103
|
-
def get_property(name, options = {})
|
104
|
-
case options[:type]
|
105
|
-
when :string, nil then get_string(name)
|
106
|
-
when :int then get_int(name)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
104
|
# Populate the entity properties from the underlying resource
|
111
|
-
|
112
|
-
|
113
|
-
|
105
|
+
# @param [Hash] options
|
106
|
+
# @option options [Boolean] :include_offline Include offline endpoints in the list
|
107
|
+
def populate(options = {})
|
108
|
+
@manufacturer = get_string(:manufacturer)
|
109
|
+
@model = get_string(:model)
|
114
110
|
@name = get_name
|
115
|
-
populate_endpoints
|
111
|
+
populate_endpoints(options)
|
116
112
|
end
|
117
113
|
|
118
114
|
end
|
data/lib/coremidi/source.rb
CHANGED
@@ -42,7 +42,7 @@ module CoreMIDI
|
|
42
42
|
def gets_s
|
43
43
|
messages = gets
|
44
44
|
messages.each do |message|
|
45
|
-
message[:data] = numeric_bytes_to_hex_string(message[:data])
|
45
|
+
message[:data] = TypeConversion.numeric_bytes_to_hex_string(message[:data])
|
46
46
|
end
|
47
47
|
messages
|
48
48
|
end
|
@@ -69,13 +69,13 @@ module CoreMIDI
|
|
69
69
|
# Close this input
|
70
70
|
# @return [Boolean]
|
71
71
|
def close
|
72
|
-
#error =
|
72
|
+
#error = API.MIDIPortDisconnectSource( @handle, @resource )
|
73
73
|
#raise "MIDIPortDisconnectSource returned error code #{error}" unless error.zero?
|
74
|
-
#error =
|
74
|
+
#error = API.MIDIClientDispose(@handle)
|
75
75
|
#raise "MIDIClientDispose returned error code #{error}" unless error.zero?
|
76
|
-
#error =
|
76
|
+
#error = API.MIDIPortDispose(@handle)
|
77
77
|
#raise "MIDIPortDispose returned error code #{error}" unless error.zero?
|
78
|
-
#error =
|
78
|
+
#error = API.MIDIEndpointDispose(@resource)
|
79
79
|
#raise "MIDIEndpointDispose returned error code #{error}" unless error.zero?
|
80
80
|
if @enabled
|
81
81
|
@enabled = false
|
@@ -110,8 +110,8 @@ module CoreMIDI
|
|
110
110
|
def connect
|
111
111
|
enable_client
|
112
112
|
initialize_port
|
113
|
-
@resource =
|
114
|
-
error =
|
113
|
+
@resource = API.MIDIEntityGetSource(@entity.resource, @resource_id)
|
114
|
+
error = API.MIDIPortConnectSource(@handle, @resource, nil )
|
115
115
|
initialize_buffer
|
116
116
|
@sysex_buffer = []
|
117
117
|
@start_time = Time.now.to_f
|
@@ -169,12 +169,10 @@ module CoreMIDI
|
|
169
169
|
|
170
170
|
# Initialize a coremidi port for this endpoint
|
171
171
|
def initialize_port
|
172
|
-
port_name = Map::CF.CFStringCreateWithCString(nil, "Port #{@resource_id}: #{name}", 0)
|
173
|
-
handle_ptr = FFI::MemoryPointer.new(:pointer)
|
174
172
|
@callback = get_event_callback
|
175
|
-
|
176
|
-
@handle =
|
177
|
-
raise "MIDIInputPortCreate returned error code #{error}" unless error.zero?
|
173
|
+
port = API.create_midi_input_port(@client, @resource_id, @name, @callback)
|
174
|
+
@handle = port[:handle]
|
175
|
+
raise "MIDIInputPortCreate returned error code #{port[:error]}" unless port[:error].zero?
|
178
176
|
true
|
179
177
|
end
|
180
178
|
|
@@ -189,18 +187,6 @@ module CoreMIDI
|
|
189
187
|
true
|
190
188
|
end
|
191
189
|
|
192
|
-
# Convert an array of numeric byes to a hex string (e.g. [0x90, 0x40, 0x40] becomes "904040")
|
193
|
-
# @param [Array<Fixnum>] bytes
|
194
|
-
# @return [String]
|
195
|
-
def numeric_bytes_to_hex_string(bytes)
|
196
|
-
string_bytes = bytes.map do |byte|
|
197
|
-
str = byte.to_s(16).upcase
|
198
|
-
str = "0" + str if byte < 16
|
199
|
-
str
|
200
|
-
end
|
201
|
-
string_bytes.join
|
202
|
-
end
|
203
|
-
|
204
190
|
end
|
205
191
|
|
206
192
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module CoreMIDI
|
2
|
+
|
3
|
+
# Helper for convertig MIDI data
|
4
|
+
module TypeConversion
|
5
|
+
|
6
|
+
extend self
|
7
|
+
|
8
|
+
# Convert an array of numeric byes to a hex string (e.g. [0x90, 0x40, 0x40] becomes "904040")
|
9
|
+
# @param [Array<Fixnum>] bytes
|
10
|
+
# @return [String]
|
11
|
+
def numeric_bytes_to_hex_string(bytes)
|
12
|
+
string_bytes = bytes.map do |byte|
|
13
|
+
str = byte.to_s(16).upcase
|
14
|
+
str = "0" + str if byte < 16
|
15
|
+
str
|
16
|
+
end
|
17
|
+
string_bytes.join
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/coremidi.rb
CHANGED
@@ -8,8 +8,9 @@ require "ffi"
|
|
8
8
|
require "forwardable"
|
9
9
|
|
10
10
|
# modules
|
11
|
+
require "coremidi/api"
|
11
12
|
require "coremidi/endpoint"
|
12
|
-
require "coremidi/
|
13
|
+
require "coremidi/type_conversion"
|
13
14
|
|
14
15
|
# classes
|
15
16
|
require "coremidi/entity"
|
@@ -18,5 +19,5 @@ require "coremidi/source"
|
|
18
19
|
require "coremidi/destination"
|
19
20
|
|
20
21
|
module CoreMIDI
|
21
|
-
VERSION = "0.
|
22
|
+
VERSION = "0.3.1"
|
22
23
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffi-coremidi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ari Russo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -34,12 +34,13 @@ files:
|
|
34
34
|
- LICENSE
|
35
35
|
- README.md
|
36
36
|
- lib/coremidi.rb
|
37
|
+
- lib/coremidi/api.rb
|
37
38
|
- lib/coremidi/destination.rb
|
38
39
|
- lib/coremidi/device.rb
|
39
40
|
- lib/coremidi/endpoint.rb
|
40
41
|
- lib/coremidi/entity.rb
|
41
|
-
- lib/coremidi/map.rb
|
42
42
|
- lib/coremidi/source.rb
|
43
|
+
- lib/coremidi/type_conversion.rb
|
43
44
|
- test/helper.rb
|
44
45
|
- test/input_buffer_test.rb
|
45
46
|
- test/io_test.rb
|