ffi-coremidi 0.1.8 → 0.2.0
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.
- 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
|