ffi-coremidi 0.3.9 → 0.5.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 +5 -5
- data/LICENSE +1 -1
- data/README.md +1 -1
- data/lib/coremidi/api.rb +44 -50
- data/lib/coremidi/destination.rb +14 -16
- data/lib/coremidi/device.rb +11 -13
- data/lib/coremidi/endpoint.rb +8 -7
- data/lib/coremidi/entity.rb +10 -18
- data/lib/coremidi/source.rb +84 -68
- data/lib/coremidi/type_conversion.rb +6 -7
- data/lib/coremidi.rb +13 -11
- metadata +32 -56
- data/test/helper.rb +0 -66
- data/test/input_buffer_test.rb +0 -42
- data/test/io_test.rb +0 -90
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: dab097a8dd88318d79ef220b1a22a244892a7a410663dab5e7fb98b622621b0a
|
4
|
+
data.tar.gz: cbe5cbe19b52e5f728f55342ca1eddaa821c5c1cd2632fa7452c4782a6770950
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a2883b3a33e0f67dda8baa3da2458a8bb1c1b027909e1f70a8cd17a28b798c951b57b205c5c81a6a721d13e6477e72b2e5a3150150f4eab436ac1b84bc2a796
|
7
|
+
data.tar.gz: 1539a681f754432531c2ec5c806214cef21e4ef688b6ea60d809631d90c36e10a8f7945ecd8fe2dfdb6a8948ec6ea2ef721cc07873a1de83c1352688cd89661d
|
data/LICENSE
CHANGED
data/README.md
CHANGED
data/lib/coremidi/api.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module CoreMIDI
|
3
4
|
# Coremidi C binding
|
4
5
|
module API
|
5
|
-
|
6
6
|
extend FFI::Library
|
7
7
|
ffi_lib '/System/Library/Frameworks/CoreMIDI.framework/Versions/Current/CoreMIDI'
|
8
8
|
|
@@ -17,12 +17,11 @@ module CoreMIDI
|
|
17
17
|
typedef :pointer, :MIDIEntityRef
|
18
18
|
typedef :pointer, :MIDIObjectRef
|
19
19
|
typedef :pointer, :MIDIPortRef
|
20
|
-
#typedef :pointer, :MIDIReadProc
|
20
|
+
# typedef :pointer, :MIDIReadProc
|
21
21
|
typedef :uint32, :MIDITimeStamp
|
22
22
|
typedef :int32, :OSStatus
|
23
23
|
|
24
24
|
class MIDISysexSendRequest < FFI::Struct
|
25
|
-
|
26
25
|
layout :destination, :MIDIEndpointRef,
|
27
26
|
:data, :pointer,
|
28
27
|
:bytes_to_send, :uint32,
|
@@ -33,18 +32,15 @@ module CoreMIDI
|
|
33
32
|
end
|
34
33
|
|
35
34
|
class MIDIPacket < FFI::Struct
|
36
|
-
|
37
35
|
layout :timestamp, :MIDITimeStamp,
|
38
36
|
:nothing, :uint32, # no idea...
|
39
37
|
:length, :uint16,
|
40
38
|
:data, [:uint8, 256]
|
41
|
-
|
42
39
|
end
|
43
40
|
|
44
41
|
class MIDIPacketList < FFI::Struct
|
45
42
|
layout :numPackets, :uint32,
|
46
43
|
:packet, [MIDIPacket.by_value, 1]
|
47
|
-
|
48
44
|
end
|
49
45
|
|
50
46
|
def self.get_callback(*args, &block)
|
@@ -53,7 +49,7 @@ module CoreMIDI
|
|
53
49
|
|
54
50
|
# Pack the given data into a coremidi MIDI packet (used by Destination)
|
55
51
|
def self.get_midi_packet(data)
|
56
|
-
format =
|
52
|
+
format = 'C' * data.size
|
57
53
|
packed_data = data.pack(format)
|
58
54
|
char_size = FFI.type_size(:char) * data.size
|
59
55
|
bytes = FFI::MemoryPointer.new(char_size)
|
@@ -67,8 +63,8 @@ module CoreMIDI
|
|
67
63
|
error = API.MIDIClientCreate(client_name, nil, nil, client_pointer)
|
68
64
|
client = client_pointer.read_pointer
|
69
65
|
{
|
70
|
-
:
|
71
|
-
:
|
66
|
+
error: error,
|
67
|
+
resource: client
|
72
68
|
}
|
73
69
|
end
|
74
70
|
|
@@ -78,8 +74,8 @@ module CoreMIDI
|
|
78
74
|
error = API.MIDIInputPortCreate(client, port_name, callback, nil, handle_ptr)
|
79
75
|
handle = handle_ptr.read_pointer
|
80
76
|
{
|
81
|
-
:
|
82
|
-
:
|
77
|
+
error: error,
|
78
|
+
handle: handle
|
83
79
|
}
|
84
80
|
end
|
85
81
|
|
@@ -89,8 +85,8 @@ module CoreMIDI
|
|
89
85
|
error = API.MIDIOutputPortCreate(client, port_name, port_pointer)
|
90
86
|
handle = port_pointer.read_pointer
|
91
87
|
{
|
92
|
-
:
|
93
|
-
:
|
88
|
+
error: error,
|
89
|
+
handle: handle
|
94
90
|
}
|
95
91
|
end
|
96
92
|
|
@@ -100,11 +96,12 @@ module CoreMIDI
|
|
100
96
|
packet_ptr = API.MIDIPacketListInit(packet_list)
|
101
97
|
time = HostTime.AudioGetCurrentHostTime
|
102
98
|
packet_ptr = if X86_64
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
99
|
+
API.MIDIPacketListAdd(packet_list, 256, packet_ptr, time, size, bytes)
|
100
|
+
else
|
101
|
+
# Pass in two 32-bit 0s for the 64 bit time
|
102
|
+
time1 = API.MIDIPacketListAdd(packet_list, 256, packet_ptr, time >> 32, time & 0xFFFFFFFF, size,
|
103
|
+
bytes)
|
104
|
+
end
|
108
105
|
packet_list
|
109
106
|
end
|
110
107
|
|
@@ -132,7 +129,7 @@ module CoreMIDI
|
|
132
129
|
bytes = FFI::MemoryPointer.new(length + 1)
|
133
130
|
|
134
131
|
if CF.CFStringGetCString(string, bytes, length + 1, :kCFStringEncodingUTF8)
|
135
|
-
bytes.read_string.force_encoding(
|
132
|
+
bytes.read_string.force_encoding('utf-8')
|
136
133
|
end
|
137
134
|
ensure
|
138
135
|
CF.CFRelease(string) unless string.nil? || string.null?
|
@@ -145,13 +142,13 @@ module CoreMIDI
|
|
145
142
|
callback :MIDIReadProc, [MIDIPacketList.by_ref, :pointer, :pointer], :pointer
|
146
143
|
|
147
144
|
# OSStatus MIDIClientCreate(CFStringRef name, MIDINotifyProc notifyProc, void *notifyRefCon, MIDIClientRef *outClient);
|
148
|
-
attach_function :MIDIClientCreate, [
|
145
|
+
attach_function :MIDIClientCreate, %i[pointer pointer pointer pointer], :int
|
149
146
|
|
150
147
|
# OSStatus MIDIClientDispose(MIDIClientRef client);
|
151
148
|
attach_function :MIDIClientDispose, [:pointer], :int
|
152
149
|
|
153
150
|
# MIDIEntityRef MIDIDeviceGetEntity(MIDIDeviceRef device, ItemCount entityIndex0);
|
154
|
-
attach_function :MIDIDeviceGetEntity, [
|
151
|
+
attach_function :MIDIDeviceGetEntity, %i[MIDIDeviceRef ItemCount], :MIDIEntityRef
|
155
152
|
|
156
153
|
# MIDIEndpointRef MIDIGetDestination(ItemCount destIndex0);
|
157
154
|
attach_function :MIDIGetNumberOfDestinations, [], :ItemCount
|
@@ -162,11 +159,11 @@ module CoreMIDI
|
|
162
159
|
# MIDIEndpointRef MIDIEntityGetDestination(MIDIEntityRef entity, ItemCount destIndex0);
|
163
160
|
attach_function :MIDIGetDestination, [:int], :pointer
|
164
161
|
|
165
|
-
#extern OSStatus MIDIEndpointDispose( MIDIEndpointRef endpt );
|
162
|
+
# extern OSStatus MIDIEndpointDispose( MIDIEndpointRef endpt );
|
166
163
|
attach_function :MIDIEndpointDispose, [:MIDIEndpointRef], :OSStatus
|
167
164
|
|
168
165
|
# MIDIEndpointRef MIDIEntityGetDestination( MIDIEntityRef entity, ItemCount destIndex0 );
|
169
|
-
attach_function :MIDIEntityGetDestination, [
|
166
|
+
attach_function :MIDIEntityGetDestination, %i[MIDIEntityRef int], :MIDIEndpointRef
|
170
167
|
|
171
168
|
# ItemCount MIDIEntityGetNumberOfDestinations (MIDIEntityRef entity);
|
172
169
|
attach_function :MIDIEntityGetNumberOfDestinations, [:MIDIEntityRef], :ItemCount
|
@@ -175,77 +172,76 @@ module CoreMIDI
|
|
175
172
|
attach_function :MIDIEntityGetNumberOfSources, [:MIDIEntityRef], :ItemCount
|
176
173
|
|
177
174
|
# MIDIEndpointRef MIDIEntityGetSource (MIDIEntityRef entity, ItemCount sourceIndex0);
|
178
|
-
attach_function :MIDIEntityGetSource, [
|
175
|
+
attach_function :MIDIEntityGetSource, %i[MIDIEntityRef ItemCount], :MIDIEndpointRef
|
179
176
|
|
180
177
|
# MIDIDeviceRef MIDIGetDevice(ItemCount deviceIndex0);
|
181
178
|
attach_function :MIDIGetDevice, [:ItemCount], :MIDIDeviceRef
|
182
179
|
|
183
180
|
# extern OSStatus MIDIInputPortCreate( MIDIClientRef client, CFStringRef portName,
|
184
181
|
# MIDIReadProc readProc, void * refCon, MIDIPortRef * outPort );
|
185
|
-
attach_function :MIDIInputPortCreate, [
|
182
|
+
attach_function :MIDIInputPortCreate, %i[MIDIClientRef CFStringRef MIDIReadProc pointer MIDIPortRef],
|
183
|
+
:OSStatus
|
186
184
|
|
187
185
|
# extern OSStatus MIDIObjectGetIntegerProperty( MIDIObjectRef obj, CFStringRef propertyID, SInt32 * outValue );
|
188
|
-
attach_function :MIDIObjectGetIntegerProperty, [
|
186
|
+
attach_function :MIDIObjectGetIntegerProperty, %i[MIDIObjectRef CFStringRef pointer], :OSStatus
|
189
187
|
|
190
188
|
# OSStatus MIDIObjectGetStringProperty (MIDIObjectRef obj, CFStringRef propertyID, CFStringRef *str);
|
191
|
-
attach_function :MIDIObjectGetStringProperty, [
|
189
|
+
attach_function :MIDIObjectGetStringProperty, %i[MIDIObjectRef CFStringRef pointer], :OSStatus
|
192
190
|
|
193
191
|
# extern OSStatus MIDIOutputPortCreate( MIDIClientRef client, CFStringRef portName, MIDIPortRef * outPort );
|
194
|
-
attach_function :MIDIOutputPortCreate, [
|
192
|
+
attach_function :MIDIOutputPortCreate, %i[MIDIClientRef CFStringRef pointer], :int
|
195
193
|
|
196
194
|
# (MIDIPacket*) MIDIPacketListInit(MIDIPacketList *pktlist);
|
197
195
|
attach_function :MIDIPacketListInit, [:pointer], :pointer
|
198
196
|
|
199
|
-
#extern OSStatus MIDIPortConnectSource( MIDIPortRef port, MIDIEndpointRef source, void * connRefCon )
|
200
|
-
attach_function :MIDIPortConnectSource, [
|
197
|
+
# extern OSStatus MIDIPortConnectSource( MIDIPortRef port, MIDIEndpointRef source, void * connRefCon )
|
198
|
+
attach_function :MIDIPortConnectSource, %i[MIDIPortRef MIDIEndpointRef pointer], :OSStatus
|
201
199
|
|
202
|
-
#extern OSStatus MIDIPortDisconnectSource( MIDIPortRef port, MIDIEndpointRef source );
|
203
|
-
attach_function :MIDIPortDisconnectSource, [
|
200
|
+
# extern OSStatus MIDIPortDisconnectSource( MIDIPortRef port, MIDIEndpointRef source );
|
201
|
+
attach_function :MIDIPortDisconnectSource, %i[MIDIPortRef MIDIEndpointRef], :OSStatus
|
204
202
|
|
205
|
-
#extern OSStatus MIDIPortDispose(MIDIPortRef port );
|
203
|
+
# extern OSStatus MIDIPortDispose(MIDIPortRef port );
|
206
204
|
attach_function :MIDIPortDispose, [:MIDIPortRef], :OSStatus
|
207
205
|
|
208
|
-
#extern OSStatus MIDISend(MIDIPortRef port,MIDIEndpointRef dest,const MIDIPacketList *pktlist);
|
209
|
-
attach_function :MIDISend, [
|
206
|
+
# extern OSStatus MIDISend(MIDIPortRef port,MIDIEndpointRef dest,const MIDIPacketList *pktlist);
|
207
|
+
attach_function :MIDISend, %i[MIDIPortRef MIDIEndpointRef pointer], :int
|
210
208
|
|
211
|
-
#OSStatus MIDISendSysex(MIDISysexSendRequest *request);
|
209
|
+
# OSStatus MIDISendSysex(MIDISysexSendRequest *request);
|
212
210
|
attach_function :MIDISendSysex, [:pointer], :int
|
213
211
|
|
214
212
|
# extern MIDIPacket * MIDIPacketListAdd( MIDIPacketList * pktlist, ByteCount listSize,
|
215
213
|
# MIDIPacket * curPacket, MIDITimeStamp time,
|
216
214
|
# ByteCount nData, const Byte * data)
|
217
215
|
if X86_64
|
218
|
-
attach_function :MIDIPacketListAdd, [
|
216
|
+
attach_function :MIDIPacketListAdd, %i[pointer int pointer uint64 int pointer], :pointer
|
219
217
|
else
|
220
|
-
attach_function :MIDIPacketListAdd, [
|
218
|
+
attach_function :MIDIPacketListAdd, %i[pointer int pointer int int int pointer], :pointer
|
221
219
|
end
|
222
220
|
|
223
221
|
module CF
|
224
|
-
|
225
222
|
extend FFI::Library
|
226
223
|
ffi_lib '/System/Library/Frameworks/CoreFoundation.framework/Versions/Current/CoreFoundation'
|
227
224
|
|
228
225
|
typedef :pointer, :CFStringRef
|
229
226
|
typedef :long, :CFIndex
|
230
|
-
enum :CFStringEncoding, [
|
227
|
+
enum :CFStringEncoding, [:kCFStringEncodingUTF8, 0x08000100]
|
231
228
|
|
232
229
|
# CFString* CFStringCreateWithCString( ?, CString, encoding)
|
233
|
-
attach_function :CFStringCreateWithCString, [
|
230
|
+
attach_function :CFStringCreateWithCString, %i[pointer string int], :pointer
|
234
231
|
# CString* CFStringGetCStringPtr(CFString*, encoding)
|
235
|
-
attach_function :CFStringGetCStringPtr, [
|
232
|
+
attach_function :CFStringGetCStringPtr, %i[pointer int], :pointer
|
236
233
|
|
237
234
|
# CFIndex CFStringGetLength(CFStringRef theString);
|
238
|
-
attach_function :CFStringGetLength, [
|
235
|
+
attach_function :CFStringGetLength, [:CFStringRef], :CFIndex
|
239
236
|
|
240
237
|
# CFIndex CFStringGetMaximumSizeForEncoding(CFIndex length, CFStringEncoding encoding);
|
241
|
-
attach_function :CFStringGetMaximumSizeForEncoding, [
|
238
|
+
attach_function :CFStringGetMaximumSizeForEncoding, %i[CFIndex CFStringEncoding], :long
|
242
239
|
|
243
240
|
# Boolean CFStringGetCString(CFStringRef theString, char *buffer, CFIndex bufferSize, CFStringEncoding encoding);
|
244
|
-
attach_function :CFStringGetCString, [
|
241
|
+
attach_function :CFStringGetCString, %i[CFStringRef pointer CFIndex CFStringEncoding], :bool
|
245
242
|
|
246
243
|
# void CFRelease (CFTypeRef cf);
|
247
|
-
attach_function :CFRelease, [
|
248
|
-
|
244
|
+
attach_function :CFRelease, [:pointer], :void
|
249
245
|
end
|
250
246
|
|
251
247
|
module HostTime
|
@@ -257,7 +253,5 @@ module CoreMIDI
|
|
257
253
|
# UInt64 AudioGetCurrentHostTime()
|
258
254
|
attach_function :AudioGetCurrentHostTime, [], :uint64
|
259
255
|
end
|
260
|
-
|
261
256
|
end
|
262
|
-
|
263
257
|
end
|
data/lib/coremidi/destination.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module CoreMIDI
|
3
4
|
# Type of endpoint used for output
|
4
5
|
class Destination
|
5
|
-
|
6
6
|
include Endpoint
|
7
7
|
|
8
8
|
attr_reader :entity
|
@@ -24,14 +24,14 @@ module CoreMIDI
|
|
24
24
|
def puts_s(data)
|
25
25
|
data = data.dup
|
26
26
|
bytes = []
|
27
|
-
until (str = data.slice!(0,2)).eql?(
|
27
|
+
until (str = data.slice!(0, 2)).eql?('')
|
28
28
|
bytes << str.hex
|
29
29
|
end
|
30
30
|
puts_bytes(*bytes)
|
31
31
|
true
|
32
32
|
end
|
33
|
-
|
34
|
-
|
33
|
+
alias puts_bytestr puts_s
|
34
|
+
alias puts_hex puts_s
|
35
35
|
|
36
36
|
# Send a MIDI message comprised of numeric bytes
|
37
37
|
# @param [*Integer] data Numeric bytes eg 0x90, 0x40, 0x40
|
@@ -39,7 +39,7 @@ module CoreMIDI
|
|
39
39
|
def puts_bytes(*data)
|
40
40
|
type = sysex?(data) ? :sysex : :small
|
41
41
|
bytes = API.get_midi_packet(data)
|
42
|
-
send("puts_#{type
|
42
|
+
send("puts_#{type}", bytes, data.size)
|
43
43
|
true
|
44
44
|
end
|
45
45
|
|
@@ -53,12 +53,12 @@ module CoreMIDI
|
|
53
53
|
when String then puts_bytestr(*args)
|
54
54
|
end
|
55
55
|
end
|
56
|
-
|
56
|
+
alias write puts
|
57
57
|
|
58
58
|
# Enable this device
|
59
59
|
# @return [Destination]
|
60
|
-
def enable(
|
61
|
-
@enabled
|
60
|
+
def enable(_options = {})
|
61
|
+
@enabled ||= true
|
62
62
|
if block_given?
|
63
63
|
begin
|
64
64
|
yield(self)
|
@@ -68,8 +68,8 @@ module CoreMIDI
|
|
68
68
|
end
|
69
69
|
self
|
70
70
|
end
|
71
|
-
|
72
|
-
|
71
|
+
alias open enable
|
72
|
+
alias start enable
|
73
73
|
|
74
74
|
# Shortcut to the first output endpoint available
|
75
75
|
# @return [Destination]
|
@@ -97,10 +97,10 @@ module CoreMIDI
|
|
97
97
|
def connect
|
98
98
|
client_error = enable_client
|
99
99
|
port_error = initialize_port
|
100
|
-
@resource = API.MIDIEntityGetDestination(
|
100
|
+
@resource = API.MIDIEntityGetDestination(@entity.resource, @resource_id)
|
101
101
|
!@resource.address.zero? && client_error.zero? && port_error.zero?
|
102
102
|
end
|
103
|
-
|
103
|
+
alias connect? connect
|
104
104
|
|
105
105
|
private
|
106
106
|
|
@@ -126,7 +126,7 @@ module CoreMIDI
|
|
126
126
|
|
127
127
|
SysexCompletionCallback =
|
128
128
|
API.get_callback([:pointer]) do |sysex_request_ptr|
|
129
|
-
|
129
|
+
# this isn't working for some reason. as of now, it's not needed though
|
130
130
|
end
|
131
131
|
|
132
132
|
# Initialize a coremidi port for this endpoint
|
@@ -140,7 +140,5 @@ module CoreMIDI
|
|
140
140
|
def sysex?(data)
|
141
141
|
data.first.eql?(0xF0) && data.last.eql?(0xF7)
|
142
142
|
end
|
143
|
-
|
144
143
|
end
|
145
|
-
|
146
144
|
end
|
data/lib/coremidi/device.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module CoreMIDI
|
3
4
|
# A MIDI device may have multiple logically distinct sub-components. For example, one device may
|
4
5
|
# encompass a MIDI synthesizer and a pair of MIDI ports, both addressable via a USB port. Each
|
5
6
|
# such element of a device is called a MIDI entity.
|
6
7
|
#
|
7
8
|
# https://developer.apple.com/library/ios/documentation/CoreMidi/Reference/MIDIServices_Reference/Reference/reference.html
|
8
9
|
class Device
|
9
|
-
|
10
10
|
attr_reader :entities,
|
11
11
|
:id, # Unique Numeric id
|
12
12
|
:name # Device name from coremidi
|
@@ -25,8 +25,8 @@ module CoreMIDI
|
|
25
25
|
# Endpoints for this device
|
26
26
|
# @return [Array<Endpoint>]
|
27
27
|
def endpoints
|
28
|
-
endpoints = { :
|
29
|
-
endpoints.
|
28
|
+
endpoints = { source: [], destination: [] }
|
29
|
+
endpoints.each_key do |key|
|
30
30
|
endpoint_group = entities.map { |entity| entity.endpoints[key] }.flatten
|
31
31
|
endpoints[key] += endpoint_group
|
32
32
|
end
|
@@ -53,8 +53,8 @@ module CoreMIDI
|
|
53
53
|
if !populated? || !use_cache
|
54
54
|
@devices = []
|
55
55
|
counter = 0
|
56
|
-
|
57
|
-
device = new(counter, device_pointer, :
|
56
|
+
until (device_pointer = API.MIDIGetDevice(counter)).null?
|
57
|
+
device = new(counter, device_pointer, include_offline: include_offline)
|
58
58
|
@devices << device
|
59
59
|
counter += 1
|
60
60
|
end
|
@@ -79,8 +79,8 @@ module CoreMIDI
|
|
79
79
|
|
80
80
|
# Populate the device name
|
81
81
|
def populate_name
|
82
|
-
@name = API.get_string(@resource,
|
83
|
-
raise
|
82
|
+
@name = API.get_string(@resource, 'name')
|
83
|
+
raise "Can't get device name" unless @name
|
84
84
|
end
|
85
85
|
|
86
86
|
# All of the endpoints for all devices a consecutive local id
|
@@ -97,8 +97,8 @@ module CoreMIDI
|
|
97
97
|
def populate_entities(options = {})
|
98
98
|
include_if_offline = options[:include_offline] || false
|
99
99
|
i = 0
|
100
|
-
|
101
|
-
@entities << Entity.new(entity_pointer, :
|
100
|
+
until (entity_pointer = API.MIDIDeviceGetEntity(@resource, i)).null?
|
101
|
+
@entities << Entity.new(entity_pointer, include_offline: include_if_offline)
|
102
102
|
i += 1
|
103
103
|
end
|
104
104
|
i
|
@@ -107,9 +107,7 @@ module CoreMIDI
|
|
107
107
|
# Populate the instance
|
108
108
|
def populate(options = {})
|
109
109
|
populate_name
|
110
|
-
populate_entities(:
|
110
|
+
populate_entities(include_offline: options[:include_offline])
|
111
111
|
end
|
112
|
-
|
113
112
|
end
|
114
|
-
|
115
113
|
end
|
data/lib/coremidi/endpoint.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module CoreMIDI
|
3
4
|
# A source or destination of a 16-channel MIDI stream
|
4
5
|
#
|
5
6
|
# https://developer.apple.com/library/ios/documentation/CoreMidi/Reference/MIDIServices_Reference/Reference/reference.html
|
6
7
|
module Endpoint
|
7
|
-
|
8
8
|
extend Forwardable
|
9
9
|
|
10
10
|
attr_reader :enabled, # has the endpoint been initialized?
|
@@ -15,7 +15,7 @@ module CoreMIDI
|
|
15
15
|
|
16
16
|
def_delegators :entity, :manufacturer, :model, :name
|
17
17
|
|
18
|
-
|
18
|
+
alias enabled? enabled
|
19
19
|
|
20
20
|
# @param [Integer] resource_id
|
21
21
|
# @param [Entity] entity
|
@@ -24,6 +24,9 @@ module CoreMIDI
|
|
24
24
|
@resource_id = resource_id
|
25
25
|
@type = get_type
|
26
26
|
@enabled = false
|
27
|
+
|
28
|
+
@threads_sync_semaphore = Mutex.new
|
29
|
+
@threads_waiting = []
|
27
30
|
end
|
28
31
|
|
29
32
|
# Is this endpoint online?
|
@@ -67,8 +70,8 @@ module CoreMIDI
|
|
67
70
|
# @return [Hash]
|
68
71
|
def self.all_by_type
|
69
72
|
{
|
70
|
-
:
|
71
|
-
:
|
73
|
+
source: sources,
|
74
|
+
destination: destinations
|
72
75
|
}
|
73
76
|
end
|
74
77
|
|
@@ -102,7 +105,5 @@ module CoreMIDI
|
|
102
105
|
@client = client[:resource]
|
103
106
|
client[:error]
|
104
107
|
end
|
105
|
-
|
106
108
|
end
|
107
|
-
|
108
109
|
end
|
data/lib/coremidi/entity.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module CoreMIDI
|
3
4
|
# A MIDI entity can have any number of MIDI endpoints, each of which is a source or destination
|
4
5
|
# of a 16-channel MIDI stream. By grouping a device's endpoints into entities, the system has
|
5
6
|
# enough information for an application to make reasonable default assumptions about how to
|
@@ -8,7 +9,6 @@ module CoreMIDI
|
|
8
9
|
#
|
9
10
|
# https://developer.apple.com/library/ios/documentation/CoreMidi/Reference/MIDIServices_Reference/Reference/reference.html
|
10
11
|
class Entity
|
11
|
-
|
12
12
|
attr_reader :endpoints,
|
13
13
|
:manufacturer,
|
14
14
|
:model,
|
@@ -20,8 +20,8 @@ module CoreMIDI
|
|
20
20
|
# @option options [Boolean] :include_offline Include offline endpoints in the list
|
21
21
|
def initialize(resource, options = {})
|
22
22
|
@endpoints = {
|
23
|
-
:
|
24
|
-
:
|
23
|
+
source: [],
|
24
|
+
destination: []
|
25
25
|
}
|
26
26
|
@resource = resource
|
27
27
|
populate(options)
|
@@ -31,18 +31,13 @@ module CoreMIDI
|
|
31
31
|
# @param [Integer] starting_id
|
32
32
|
# @return [Integer]
|
33
33
|
def populate_endpoint_ids(starting_id)
|
34
|
-
|
35
|
-
@endpoints.values.flatten.each do |endpoint|
|
36
|
-
endpoint.id = counter + starting_id
|
37
|
-
counter += 1
|
38
|
-
end
|
39
|
-
counter
|
34
|
+
@endpoints.values.flatten.map.with_index { |endpoint, index| endpoint.id = index + starting_id }.length
|
40
35
|
end
|
41
36
|
|
42
37
|
# Is the entity online?
|
43
38
|
# @return [Boolean]
|
44
39
|
def online?
|
45
|
-
get_int(:offline)
|
40
|
+
get_int(:offline).zero?
|
46
41
|
end
|
47
42
|
|
48
43
|
private
|
@@ -63,9 +58,8 @@ module CoreMIDI
|
|
63
58
|
num_endpoints = number_of_endpoints(type)
|
64
59
|
(0..num_endpoints).each do |i|
|
65
60
|
endpoint = endpoint_class.new(i, self)
|
66
|
-
|
67
|
-
|
68
|
-
end
|
61
|
+
should_include_message = endpoint.online? || options[:include_offline]
|
62
|
+
@endpoints[type] << endpoint if should_include_message
|
69
63
|
end
|
70
64
|
@endpoints[type].size
|
71
65
|
end
|
@@ -82,8 +76,8 @@ module CoreMIDI
|
|
82
76
|
# @param [Symbol] type The endpoint type eg :source, :destination
|
83
77
|
def number_of_endpoints(type)
|
84
78
|
case type
|
85
|
-
|
86
|
-
|
79
|
+
when :source then API.MIDIEntityGetNumberOfSources(@resource)
|
80
|
+
when :destination then API.MIDIEntityGetNumberOfDestinations(@resource)
|
87
81
|
end
|
88
82
|
end
|
89
83
|
|
@@ -110,7 +104,5 @@ module CoreMIDI
|
|
110
104
|
@name = get_name
|
111
105
|
populate_endpoints(options)
|
112
106
|
end
|
113
|
-
|
114
107
|
end
|
115
|
-
|
116
108
|
end
|
data/lib/coremidi/source.rb
CHANGED
@@ -1,11 +1,16 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module CoreMIDI
|
3
4
|
# Type of endpoint used for input
|
4
5
|
class Source
|
5
|
-
|
6
6
|
include Endpoint
|
7
7
|
|
8
|
-
|
8
|
+
# The buffer of received messages since instantiation
|
9
|
+
# @return [Array<Hash>]
|
10
|
+
def buffer
|
11
|
+
fill_buffer
|
12
|
+
@buffer
|
13
|
+
end
|
9
14
|
|
10
15
|
#
|
11
16
|
# An array of MIDI event hashes as such:
|
@@ -20,15 +25,9 @@ module CoreMIDI
|
|
20
25
|
#
|
21
26
|
# @return [Array<Hash>]
|
22
27
|
def gets
|
23
|
-
|
24
|
-
# per https://github.com/arirusso/unimidi/issues/20#issuecomment-44761318
|
25
|
-
sleep(0.0001) # patch to prevent 100% CPU issue with some midi controllers
|
26
|
-
end
|
27
|
-
messages = queued_messages
|
28
|
-
@pointer = @buffer.length
|
29
|
-
messages
|
28
|
+
fill_buffer(locking: true)
|
30
29
|
end
|
31
|
-
|
30
|
+
alias read gets
|
32
31
|
|
33
32
|
# Same as Source#gets except that it returns message data as string of hex
|
34
33
|
# digits as such:
|
@@ -46,12 +45,12 @@ module CoreMIDI
|
|
46
45
|
end
|
47
46
|
messages
|
48
47
|
end
|
49
|
-
|
48
|
+
alias gets_bytestr gets_s
|
50
49
|
|
51
50
|
# Enable this the input for use; can be passed a block
|
52
51
|
# @return [Source]
|
53
|
-
def enable(
|
54
|
-
@enabled
|
52
|
+
def enable(_options = {})
|
53
|
+
@enabled ||= true
|
55
54
|
if block_given?
|
56
55
|
begin
|
57
56
|
yield(self)
|
@@ -61,20 +60,20 @@ module CoreMIDI
|
|
61
60
|
end
|
62
61
|
self
|
63
62
|
end
|
64
|
-
|
65
|
-
|
63
|
+
alias open enable
|
64
|
+
alias start enable
|
66
65
|
|
67
66
|
# Close this input
|
68
67
|
# @return [Boolean]
|
69
68
|
def close
|
70
|
-
#error = API.MIDIPortDisconnectSource( @handle, @resource )
|
71
|
-
#raise "MIDIPortDisconnectSource returned error code #{error}" unless error.zero?
|
72
|
-
#error = API.MIDIClientDispose(@handle)
|
73
|
-
#raise "MIDIClientDispose returned error code #{error}" unless error.zero?
|
74
|
-
#error = API.MIDIPortDispose(@handle)
|
75
|
-
#raise "MIDIPortDispose returned error code #{error}" unless error.zero?
|
76
|
-
#error = API.MIDIEndpointDispose(@resource)
|
77
|
-
#raise "MIDIEndpointDispose returned error code #{error}" unless error.zero?
|
69
|
+
# error = API.MIDIPortDisconnectSource( @handle, @resource )
|
70
|
+
# raise "MIDIPortDisconnectSource returned error code #{error}" unless error.zero?
|
71
|
+
# error = API.MIDIClientDispose(@handle)
|
72
|
+
# raise "MIDIClientDispose returned error code #{error}" unless error.zero?
|
73
|
+
# error = API.MIDIPortDispose(@handle)
|
74
|
+
# raise "MIDIPortDispose returned error code #{error}" unless error.zero?
|
75
|
+
# error = API.MIDIEndpointDispose(@resource)
|
76
|
+
# raise "MIDIEndpointDispose returned error code #{error}" unless error.zero?
|
78
77
|
if @enabled
|
79
78
|
@enabled = false
|
80
79
|
true
|
@@ -103,25 +102,50 @@ module CoreMIDI
|
|
103
102
|
|
104
103
|
protected
|
105
104
|
|
105
|
+
def truncate_buffer
|
106
|
+
@buffer.slice!(-1024, 1024)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Migrate new received messages from the callback queue to
|
110
|
+
# the buffer
|
111
|
+
def fill_buffer(locking: nil)
|
112
|
+
locking ||= false
|
113
|
+
|
114
|
+
messages = []
|
115
|
+
|
116
|
+
if locking && @queue.empty?
|
117
|
+
@threads_sync_semaphore.synchronize do
|
118
|
+
@threads_waiting << Thread.current
|
119
|
+
end
|
120
|
+
sleep
|
121
|
+
end
|
122
|
+
|
123
|
+
messages << @queue.pop until @queue.empty?
|
124
|
+
@buffer += messages
|
125
|
+
truncate_buffer
|
126
|
+
@pointer = @buffer.length
|
127
|
+
messages
|
128
|
+
end
|
129
|
+
|
106
130
|
# Base initialization for this endpoint -- done whether or not the endpoint is enabled to check whether
|
107
131
|
# it is truly available for use
|
108
132
|
def connect
|
109
133
|
enable_client
|
110
134
|
initialize_port
|
111
135
|
@resource = API.MIDIEntityGetSource(@entity.resource, @resource_id)
|
112
|
-
error = API.MIDIPortConnectSource(@handle, @resource, nil
|
136
|
+
error = API.MIDIPortConnectSource(@handle, @resource, nil)
|
113
137
|
initialize_buffer
|
138
|
+
@queue = Queue.new
|
114
139
|
@sysex_buffer = []
|
115
|
-
@start_time = Time.now.to_f
|
116
140
|
|
117
141
|
error.zero?
|
118
142
|
end
|
119
|
-
|
143
|
+
alias connect? connect
|
120
144
|
|
121
145
|
private
|
122
146
|
|
123
|
-
# Add a single message to the
|
124
|
-
# @param [Array<
|
147
|
+
# Add a single message to the callback queue
|
148
|
+
# @param [Array<Fixnum>] bytes Message data
|
125
149
|
# @param [Float] timestamp The system float timestamp
|
126
150
|
# @return [Array<Hash>] The resulting buffer
|
127
151
|
def enqueue_message(bytes, timestamp)
|
@@ -132,62 +156,61 @@ module CoreMIDI
|
|
132
156
|
@sysex_buffer.clear
|
133
157
|
end
|
134
158
|
end
|
135
|
-
|
136
|
-
@
|
137
|
-
end
|
159
|
+
message = get_message_formatted(bytes, timestamp)
|
160
|
+
@queue << message
|
138
161
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
162
|
+
@threads_sync_semaphore.synchronize do
|
163
|
+
@threads_waiting.each(&:run)
|
164
|
+
@threads_waiting.clear
|
165
|
+
end
|
143
166
|
|
144
|
-
|
145
|
-
def queued_messages?
|
146
|
-
@pointer < @buffer.length
|
167
|
+
message
|
147
168
|
end
|
148
169
|
|
149
|
-
# The callback fired by coremidi when new MIDI messages are
|
170
|
+
# The callback fired by coremidi when new MIDI messages are received
|
150
171
|
def get_event_callback
|
151
|
-
|
172
|
+
Thread.abort_on_exception = true
|
173
|
+
proc do |new_packets, _refCon_ptr, _connRefCon_ptr|
|
152
174
|
begin
|
153
|
-
# p "packets received: #{new_packets[:numPackets]}"
|
154
175
|
timestamp = Time.now.to_f
|
155
176
|
messages = get_messages(new_packets)
|
156
|
-
messages.each
|
157
|
-
|
158
|
-
|
177
|
+
messages.each do |message|
|
178
|
+
enqueue_message(message, timestamp)
|
179
|
+
end
|
180
|
+
rescue Exception => e
|
181
|
+
Thread.main.raise(e)
|
159
182
|
end
|
160
183
|
end
|
161
184
|
end
|
162
185
|
|
163
186
|
# Get MIDI messages from the given CoreMIDI packet list
|
164
187
|
# @param [API::MIDIPacketList] new_packets The packet list
|
165
|
-
# @return [Array<Array<
|
188
|
+
# @return [Array<Array<Fixnum>>] A collection of MIDI messages
|
166
189
|
def get_messages(packet_list)
|
167
190
|
count = packet_list[:numPackets]
|
168
191
|
first = packet_list[:packet][0]
|
169
192
|
data = first[:data].to_a
|
170
193
|
messages = []
|
171
194
|
messages << data.slice!(0, first[:length])
|
172
|
-
(count - 1).times do |
|
195
|
+
(count - 1).times do |_i|
|
173
196
|
length_index = find_next_length_index(data)
|
174
197
|
message_length = data[length_index]
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
198
|
+
next if message_length.nil?
|
199
|
+
|
200
|
+
packet_start_index = length_index + 2
|
201
|
+
packet_end_index = packet_start_index + message_length
|
202
|
+
next unless data.length >= packet_end_index + 1
|
203
|
+
|
204
|
+
packet = data.slice!(0..packet_end_index)
|
205
|
+
message = packet.slice(packet_start_index, message_length)
|
206
|
+
messages << message
|
184
207
|
end
|
185
208
|
messages
|
186
209
|
end
|
187
210
|
|
188
211
|
# Get the next index for "length" from the blob of MIDI data
|
189
|
-
# @param [Array<
|
190
|
-
# @return [
|
212
|
+
# @param [Array<Fixnum>] data
|
213
|
+
# @return [Fixnum]
|
191
214
|
def find_next_length_index(data)
|
192
215
|
last_is_zero = false
|
193
216
|
data.each_with_index do |num, i|
|
@@ -203,18 +226,12 @@ module CoreMIDI
|
|
203
226
|
end
|
204
227
|
end
|
205
228
|
|
206
|
-
# Timestamp for a received MIDI message
|
207
|
-
# @return [Integer]
|
208
|
-
def timestamp(now)
|
209
|
-
(now - @start_time) * 1000
|
210
|
-
end
|
211
|
-
|
212
229
|
# Give a message its timestamp and package it in a Hash
|
213
230
|
# @return [Hash]
|
214
231
|
def get_message_formatted(raw, time)
|
215
232
|
{
|
216
|
-
:
|
217
|
-
:
|
233
|
+
data: raw,
|
234
|
+
timestamp: time
|
218
235
|
}
|
219
236
|
end
|
220
237
|
|
@@ -225,6 +242,7 @@ module CoreMIDI
|
|
225
242
|
port = API.create_midi_input_port(@client, @resource_id, @name, @callback)
|
226
243
|
@handle = port[:handle]
|
227
244
|
raise "MIDIInputPortCreate returned error code #{port[:error]}" unless port[:error].zero?
|
245
|
+
|
228
246
|
true
|
229
247
|
end
|
230
248
|
|
@@ -239,7 +257,5 @@ module CoreMIDI
|
|
239
257
|
end
|
240
258
|
true
|
241
259
|
end
|
242
|
-
|
243
260
|
end
|
244
|
-
|
245
261
|
end
|
@@ -1,9 +1,9 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
module CoreMIDI
|
4
|
+
# Helper for converting MIDI data
|
4
5
|
module TypeConversion
|
5
|
-
|
6
|
-
extend self
|
6
|
+
module_function
|
7
7
|
|
8
8
|
# Convert an array of numeric byes to a hex string (e.g. [0x90, 0x40, 0x40] becomes "904040")
|
9
9
|
# @param [Array<Integer>] bytes
|
@@ -11,11 +11,10 @@ module CoreMIDI
|
|
11
11
|
def numeric_bytes_to_hex_string(bytes)
|
12
12
|
string_bytes = bytes.map do |byte|
|
13
13
|
str = byte.to_s(16).upcase
|
14
|
-
str = "0"
|
14
|
+
str = "0#{str}" if byte < 16
|
15
15
|
str
|
16
16
|
end
|
17
17
|
string_bytes.join
|
18
|
-
end
|
19
|
-
|
18
|
+
end
|
20
19
|
end
|
21
20
|
end
|
data/lib/coremidi.rb
CHANGED
@@ -1,26 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
#
|
2
4
|
# ffi-coremidi
|
3
5
|
# Realtime MIDI IO with Ruby for OSX
|
4
6
|
#
|
5
|
-
# (c)2011-
|
7
|
+
# (c)2011-2022 Ari Russo
|
6
8
|
# https://github.com/arirusso/ffi-coremidi
|
7
9
|
#
|
8
10
|
|
9
11
|
# Libs
|
10
|
-
require
|
11
|
-
require
|
12
|
+
require 'ffi'
|
13
|
+
require 'forwardable'
|
12
14
|
|
13
15
|
# Modules
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
16
|
+
require 'coremidi/api'
|
17
|
+
require 'coremidi/endpoint'
|
18
|
+
require 'coremidi/type_conversion'
|
17
19
|
|
18
20
|
# Classes
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
22
|
-
require
|
21
|
+
require 'coremidi/entity'
|
22
|
+
require 'coremidi/device'
|
23
|
+
require 'coremidi/source'
|
24
|
+
require 'coremidi/destination'
|
23
25
|
|
24
26
|
module CoreMIDI
|
25
|
-
VERSION =
|
27
|
+
VERSION = '0.5.1'
|
26
28
|
end
|
metadata
CHANGED
@@ -1,115 +1,95 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffi-coremidi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ari Russo
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: rake
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '13.0'
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version:
|
22
|
+
version: 13.0.6
|
23
23
|
type: :development
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - "~>"
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: '
|
29
|
+
version: '13.0'
|
30
30
|
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
32
|
+
version: 13.0.6
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
34
|
+
name: rspec
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
|
-
- - "~>"
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version: '1.1'
|
40
37
|
- - ">="
|
41
38
|
- !ruby/object:Gem::Version
|
42
|
-
version:
|
43
|
-
type: :development
|
44
|
-
prerelease: false
|
45
|
-
version_requirements: !ruby/object:Gem::Requirement
|
46
|
-
requirements:
|
47
|
-
- - "~>"
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: '1.1'
|
50
|
-
- - ">="
|
51
|
-
- !ruby/object:Gem::Version
|
52
|
-
version: 1.1.0
|
53
|
-
- !ruby/object:Gem::Dependency
|
54
|
-
name: rake
|
55
|
-
requirement: !ruby/object:Gem::Requirement
|
56
|
-
requirements:
|
39
|
+
version: 3.11.0
|
57
40
|
- - "~>"
|
58
41
|
- !ruby/object:Gem::Version
|
59
|
-
version: '
|
60
|
-
- - ">="
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
version: 10.4.2
|
42
|
+
version: '3.11'
|
63
43
|
type: :development
|
64
44
|
prerelease: false
|
65
45
|
version_requirements: !ruby/object:Gem::Requirement
|
66
46
|
requirements:
|
67
|
-
- - "~>"
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
version: '10.4'
|
70
47
|
- - ">="
|
71
48
|
- !ruby/object:Gem::Version
|
72
|
-
version:
|
49
|
+
version: 3.11.0
|
50
|
+
- - "~>"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '3.11'
|
73
53
|
- !ruby/object:Gem::Dependency
|
74
|
-
name:
|
54
|
+
name: rubocop
|
75
55
|
requirement: !ruby/object:Gem::Requirement
|
76
56
|
requirements:
|
77
|
-
- - "~>"
|
78
|
-
- !ruby/object:Gem::Version
|
79
|
-
version: '1.2'
|
80
57
|
- - ">="
|
81
58
|
- !ruby/object:Gem::Version
|
82
|
-
version: 1.
|
59
|
+
version: 1.10.0
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '1.10'
|
83
63
|
type: :development
|
84
64
|
prerelease: false
|
85
65
|
version_requirements: !ruby/object:Gem::Requirement
|
86
66
|
requirements:
|
87
|
-
- - "~>"
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '1.2'
|
90
67
|
- - ">="
|
91
68
|
- !ruby/object:Gem::Version
|
92
|
-
version: 1.
|
69
|
+
version: 1.10.0
|
70
|
+
- - "~>"
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '1.10'
|
93
73
|
- !ruby/object:Gem::Dependency
|
94
74
|
name: ffi
|
95
75
|
requirement: !ruby/object:Gem::Requirement
|
96
76
|
requirements:
|
97
77
|
- - "~>"
|
98
78
|
- !ruby/object:Gem::Version
|
99
|
-
version: '1.
|
79
|
+
version: '1.15'
|
100
80
|
- - ">="
|
101
81
|
- !ruby/object:Gem::Version
|
102
|
-
version: 1.
|
82
|
+
version: 1.15.5
|
103
83
|
type: :runtime
|
104
84
|
prerelease: false
|
105
85
|
version_requirements: !ruby/object:Gem::Requirement
|
106
86
|
requirements:
|
107
87
|
- - "~>"
|
108
88
|
- !ruby/object:Gem::Version
|
109
|
-
version: '1.
|
89
|
+
version: '1.15'
|
110
90
|
- - ">="
|
111
91
|
- !ruby/object:Gem::Version
|
112
|
-
version: 1.
|
92
|
+
version: 1.15.5
|
113
93
|
description: Perform realtime MIDI IO with Ruby for OSX
|
114
94
|
email:
|
115
95
|
- ari.russo@gmail.com
|
@@ -127,14 +107,11 @@ files:
|
|
127
107
|
- lib/coremidi/entity.rb
|
128
108
|
- lib/coremidi/source.rb
|
129
109
|
- lib/coremidi/type_conversion.rb
|
130
|
-
- test/helper.rb
|
131
|
-
- test/input_buffer_test.rb
|
132
|
-
- test/io_test.rb
|
133
110
|
homepage: http://github.com/arirusso/ffi-coremidi
|
134
111
|
licenses:
|
135
112
|
- Apache-2.0
|
136
113
|
metadata: {}
|
137
|
-
post_install_message:
|
114
|
+
post_install_message:
|
138
115
|
rdoc_options: []
|
139
116
|
require_paths:
|
140
117
|
- lib
|
@@ -147,11 +124,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
147
124
|
requirements:
|
148
125
|
- - ">="
|
149
126
|
- !ruby/object:Gem::Version
|
150
|
-
version:
|
127
|
+
version: '0'
|
151
128
|
requirements: []
|
152
|
-
|
153
|
-
|
154
|
-
signing_key:
|
129
|
+
rubygems_version: 3.0.9
|
130
|
+
signing_key:
|
155
131
|
specification_version: 4
|
156
132
|
summary: Realtime MIDI IO with Ruby for OSX
|
157
133
|
test_files: []
|
data/test/helper.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
dir = File.dirname(File.expand_path(__FILE__))
|
2
|
-
$LOAD_PATH.unshift dir + "/../lib"
|
3
|
-
|
4
|
-
require "minitest/autorun"
|
5
|
-
require "mocha/test_unit"
|
6
|
-
require "shoulda-context"
|
7
|
-
|
8
|
-
require "coremidi"
|
9
|
-
|
10
|
-
module TestHelper
|
11
|
-
|
12
|
-
extend self
|
13
|
-
|
14
|
-
def device
|
15
|
-
@device ||= select_devices
|
16
|
-
end
|
17
|
-
|
18
|
-
def select_devices
|
19
|
-
@device ||= {}
|
20
|
-
{ :input => CoreMIDI::Source.all, :output => CoreMIDI::Destination.all }.each do |type, devs|
|
21
|
-
puts ""
|
22
|
-
puts "select an #{type.to_s}..."
|
23
|
-
while @device[type].nil?
|
24
|
-
devs.each do |device|
|
25
|
-
puts "#{device.id}: #{device.name}"
|
26
|
-
end
|
27
|
-
selection = $stdin.gets.chomp
|
28
|
-
if selection != ""
|
29
|
-
selection = selection.to_i
|
30
|
-
@device[type] = devs.find { |d| d.id == selection }
|
31
|
-
puts "selected #{selection} for #{type.to_s}" unless @device[type]
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
@device
|
36
|
-
end
|
37
|
-
|
38
|
-
def bytestrs_to_ints(arr)
|
39
|
-
data = arr.map { |m| m[:data] }.join
|
40
|
-
output = []
|
41
|
-
until (bytestr = data.slice!(0,2)).eql?("")
|
42
|
-
output << bytestr.hex
|
43
|
-
end
|
44
|
-
output
|
45
|
-
end
|
46
|
-
|
47
|
-
# some MIDI messages
|
48
|
-
VariousMIDIMessages = [
|
49
|
-
[0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7], # SysEx
|
50
|
-
[0x90, 100, 100], # note on
|
51
|
-
[0x90, 43, 100], # note on
|
52
|
-
[0x90, 76, 100], # note on
|
53
|
-
[0x90, 60, 100], # note on
|
54
|
-
[0x80, 100, 100] # note off
|
55
|
-
]
|
56
|
-
|
57
|
-
# some MIDI messages
|
58
|
-
VariousMIDIByteStrMessages = [
|
59
|
-
"F04110421240007F0041F7", # SysEx
|
60
|
-
"906440", # note on
|
61
|
-
"804340" # note off
|
62
|
-
]
|
63
|
-
|
64
|
-
end
|
65
|
-
|
66
|
-
TestHelper.select_devices
|
data/test/input_buffer_test.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
require "helper"
|
2
|
-
|
3
|
-
class InputBufferTest < Minitest::Test
|
4
|
-
|
5
|
-
context "CoreMIDI" do
|
6
|
-
|
7
|
-
setup do
|
8
|
-
sleep(1)
|
9
|
-
end
|
10
|
-
|
11
|
-
context "Source#buffer" do
|
12
|
-
|
13
|
-
setup do
|
14
|
-
@messages = TestHelper::VariousMIDIMessages
|
15
|
-
@messages_arr = @messages.inject { |a,b| a+b }.flatten
|
16
|
-
@received_arr = []
|
17
|
-
@pointer = 0
|
18
|
-
|
19
|
-
@output = TestHelper.device[:output].open
|
20
|
-
@input = TestHelper.device[:input].open
|
21
|
-
@input.buffer.clear
|
22
|
-
end
|
23
|
-
|
24
|
-
should "have the correct messages in the buffer" do
|
25
|
-
bytes = []
|
26
|
-
@messages.each do |message|
|
27
|
-
puts "sending: #{message.inspect}"
|
28
|
-
@output.puts(message)
|
29
|
-
bytes += message
|
30
|
-
|
31
|
-
sleep(0.5)
|
32
|
-
|
33
|
-
buffer = @input.buffer.map { |m| m[:data] }.flatten
|
34
|
-
puts "received: #{buffer.to_s}"
|
35
|
-
assert_equal(bytes, buffer)
|
36
|
-
end
|
37
|
-
assert_equal(bytes.length, @input.buffer.map { |m| m[:data] }.flatten.length)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|
42
|
-
end
|
data/test/io_test.rb
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
require "helper"
|
2
|
-
|
3
|
-
class CoreMIDI::IOTest < Minitest::Test
|
4
|
-
|
5
|
-
# ** these tests assume that TestOutput is connected to TestInput
|
6
|
-
context "CoreMIDI" do
|
7
|
-
|
8
|
-
setup do
|
9
|
-
sleep(1)
|
10
|
-
end
|
11
|
-
|
12
|
-
context "full IO" do
|
13
|
-
|
14
|
-
context "using Arrays" do
|
15
|
-
|
16
|
-
setup do
|
17
|
-
@messages = TestHelper::VariousMIDIMessages
|
18
|
-
@messages_arr = @messages.inject { |a,b| a+b }.flatten
|
19
|
-
@received_arr = []
|
20
|
-
@pointer = 0
|
21
|
-
end
|
22
|
-
|
23
|
-
should "do IO" do
|
24
|
-
TestHelper.device[:output].open do |output|
|
25
|
-
TestHelper.device[:input].open do |input|
|
26
|
-
|
27
|
-
input.buffer.clear
|
28
|
-
|
29
|
-
@messages.each do |msg|
|
30
|
-
|
31
|
-
$>.puts "sending: " + msg.inspect
|
32
|
-
|
33
|
-
output.puts(msg)
|
34
|
-
sleep(1)
|
35
|
-
received = input.gets.map { |m| m[:data] }.flatten
|
36
|
-
|
37
|
-
$>.puts "received: " + received.inspect
|
38
|
-
|
39
|
-
assert_equal(@messages_arr.slice(@pointer, received.length), received)
|
40
|
-
@pointer += received.length
|
41
|
-
@received_arr += received
|
42
|
-
end
|
43
|
-
assert_equal(@messages_arr.length, @received_arr.length)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
context "using byte Strings" do
|
51
|
-
|
52
|
-
setup do
|
53
|
-
@messages = TestHelper::VariousMIDIByteStrMessages
|
54
|
-
@messages_str = @messages.join
|
55
|
-
@received_str = ""
|
56
|
-
@pointer = 0
|
57
|
-
end
|
58
|
-
|
59
|
-
should "do IO" do
|
60
|
-
TestHelper.device[:output].open do |output|
|
61
|
-
TestHelper.device[:input].open do |input|
|
62
|
-
|
63
|
-
@messages.each do |msg|
|
64
|
-
|
65
|
-
$>.puts "sending: " + msg.inspect
|
66
|
-
|
67
|
-
output.puts(msg)
|
68
|
-
sleep(1)
|
69
|
-
received = input.gets_bytestr.map { |m| m[:data] }.flatten.join
|
70
|
-
$>.puts "received: " + received.inspect
|
71
|
-
|
72
|
-
assert_equal(@messages_str.slice(@pointer, received.length), received)
|
73
|
-
@pointer += received.length
|
74
|
-
@received_str += received
|
75
|
-
end
|
76
|
-
assert_equal(@messages_str, @received_str)
|
77
|
-
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
|
-
end
|
87
|
-
|
88
|
-
end
|
89
|
-
|
90
|
-
end
|