ffi-coremidi 0.1.8 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +2 -2
- data/README.md +51 -0
- data/lib/coremidi.rb +2 -5
- data/lib/coremidi/destination.rb +13 -30
- data/lib/coremidi/device.rb +53 -41
- data/lib/coremidi/endpoint.rb +10 -15
- data/lib/coremidi/entity.rb +6 -8
- data/lib/coremidi/map.rb +13 -6
- data/lib/coremidi/source.rb +14 -20
- metadata +4 -4
- data/README.rdoc +0 -48
data/LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright 2011 Ari Russo
|
1
|
+
Copyright 2011-2013 Ari Russo
|
2
2
|
|
3
3
|
Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
you may not use this file except in compliance with the License.
|
@@ -10,4 +10,4 @@ Unless required by applicable law or agreed to in writing, software
|
|
10
10
|
distributed under the License is distributed on an "AS IS" BASIS,
|
11
11
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
12
|
See the License for the specific language governing permissions and
|
13
|
-
limitations under the License.
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# ffi-coremidi
|
2
|
+
|
3
|
+
#### Realtime MIDI IO with Ruby for OSX
|
4
|
+
|
5
|
+
This is a Ruby implementation of the Apple Core MIDI framework API.
|
6
|
+
|
7
|
+
Note that in the interest of allowing people on other platforms to utilize your code, please consider using [UniMIDI](http://github.com/arirusso/unimidi). UniMIDI is a platform independent wrapper which implements this library with a similar API.
|
8
|
+
|
9
|
+
### Features
|
10
|
+
|
11
|
+
* Simpler API than Core MIDI
|
12
|
+
* Input and output on multiple devices concurrently
|
13
|
+
* Agnostically handle different MIDI Message types (including SysEx)
|
14
|
+
* Timestamped input events
|
15
|
+
* Internally patch MIDI to other programs using IAC
|
16
|
+
|
17
|
+
### Requirements
|
18
|
+
|
19
|
+
* [ffi](http://github.com/ffi/ffi)
|
20
|
+
|
21
|
+
### Install
|
22
|
+
|
23
|
+
If you're using Bundler, add this line to your application's Gemfile:
|
24
|
+
|
25
|
+
`gem "ffi-coremidi"`
|
26
|
+
|
27
|
+
Otherwise
|
28
|
+
|
29
|
+
`gem install ffi-coremidi`
|
30
|
+
|
31
|
+
### Documentation
|
32
|
+
|
33
|
+
[rdoc](http://rubydoc.info/github/arirusso/ffi-coremidi)
|
34
|
+
|
35
|
+
### Author
|
36
|
+
|
37
|
+
[Ari Russo](http://github.com/arirusso) <ari.russo at gmail.com>
|
38
|
+
|
39
|
+
### Credits
|
40
|
+
|
41
|
+
This library began with some coremidi/ffi binding code for MIDI output by [Colin Harris](http://github.com/aberant) contained in [his fork of MIDIator](http://github.com/aberant/midiator) and a [blog post](http://aberant.tumblr.com/post/694878119/sending-midi-sysex-with-core-midi-and-ruby-ffi).
|
42
|
+
|
43
|
+
[MIDIator](http://github.com/bleything/midiator) is (c)2008 by Ben Bleything and Topher Cyll and released under the MIT license (see LICENSE.midiator and LICENSE.prp)
|
44
|
+
|
45
|
+
Also thank you to [Jeremy Voorhis](http://github.com/jvoorhis) for some useful debugging.
|
46
|
+
|
47
|
+
### License
|
48
|
+
|
49
|
+
Apache 2.0, See the file LICENSE
|
50
|
+
|
51
|
+
Copyright (c) 2011-2013 [Ari Russo](http://github.com/arirusso)
|
data/lib/coremidi.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
#
|
3
1
|
# ffi-coremidi
|
4
2
|
# Realtime MIDI IO with Ruby for OSX
|
5
|
-
# (c)2011 Ari Russo
|
6
|
-
#
|
3
|
+
# (c)2011-2013 Ari Russo
|
7
4
|
|
8
5
|
# libs
|
9
6
|
require 'ffi'
|
@@ -20,5 +17,5 @@ require 'coremidi/source'
|
|
20
17
|
require 'coremidi/destination'
|
21
18
|
|
22
19
|
module CoreMIDI
|
23
|
-
VERSION = "0.
|
20
|
+
VERSION = "0.2.0"
|
24
21
|
end
|
data/lib/coremidi/destination.rb
CHANGED
@@ -1,29 +1,18 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
1
|
module CoreMIDI
|
4
2
|
|
5
|
-
#
|
6
3
|
# Output/Destination endpoint class
|
7
|
-
#
|
8
4
|
class Destination
|
9
5
|
|
10
6
|
include Endpoint
|
11
7
|
|
12
8
|
attr_reader :entity
|
13
9
|
|
14
|
-
#
|
10
|
+
# Close this output
|
15
11
|
def close
|
16
|
-
#error = Map.MIDIClientDispose(@handle)
|
17
|
-
#raise "MIDIClientDispose returned error code #{error}" unless error.zero?
|
18
|
-
#error = Map.MIDIPortDispose(@handle)
|
19
|
-
#raise "MIDIPortDispose returned error code #{error}" unless error.zero?
|
20
|
-
#error = Map.MIDIEndpointDispose(@resource)
|
21
|
-
#raise "MIDIEndpointDispose returned error code #{error}" unless error.zero?
|
22
12
|
@enabled = false
|
23
|
-
|
24
13
|
end
|
25
14
|
|
26
|
-
#
|
15
|
+
# Send a MIDI message comprised of a String of hex digits
|
27
16
|
def puts_s(data)
|
28
17
|
data = data.dup
|
29
18
|
output = []
|
@@ -35,7 +24,7 @@ module CoreMIDI
|
|
35
24
|
alias_method :puts_bytestr, :puts_s
|
36
25
|
alias_method :puts_hex, :puts_s
|
37
26
|
|
38
|
-
#
|
27
|
+
# Send a MIDI messages comprised of Numeric bytes
|
39
28
|
def puts_bytes(*data)
|
40
29
|
|
41
30
|
format = "C" * data.size
|
@@ -49,7 +38,7 @@ module CoreMIDI
|
|
49
38
|
end
|
50
39
|
end
|
51
40
|
|
52
|
-
#
|
41
|
+
# Send a MIDI message of an indeterminant type
|
53
42
|
def puts(*a)
|
54
43
|
case a.first
|
55
44
|
when Array then puts_bytes(*a.first)
|
@@ -59,7 +48,7 @@ module CoreMIDI
|
|
59
48
|
end
|
60
49
|
alias_method :write, :puts
|
61
50
|
|
62
|
-
#
|
51
|
+
# Enable this device
|
63
52
|
def enable(options = {}, &block)
|
64
53
|
@enabled = true
|
65
54
|
if block_given?
|
@@ -75,29 +64,28 @@ module CoreMIDI
|
|
75
64
|
alias_method :open, :enable
|
76
65
|
alias_method :start, :enable
|
77
66
|
|
78
|
-
#
|
67
|
+
# Shortcut to the first output endpoint available
|
79
68
|
def self.first
|
80
69
|
Endpoint.first(:destination)
|
81
70
|
end
|
82
71
|
|
83
|
-
#
|
72
|
+
# Shortcut to the last output endpoint available
|
84
73
|
def self.last
|
85
74
|
Endpoint.last(:destination)
|
86
75
|
end
|
87
76
|
|
88
|
-
#
|
77
|
+
# All output endpoints
|
89
78
|
def self.all
|
90
79
|
Endpoint.all_by_type[:destination]
|
91
80
|
end
|
92
81
|
|
93
82
|
protected
|
94
83
|
|
95
|
-
#
|
84
|
+
# Base initialization for this endpoint -- done whether or not the endpoint is enabled to
|
96
85
|
# check whether it is truly available for use
|
97
86
|
def connect
|
98
87
|
client_error = enable_client
|
99
88
|
port_error = initialize_port
|
100
|
-
|
101
89
|
@resource = Map.MIDIEntityGetDestination( @entity.resource, @resource_id )
|
102
90
|
!@resource.address.zero? && client_error.zero? && port_error.zero?
|
103
91
|
end
|
@@ -105,24 +93,21 @@ module CoreMIDI
|
|
105
93
|
|
106
94
|
private
|
107
95
|
|
108
|
-
#
|
96
|
+
# Output a short MIDI message
|
109
97
|
def puts_small(bytes, size)
|
110
98
|
packet_list = FFI::MemoryPointer.new(256)
|
111
99
|
packet_ptr = Map.MIDIPacketListInit(packet_list)
|
112
|
-
|
113
100
|
if Map::SnowLeopard
|
114
101
|
packet_ptr = Map.MIDIPacketListAdd(packet_list, 256, packet_ptr, 0, size, bytes)
|
115
102
|
else
|
116
103
|
# Pass in two 32-bit 0s for the 64 bit time
|
117
104
|
packet_ptr = Map.MIDIPacketListAdd(packet_list, 256, packet_ptr, 0, 0, size, bytes)
|
118
105
|
end
|
119
|
-
|
120
106
|
Map.MIDISend( @handle, @resource, packet_list )
|
121
107
|
end
|
122
108
|
|
123
|
-
#
|
109
|
+
# Output a System Exclusive MIDI message
|
124
110
|
def puts_sysex(bytes, size)
|
125
|
-
|
126
111
|
request = Map::MIDISysexSendRequest.new
|
127
112
|
request[:destination] = @resource
|
128
113
|
request[:data] = bytes
|
@@ -130,17 +115,15 @@ module CoreMIDI
|
|
130
115
|
request[:complete] = 0
|
131
116
|
request[:completion_proc] = SysexCompletionCallback
|
132
117
|
request[:completion_ref_con] = request
|
133
|
-
|
134
118
|
Map.MIDISendSysex(request)
|
135
119
|
end
|
136
120
|
|
137
121
|
SysexCompletionCallback =
|
138
122
|
FFI::Function.new(:void, [:pointer]) do |sysex_request_ptr|
|
139
|
-
# this isn't working for some reason
|
140
|
-
# as of now, we don't need it though
|
123
|
+
# this isn't working for some reason. as of now, it's not needed though
|
141
124
|
end
|
142
125
|
|
143
|
-
#
|
126
|
+
# Initialize a coremidi port for this endpoint
|
144
127
|
def initialize_port
|
145
128
|
port_name = Map::CF.CFStringCreateWithCString(nil, "Port #{@resource_id}: #{name}", 0)
|
146
129
|
outport_ptr = FFI::MemoryPointer.new(:pointer)
|
data/lib/coremidi/device.rb
CHANGED
@@ -1,36 +1,44 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
1
|
module CoreMIDI
|
4
2
|
|
5
3
|
class Device
|
6
4
|
|
7
5
|
attr_reader :entities,
|
8
|
-
#
|
9
|
-
:
|
10
|
-
# device name from coremidi
|
11
|
-
:name
|
6
|
+
:id, # Unique Numeric id
|
7
|
+
:name # Device name from coremidi
|
12
8
|
|
13
9
|
def initialize(id, device_pointer, options = {})
|
14
|
-
include_if_offline = options
|
10
|
+
include_if_offline = options.fetch(:include_offline, false)
|
15
11
|
@id = id
|
16
12
|
@resource = device_pointer
|
17
13
|
@entities = []
|
18
|
-
|
19
|
-
prop = Map::CF.CFStringCreateWithCString( nil, "name", 0 )
|
20
|
-
name = Map::CF.CFStringCreateWithCString( nil, id.to_s, 0 )
|
21
|
-
Map::MIDIObjectGetStringProperty(@resource, prop, name)
|
22
|
-
|
23
|
-
@name = Map::CF.CFStringGetCStringPtr(name.read_pointer, 0).read_string
|
14
|
+
populate_name
|
24
15
|
populate_entities(:include_offline => include_if_offline)
|
25
16
|
end
|
17
|
+
|
18
|
+
# Endpoints for this device
|
19
|
+
# @return [Array<Endpoint>]
|
20
|
+
def endpoints
|
21
|
+
endpoints = { :source => [], :destination => [] }
|
22
|
+
endpoints.keys.each do |k|
|
23
|
+
endpoints[k] += entities.map { |entity| entity.endpoints[k] }.flatten
|
24
|
+
end
|
25
|
+
endpoints
|
26
|
+
end
|
26
27
|
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
28
|
+
# Assign all of this Device's endpoints an consecutive local id
|
29
|
+
# @param [Integer] last_id The highest already used endpoint ID
|
30
|
+
# @return [Integer] The highest used endpoint ID after populating this device's endpoints
|
31
|
+
def populate_endpoint_ids(last_id)
|
32
|
+
i = 0
|
33
|
+
entities.each { |entity| i += entity.populate_endpoint_ids(i + last_id) }
|
34
|
+
i
|
35
|
+
end
|
36
|
+
|
37
|
+
# All cached devices
|
38
|
+
# @param [Hash] options The options to select devices with
|
39
|
+
# @option options [Boolean] :cache If false, the device list will never be cached. This would be useful if one needs to alter the device list (e.g. plug in a USB MIDI interface) while their program is running.
|
40
|
+
# @option options [Boolean] :include_offline If true, devices marked offline by coremidi will be included in the list
|
41
|
+
# @return [Array<Device>] All cached devices
|
34
42
|
def self.all(options = {})
|
35
43
|
use_cache = options[:cache] || true
|
36
44
|
include_offline = options[:include_offline] || false
|
@@ -46,38 +54,42 @@ module CoreMIDI
|
|
46
54
|
end
|
47
55
|
@devices
|
48
56
|
end
|
49
|
-
|
50
|
-
# Refresh the Device
|
51
|
-
#
|
57
|
+
|
58
|
+
# Refresh the Device cache. This is needed if, for example a USB MIDI device is plugged in while the program is running
|
59
|
+
# @return [Array<Device>] The Device cache
|
52
60
|
def self.refresh
|
53
61
|
@devices.clear
|
54
|
-
|
55
|
-
|
56
|
-
# returns all of the Endpoints for this device
|
57
|
-
def endpoints
|
58
|
-
endpoints = { :source => [], :destination => [] }
|
59
|
-
endpoints.keys.each do |k|
|
60
|
-
endpoints[k] += entities.map { |entity| entity.endpoints[k] }.flatten
|
61
|
-
end
|
62
|
-
endpoints
|
63
|
-
end
|
64
|
-
|
65
|
-
# assign all of this Device's endpoints an consecutive local id
|
66
|
-
def populate_endpoint_ids(starting_id)
|
67
|
-
i = 0
|
68
|
-
entities.each { |entity| i += entity.populate_endpoint_ids(i + starting_id) }
|
69
|
-
i
|
62
|
+
@devices
|
70
63
|
end
|
71
64
|
|
72
65
|
private
|
66
|
+
|
67
|
+
# Populate the device name
|
68
|
+
def populate_name
|
69
|
+
prop = Map::CF.CFStringCreateWithCString( nil, "name", 0 )
|
70
|
+
|
71
|
+
begin
|
72
|
+
name_ptr = FFI::MemoryPointer.new(:pointer)
|
73
|
+
Map::MIDIObjectGetStringProperty(@resource, prop, name_ptr)
|
74
|
+
name = name_ptr.read_pointer
|
75
|
+
len = Map::CF.CFStringGetMaximumSizeForEncoding(Map::CF.CFStringGetLength(name), :kCFStringEncodingUTF8)
|
76
|
+
bytes = FFI::MemoryPointer.new(len + 1)
|
77
|
+
raise RuntimeError.new("CFStringGetCString") unless Map::CF.CFStringGetCString(name, bytes, len, :kCFStringEncodingUTF8)
|
78
|
+
@name = bytes.read_string
|
79
|
+
ensure
|
80
|
+
Map::CF.CFRelease(name) unless name.nil? || name.null?
|
81
|
+
Map::CF.CFRelease(prop) unless prop.null?
|
82
|
+
end
|
83
|
+
end
|
73
84
|
|
74
|
-
#
|
85
|
+
# All of the endpoints for all devices a consecutive local id
|
75
86
|
def self.populate_endpoint_ids
|
76
87
|
i = 0
|
77
88
|
all.each { |device| i += device.populate_endpoint_ids(i) }
|
78
89
|
end
|
79
90
|
|
80
|
-
#
|
91
|
+
# Populates the entities for this device. These entities are in turn used to gather the endpoints.
|
92
|
+
#
|
81
93
|
def populate_entities(options = {})
|
82
94
|
include_if_offline = options[:include_offline] || false
|
83
95
|
i = 0
|
data/lib/coremidi/endpoint.rb
CHANGED
@@ -1,18 +1,13 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
1
|
module CoreMIDI
|
4
2
|
|
5
3
|
module Endpoint
|
6
4
|
|
7
5
|
extend Forwardable
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
:entity,
|
12
|
-
# unique local Numeric id of the endpoint
|
7
|
+
attr_reader :enabled, # has the endpoint been initialized?
|
8
|
+
:entity, # unique local Numeric id of the endpoint
|
13
9
|
:id,
|
14
|
-
:resource_id,
|
15
|
-
# :input or :output
|
10
|
+
:resource_id, # :input or :output
|
16
11
|
:type
|
17
12
|
|
18
13
|
def_delegators :entity, :manufacturer, :model, :name
|
@@ -26,27 +21,27 @@ module CoreMIDI
|
|
26
21
|
@enabled = false
|
27
22
|
end
|
28
23
|
|
29
|
-
#
|
24
|
+
# Is this endpoint online?
|
30
25
|
def online?
|
31
26
|
@entity.online? && connect?
|
32
27
|
end
|
33
28
|
|
34
|
-
#
|
29
|
+
# Set the id for this endpoint (the id is immutable once its set)
|
35
30
|
def id=(val)
|
36
31
|
@id ||= val
|
37
32
|
end
|
38
33
|
|
39
|
-
#
|
34
|
+
# Select the first endpoint of the specified type
|
40
35
|
def self.first(type)
|
41
36
|
all_by_type[type].first
|
42
37
|
end
|
43
38
|
|
44
|
-
#
|
39
|
+
# Select the last endpoint of the specified type
|
45
40
|
def self.last(type)
|
46
41
|
all_by_type[type].last
|
47
42
|
end
|
48
43
|
|
49
|
-
#
|
44
|
+
# A Hash of :source and :destination endpoints
|
50
45
|
def self.all_by_type
|
51
46
|
{
|
52
47
|
:source => Device.all.map { |d| d.endpoints[:source] }.flatten,
|
@@ -54,14 +49,14 @@ module CoreMIDI
|
|
54
49
|
}
|
55
50
|
end
|
56
51
|
|
57
|
-
#
|
52
|
+
# All endpoints of both types
|
58
53
|
def self.all
|
59
54
|
Device.all.map { |d| d.endpoints }.flatten
|
60
55
|
end
|
61
56
|
|
62
57
|
protected
|
63
58
|
|
64
|
-
#
|
59
|
+
# Enables the coremidi MIDI client that will go with this endpoint
|
65
60
|
def enable_client
|
66
61
|
client_name = Map::CF.CFStringCreateWithCString( nil, "Client #{@resource_id} #{name}", 0 )
|
67
62
|
client_ptr = FFI::MemoryPointer.new(:pointer)
|
data/lib/coremidi/entity.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
1
|
module CoreMIDI
|
4
2
|
|
5
3
|
class Entity
|
@@ -23,7 +21,7 @@ module CoreMIDI
|
|
23
21
|
@endpoints.keys.each { |type| populate_endpoints(type) }
|
24
22
|
end
|
25
23
|
|
26
|
-
#
|
24
|
+
# Assign all of this Entity's endpoints an consecutive local id
|
27
25
|
def populate_endpoint_ids(starting_id)
|
28
26
|
i = 0
|
29
27
|
@endpoints.values.flatten.each do |e|
|
@@ -35,7 +33,7 @@ module CoreMIDI
|
|
35
33
|
|
36
34
|
private
|
37
35
|
|
38
|
-
#
|
36
|
+
# Populate endpoints of a specified type for this entity
|
39
37
|
def populate_endpoints(type, options = {})
|
40
38
|
include_if_offline = options[:include_offline] || false
|
41
39
|
endpoint_class = case type
|
@@ -50,7 +48,7 @@ module CoreMIDI
|
|
50
48
|
@endpoints[type].size
|
51
49
|
end
|
52
50
|
|
53
|
-
#
|
51
|
+
# The number of endpoints for this entity
|
54
52
|
def number_of_endpoints(type)
|
55
53
|
case type
|
56
54
|
when :source then Map.MIDIEntityGetNumberOfSources(@resource)
|
@@ -58,7 +56,7 @@ module CoreMIDI
|
|
58
56
|
end
|
59
57
|
end
|
60
58
|
|
61
|
-
#
|
59
|
+
# A CFString property
|
62
60
|
def get_string(name, pointer)
|
63
61
|
prop = Map::CF.CFStringCreateWithCString( nil, name.to_s, 0 )
|
64
62
|
val = Map::CF.CFStringCreateWithCString( nil, name.to_s, 0 ) # placeholder
|
@@ -66,7 +64,7 @@ module CoreMIDI
|
|
66
64
|
Map::CF.CFStringGetCStringPtr(val.read_pointer, 0).read_string rescue nil
|
67
65
|
end
|
68
66
|
|
69
|
-
#
|
67
|
+
# An Integer property
|
70
68
|
def get_int(name, pointer)
|
71
69
|
prop = Map::CF.CFStringCreateWithCString( nil, name.to_s, 0 )
|
72
70
|
val = FFI::MemoryPointer.new(:pointer, 32)
|
@@ -74,7 +72,7 @@ module CoreMIDI
|
|
74
72
|
val.read_int
|
75
73
|
end
|
76
74
|
|
77
|
-
#
|
75
|
+
# A CString or Integer property from this Endpoint's entity
|
78
76
|
def get_property(name, options = {})
|
79
77
|
from = options[:from] || @resource
|
80
78
|
type = options[:type] || :string
|
data/lib/coremidi/map.rb
CHANGED
@@ -1,11 +1,6 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
1
|
module CoreMIDI
|
4
2
|
|
5
|
-
#
|
6
|
-
# coremidi binding
|
7
|
-
#
|
8
|
-
#
|
3
|
+
# Coremidi C binding
|
9
4
|
module Map
|
10
5
|
|
11
6
|
extend FFI::Library
|
@@ -122,11 +117,23 @@ module CoreMIDI
|
|
122
117
|
extend FFI::Library
|
123
118
|
ffi_lib '/System/Library/Frameworks/CoreFoundation.framework/Versions/Current/CoreFoundation'
|
124
119
|
|
120
|
+
typedef :pointer, :CFStringRef
|
121
|
+
typedef :long, :CFIndex
|
122
|
+
enum :CFStringEncoding, [ :kCFStringEncodingUTF8, 0x08000100 ]
|
123
|
+
|
125
124
|
# CFString* CFStringCreateWithCString( ?, CString, encoding)
|
126
125
|
attach_function :CFStringCreateWithCString, [:pointer, :string, :int], :pointer
|
127
126
|
# CString* CFStringGetCStringPtr(CFString*, encoding)
|
128
127
|
attach_function :CFStringGetCStringPtr, [:pointer, :int], :pointer
|
129
128
|
|
129
|
+
attach_function :CFStringGetLength, [ :CFStringRef ], :CFIndex
|
130
|
+
|
131
|
+
attach_function :CFStringGetMaximumSizeForEncoding, [ :CFIndex, :CFStringEncoding ], :long
|
132
|
+
|
133
|
+
attach_function :CFStringGetCString, [ :CFStringRef, :pointer, :CFIndex, :CFStringEncoding ], :bool
|
134
|
+
|
135
|
+
attach_function :CFRelease, [ :pointer ], :void
|
136
|
+
|
130
137
|
end
|
131
138
|
|
132
139
|
module HostTime
|
data/lib/coremidi/source.rb
CHANGED
@@ -1,10 +1,6 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
1
|
module CoreMIDI
|
4
2
|
|
5
|
-
#
|
6
3
|
# Input/Source endpoint class
|
7
|
-
#
|
8
4
|
class Source
|
9
5
|
|
10
6
|
include Endpoint
|
@@ -63,7 +59,7 @@ module CoreMIDI
|
|
63
59
|
alias_method :open, :enable
|
64
60
|
alias_method :start, :enable
|
65
61
|
|
66
|
-
#
|
62
|
+
# Close this input
|
67
63
|
def close
|
68
64
|
#error = Map.MIDIPortDisconnectSource( @handle, @resource )
|
69
65
|
#raise "MIDIPortDisconnectSource returned error code #{error}" unless error.zero?
|
@@ -76,25 +72,25 @@ module CoreMIDI
|
|
76
72
|
@enabled = false
|
77
73
|
end
|
78
74
|
|
79
|
-
#
|
75
|
+
# Shortcut to the first available input endpoint
|
80
76
|
def self.first
|
81
77
|
Endpoint.first(:source)
|
82
78
|
end
|
83
79
|
|
84
|
-
#
|
80
|
+
# Shortcut to the last available input endpoint
|
85
81
|
def self.last
|
86
82
|
Endpoint.last(:source)
|
87
83
|
end
|
88
84
|
|
89
|
-
#
|
85
|
+
# All input endpoints
|
90
86
|
def self.all
|
91
87
|
Endpoint.all_by_type[:source]
|
92
88
|
end
|
93
89
|
|
94
90
|
protected
|
95
91
|
|
96
|
-
#
|
97
|
-
#
|
92
|
+
# Base initialization for this endpoint -- done whether or not the endpoint is enabled to check whether
|
93
|
+
# it is truly available for use
|
98
94
|
def connect
|
99
95
|
enable_client
|
100
96
|
initialize_port
|
@@ -110,17 +106,17 @@ module CoreMIDI
|
|
110
106
|
|
111
107
|
private
|
112
108
|
|
113
|
-
#
|
109
|
+
# New MIDI messages from the queue
|
114
110
|
def queued_messages
|
115
111
|
@buffer.slice(@pointer, @buffer.length - @pointer)
|
116
112
|
end
|
117
113
|
|
118
|
-
#
|
114
|
+
# Are there new MIDI messages in the queue?
|
119
115
|
def queued_messages?
|
120
116
|
@pointer < @buffer.length
|
121
117
|
end
|
122
118
|
|
123
|
-
#
|
119
|
+
# The callback fired by coremidi when new MIDI messages are in the buffer
|
124
120
|
def get_event_callback
|
125
121
|
Proc.new do | new_packets, refCon_ptr, connRefCon_ptr |
|
126
122
|
time = Time.now.to_f
|
@@ -142,17 +138,17 @@ module CoreMIDI
|
|
142
138
|
end
|
143
139
|
end
|
144
140
|
|
145
|
-
#
|
141
|
+
# Timestamp for a received MIDI message
|
146
142
|
def timestamp(now)
|
147
143
|
((now - @start_time) * 1000)
|
148
144
|
end
|
149
145
|
|
150
|
-
#
|
146
|
+
# Give a message its timestamp and package it in a Hash
|
151
147
|
def get_message_formatted(raw, time)
|
152
148
|
{ :data => raw, :timestamp => timestamp(time) }
|
153
149
|
end
|
154
150
|
|
155
|
-
#
|
151
|
+
# Initialize a coremidi port for this endpoint
|
156
152
|
def initialize_port
|
157
153
|
port_name = Map::CF.CFStringCreateWithCString(nil, "Port #{@resource_id}: #{name}", 0)
|
158
154
|
handle_ptr = FFI::MemoryPointer.new(:pointer)
|
@@ -162,7 +158,7 @@ module CoreMIDI
|
|
162
158
|
raise "MIDIInputPortCreate returned error code #{error}" unless error.zero?
|
163
159
|
end
|
164
160
|
|
165
|
-
#
|
161
|
+
# Initialize the MIDI message buffer
|
166
162
|
def initialize_buffer
|
167
163
|
@pointer = 0
|
168
164
|
@buffer = []
|
@@ -172,9 +168,7 @@ module CoreMIDI
|
|
172
168
|
end
|
173
169
|
end
|
174
170
|
|
175
|
-
#
|
176
|
-
# e.g.
|
177
|
-
# [0x90, 0x40, 0x40] -> "904040"
|
171
|
+
# Convert an array of numeric byes to a hex string (e.g. [0x90, 0x40, 0x40] becomes "904040")
|
178
172
|
def numeric_bytes_to_hex_string(bytes)
|
179
173
|
bytes.map { |b| s = b.to_s(16).upcase; b < 16 ? s = "0" + s : s; s }.join
|
180
174
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffi-coremidi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-06-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi
|
@@ -45,7 +45,7 @@ files:
|
|
45
45
|
- test/test_input_buffer.rb
|
46
46
|
- test/test_io.rb
|
47
47
|
- LICENSE
|
48
|
-
- README.
|
48
|
+
- README.md
|
49
49
|
homepage: http://github.com/arirusso/ffi-coremidi
|
50
50
|
licenses: []
|
51
51
|
post_install_message:
|
@@ -66,7 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
66
|
version: 1.3.6
|
67
67
|
requirements: []
|
68
68
|
rubyforge_project: ffi-coremidi
|
69
|
-
rubygems_version: 1.8.
|
69
|
+
rubygems_version: 1.8.25
|
70
70
|
signing_key:
|
71
71
|
specification_version: 3
|
72
72
|
summary: Realtime MIDI IO with Ruby for OSX
|
data/README.rdoc
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
= ffi-coremidi
|
2
|
-
|
3
|
-
== Summary
|
4
|
-
|
5
|
-
Realtime MIDI IO with Ruby for OSX
|
6
|
-
|
7
|
-
Note that in the interest of allowing people on other platforms to utilize your code, you should consider using {unimidi}[http://github.com/arirusso/unimidi]. Unimidi is a platform independent wrapper that implements this library and has a similar API.
|
8
|
-
|
9
|
-
== Features
|
10
|
-
|
11
|
-
* Simplified API
|
12
|
-
* Input and output on multiple devices concurrently
|
13
|
-
* Agnostically handle different MIDI Message types (including SysEx)
|
14
|
-
* Timestamped input events
|
15
|
-
* Internally patch MIDI to other programs using IAC (and {MIDI Patch Bay}[http://notahat.com/midi_patchbay] if you're using OSX Snow Leopard or earlier)
|
16
|
-
== Requirements
|
17
|
-
|
18
|
-
* {ffi}[http://github.com/ffi/ffi] (gem install ffi)
|
19
|
-
|
20
|
-
== Install
|
21
|
-
|
22
|
-
gem install ffi-coremidi
|
23
|
-
|
24
|
-
== Documentation
|
25
|
-
|
26
|
-
* {rdoc}[http://rubydoc.info/github/arirusso/ffi-coremidi]
|
27
|
-
|
28
|
-
== Author
|
29
|
-
|
30
|
-
* {Ari Russo}[http://github.com/arirusso] <ari.russo at gmail.com>
|
31
|
-
|
32
|
-
== Credits
|
33
|
-
|
34
|
-
This library began with some coremidi/ffi binding code for MIDI output by
|
35
|
-
|
36
|
-
* Colin Harris -- http://github.com/aberant
|
37
|
-
|
38
|
-
contained in {his fork of MIDIator}[http://github.com/aberant/midiator] and a {blog post}[http://aberant.tumblr.com/post/694878119/sending-midi-sysex-with-core-midi-and-ruby-ffi]
|
39
|
-
|
40
|
-
{MIDIator}[http://github.com/bleything/midiator] is (c)2008 by Ben Bleything and Topher Cyll and released under the MIT license (see LICENSE.midiator and LICENSE.prp)
|
41
|
-
|
42
|
-
Also thank you to {Jeremy Voorhis}[http://github.com/jvoorhis] for some useful debugging
|
43
|
-
|
44
|
-
== License
|
45
|
-
|
46
|
-
Apache 2.0, See the file LICENSE
|
47
|
-
|
48
|
-
Copyright (c) 2011 Ari Russo
|