unimidi 0.3.5 → 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +3 -3
- data/lib/unimidi.rb +16 -26
- data/lib/unimidi/adapter/alsa-rawmidi.rb +22 -17
- data/lib/unimidi/adapter/ffi-coremidi.rb +21 -25
- data/lib/unimidi/adapter/midi-jruby.rb +21 -16
- data/lib/unimidi/adapter/midi-winmm.rb +21 -16
- data/lib/unimidi/command.rb +26 -0
- data/lib/unimidi/device.rb +182 -0
- data/lib/unimidi/input.rb +113 -0
- data/lib/unimidi/loader.rb +30 -0
- data/lib/unimidi/output.rb +63 -0
- data/lib/unimidi/platform.rb +11 -10
- data/lib/unimidi/type_conversion.rb +1 -1
- data/test/adapter_test.rb +70 -0
- data/test/class_methods_test.rb +99 -0
- data/test/functional_test.rb +128 -0
- data/test/helper.rb +20 -25
- data/test/input_test.rb +46 -0
- data/test/platform_test.rb +41 -23
- data/test/type_conversion_test.rb +14 -6
- metadata +16 -11
- data/lib/unimidi/congruous_api_adapter.rb +0 -321
- data/test/congruous_api_adapter_test.rb +0 -36
- data/test/input_buffer_test.rb +0 -40
- data/test/io_test.rb +0 -115
- data/test/selectors_test.rb +0 -59
@@ -0,0 +1,113 @@
|
|
1
|
+
module UniMIDI
|
2
|
+
|
3
|
+
# A MIDI input device
|
4
|
+
class Input
|
5
|
+
|
6
|
+
extend Device::ClassMethods
|
7
|
+
include Device::InstanceMethods
|
8
|
+
|
9
|
+
# All MIDI input devices -- used to populate the class
|
10
|
+
# @return [Array<Input>]
|
11
|
+
def self.all
|
12
|
+
Loader.devices(:direction => :input)
|
13
|
+
end
|
14
|
+
|
15
|
+
# The device buffer
|
16
|
+
# @return [Array<Hash>]
|
17
|
+
def buffer
|
18
|
+
@device.buffer
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# Plucks data from the input buffer and returns it as array of MIDI event hashes as such:
|
23
|
+
# [
|
24
|
+
# { :data => [144, 60, 100], :timestamp => 1024 },
|
25
|
+
# { :data => [128, 60, 100], :timestamp => 1100 },
|
26
|
+
# { :data => [144, 40, 120], :timestamp => 1200 }
|
27
|
+
# ]
|
28
|
+
#
|
29
|
+
# In this case, the data is an array of Numeric bytes
|
30
|
+
# The timestamp is the number of millis since this input was enabled
|
31
|
+
# Arguments are passed to the underlying device object
|
32
|
+
#
|
33
|
+
# @param [*Object] args
|
34
|
+
# @return [Array<Hash>]
|
35
|
+
def gets(*args)
|
36
|
+
@device.gets(*args)
|
37
|
+
rescue SystemExit, Interrupt
|
38
|
+
exit
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Plucks data from the input buffer and returns it as array of MIDI event hashes.
|
43
|
+
# Similar to Input#gets except that the returned message data as string of hex digits eg:
|
44
|
+
# [
|
45
|
+
# { :data => "904060", :timestamp => 904 },
|
46
|
+
# { :data => "804060", :timestamp => 1150 },
|
47
|
+
# { :data => "90447F", :timestamp => 1300 }
|
48
|
+
# ]
|
49
|
+
#
|
50
|
+
# @param [*Object] args
|
51
|
+
# @return [Array<Hash>]
|
52
|
+
def gets_s(*args)
|
53
|
+
@device.gets_s(*args)
|
54
|
+
rescue SystemExit, Interrupt
|
55
|
+
exit
|
56
|
+
end
|
57
|
+
alias_method :gets_bytestr, :gets_s
|
58
|
+
alias_method :gets_hex, :gets_s
|
59
|
+
|
60
|
+
#
|
61
|
+
# Plucks data from the input buffer and returns it as an array of data bytes such as
|
62
|
+
# [144, 60, 100, 128, 60, 100, 144, 40, 120]
|
63
|
+
#
|
64
|
+
# @param [*Object] args
|
65
|
+
# @return [Array<Fixnum>]
|
66
|
+
def gets_data(*args)
|
67
|
+
arr = gets(*args)
|
68
|
+
arr.map { |msg| msg[:data] }.inject(:+)
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
# Plucks data from the input buffer and returns it as a string of data such as
|
73
|
+
# "90406080406090447F"
|
74
|
+
#
|
75
|
+
# @param [*Object] args
|
76
|
+
# @return [String]
|
77
|
+
def gets_data_s(*args)
|
78
|
+
arr = gets_bytestr(*args)
|
79
|
+
arr.map { |msg| msg[:data] }.join
|
80
|
+
end
|
81
|
+
alias_method :gets_data_bytestr, :gets_data_s
|
82
|
+
alias_method :gets_data_hex, :gets_data_s
|
83
|
+
|
84
|
+
# Clears the input buffer
|
85
|
+
# @return [Array]
|
86
|
+
def clear_buffer
|
87
|
+
@device.buffer.clear
|
88
|
+
end
|
89
|
+
|
90
|
+
# Gets any messages in the buffer in the same format as Input#gets, without removing them from the buffer
|
91
|
+
# @param [*Object] args
|
92
|
+
# @return [Array<Hash>]
|
93
|
+
def gets_buffer(*args)
|
94
|
+
@device.buffer
|
95
|
+
end
|
96
|
+
|
97
|
+
# Gets any messages in the buffer in the same format as Input#gets_s, without removing them from the buffer
|
98
|
+
# @param [*Object] args
|
99
|
+
# @return [Array<Hash>]
|
100
|
+
def gets_buffer_s(*args)
|
101
|
+
@device.buffer.map { |msg| msg[:data] = TypeConversion.numeric_byte_array_to_hex_string(msg[:data]); msg }
|
102
|
+
end
|
103
|
+
|
104
|
+
# Gets any messages in the buffer in the same format as Input#gets_data without removing them from the buffer
|
105
|
+
# @param [*Object] args
|
106
|
+
# @return [Array<Fixnum>]
|
107
|
+
def gets_buffer_data(*args)
|
108
|
+
@device.buffer.map { |msg| msg[:data] }
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module UniMIDI
|
2
|
+
|
3
|
+
# Populate UniMIDI devices using the underlying device objects from the platform-specific gems
|
4
|
+
module Loader
|
5
|
+
|
6
|
+
extend self
|
7
|
+
|
8
|
+
# Use the given platform-specific adapter to load devices
|
9
|
+
# @param [UniMIDI::Adapter::Loader] loader
|
10
|
+
def use(loader)
|
11
|
+
@loader = loader
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param [Hash] options
|
15
|
+
# @option options [Symbol] :direction Return only a particular direction of device eg :input, :output
|
16
|
+
# @return [Array<Input>, Array<Output>]
|
17
|
+
def devices(options = {})
|
18
|
+
if @devices.nil?
|
19
|
+
inputs = @loader.inputs.map { |device| ::UniMIDI::Input.new(device) }
|
20
|
+
outputs = @loader.outputs.map { |device| ::UniMIDI::Output.new(device) }
|
21
|
+
@devices = {
|
22
|
+
:input => inputs,
|
23
|
+
:output => outputs
|
24
|
+
}
|
25
|
+
end
|
26
|
+
options[:direction].nil? ? @devices.values.flatten : @devices[options[:direction]]
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module UniMIDI
|
2
|
+
|
3
|
+
# A MIDI output device
|
4
|
+
class Output
|
5
|
+
|
6
|
+
extend Device::ClassMethods
|
7
|
+
include Device::InstanceMethods
|
8
|
+
|
9
|
+
# All MIDI output devices -- used to populate the class
|
10
|
+
# @return [Array<Output>]
|
11
|
+
def self.all
|
12
|
+
Loader.devices(:direction => :output)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Sends a message to the output.
|
16
|
+
#
|
17
|
+
# The message format can be:
|
18
|
+
#
|
19
|
+
# 1. Numeric bytes eg output.puts(0x90, 0x40, 0x40)
|
20
|
+
# 2. An array of numeric bytes [0x90, 0x40, 0x40]
|
21
|
+
# 3. A string of bytes eg "904040"
|
22
|
+
# 4. An array of strings ["904040", "804040"]
|
23
|
+
#
|
24
|
+
# @param [*Array<Fixnum>, *Array<String>, *Fixnum, *String] messages
|
25
|
+
# @return [Array<Fixnum>, Array<String>]
|
26
|
+
def puts(*messages)
|
27
|
+
message = messages.first
|
28
|
+
case message
|
29
|
+
when Array then message.each { |message| puts(*message) }
|
30
|
+
when Fixnum then puts_bytes(*messages)
|
31
|
+
when String then puts_s(*messages)
|
32
|
+
else
|
33
|
+
if message.respond_to?(:to_bytes)
|
34
|
+
puts_bytes(*message.to_bytes.flatten)
|
35
|
+
elsif message.respond_to?(:to_a)
|
36
|
+
puts_bytes(*message.to_a.flatten)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Sends a message to the output in a form of a string eg "904040". This method does not do
|
42
|
+
# type checking
|
43
|
+
# @param [*String] messages
|
44
|
+
# @return [Array<String>, Array<Array<String>>]
|
45
|
+
def puts_s(*messages)
|
46
|
+
@device.puts_s(*messages)
|
47
|
+
messages.count < 2 ? messages[0] : messages
|
48
|
+
end
|
49
|
+
alias_method :puts_bytestr, :puts_s
|
50
|
+
alias_method :puts_hex, :puts_s
|
51
|
+
|
52
|
+
# Sends a message to the output in a form of bytes eg output.puts_bytes(0x90, 0x40, 0x40).
|
53
|
+
# This method does not do type checking.
|
54
|
+
# @param [*Array<Fixnum>] messages
|
55
|
+
# @return [Array<Fixnum>, Array<Array<Fixnum>>]
|
56
|
+
def puts_bytes(*messages)
|
57
|
+
@device.puts_bytes(*messages)
|
58
|
+
messages.count < 2 ? messages[0] : messages
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
data/lib/unimidi/platform.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
module UniMIDI
|
2
|
-
|
3
|
-
class Platform
|
4
2
|
|
5
|
-
|
3
|
+
# Deal with different dependencies between different user environments
|
4
|
+
module Platform
|
6
5
|
|
7
|
-
|
6
|
+
extend self
|
8
7
|
|
9
|
-
|
8
|
+
# Loads the proper MIDI library and adapter for the user's environment
|
9
|
+
def bootstrap
|
10
10
|
lib = case RUBY_PLATFORM
|
11
11
|
when /darwin/ then "ffi-coremidi"
|
12
12
|
when /java/ then "midi-jruby"
|
@@ -14,12 +14,13 @@ module UniMIDI
|
|
14
14
|
when /mingw/ then "midi-winmm"
|
15
15
|
end
|
16
16
|
require("unimidi/adapter/#{lib}")
|
17
|
-
|
18
|
-
when /darwin/ then
|
19
|
-
when /java/ then
|
20
|
-
when /linux/ then
|
21
|
-
when /mingw/ then
|
17
|
+
interface = case RUBY_PLATFORM
|
18
|
+
when /darwin/ then Adapter::CoreMIDI
|
19
|
+
when /java/ then Adapter::MIDIJRuby
|
20
|
+
when /linux/ then Adapter::AlsaRawMIDI
|
21
|
+
when /mingw/ then Adapter::MIDIWinMM
|
22
22
|
end
|
23
|
+
Loader.use(interface::Loader)
|
23
24
|
end
|
24
25
|
|
25
26
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class UniMIDI::AdapterTest < UniMIDI::TestCase
|
4
|
+
|
5
|
+
context "Adapter" do
|
6
|
+
|
7
|
+
setup do
|
8
|
+
TestDeviceHelper.setup
|
9
|
+
end
|
10
|
+
|
11
|
+
context "Device#type" do
|
12
|
+
|
13
|
+
should "be an input" do
|
14
|
+
input = TestDeviceHelper.devices[:input]
|
15
|
+
assert_equal(:input, input.type)
|
16
|
+
end
|
17
|
+
|
18
|
+
should "be an output" do
|
19
|
+
output = TestDeviceHelper.devices[:output]
|
20
|
+
assert_equal(:output, output.type)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
context "Device.count" do
|
26
|
+
|
27
|
+
setup do
|
28
|
+
@inputs = Input.all
|
29
|
+
end
|
30
|
+
|
31
|
+
should "count all of the inputs" do
|
32
|
+
assert_equal @inputs.count, Input.count
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
context "Device.find_by_name" do
|
38
|
+
|
39
|
+
setup do
|
40
|
+
index = rand(0..(Output.count-1))
|
41
|
+
@output = Output.all[index]
|
42
|
+
end
|
43
|
+
|
44
|
+
should "select the correct input" do
|
45
|
+
result = Output.find_by_name(@output.name)
|
46
|
+
assert_equal @output, result
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
context "Device.first" do
|
52
|
+
|
53
|
+
setup do
|
54
|
+
@output = Output.all.first
|
55
|
+
end
|
56
|
+
|
57
|
+
should "open the output" do
|
58
|
+
@output.expects(:open)
|
59
|
+
output = Output.first
|
60
|
+
@output.unstub(:open)
|
61
|
+
end
|
62
|
+
|
63
|
+
should "return the correct output" do
|
64
|
+
output = Output.first
|
65
|
+
assert_equal @output, output
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class UniMIDI::ClassMethodsTest < UniMIDI::TestCase
|
4
|
+
|
5
|
+
context "ClassMethods" do
|
6
|
+
|
7
|
+
setup do
|
8
|
+
TestDeviceHelper.setup
|
9
|
+
end
|
10
|
+
|
11
|
+
context "#first" do
|
12
|
+
|
13
|
+
context "no block given" do
|
14
|
+
should "return first input" do
|
15
|
+
i = Input.first
|
16
|
+
assert_equal(Input.all.first, i)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "block given" do
|
21
|
+
should "pass and return first input" do
|
22
|
+
sleep(1)
|
23
|
+
i = Input.first do |i|
|
24
|
+
assert_equal(true, i.enabled?)
|
25
|
+
end
|
26
|
+
assert_equal(Input.all.first, i)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "#last" do
|
33
|
+
|
34
|
+
context "no block given" do
|
35
|
+
should "return last input" do
|
36
|
+
i = Input.last
|
37
|
+
assert_equal(Input.all.last, i)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "block given" do
|
42
|
+
should "pass and return last input" do
|
43
|
+
sleep(1)
|
44
|
+
i = Input.last do |i|
|
45
|
+
assert_equal(true, i.enabled?)
|
46
|
+
end
|
47
|
+
assert_equal(Input.all.last, i)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "#[]" do
|
54
|
+
|
55
|
+
should "return correct input" do
|
56
|
+
i = Input[0]
|
57
|
+
assert_equal(Input.first, i)
|
58
|
+
assert_equal(Input.all.first, i)
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
context "#use" do
|
64
|
+
|
65
|
+
context "block given" do
|
66
|
+
should "return and enable an input" do
|
67
|
+
sleep(1)
|
68
|
+
i = Input.use(0) do |i|
|
69
|
+
assert_equal(true, i.enabled?)
|
70
|
+
end
|
71
|
+
assert_equal(Input.first, i)
|
72
|
+
assert_equal(Input.all.first, i)
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
context "with symbol" do
|
78
|
+
|
79
|
+
should "return an enabled input" do
|
80
|
+
sleep(1)
|
81
|
+
input = Input.use(:first)
|
82
|
+
assert_equal(true, input.enabled?)
|
83
|
+
assert_equal(Input.first, input)
|
84
|
+
assert_equal(Input.all.first, input)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
context "#all" do
|
92
|
+
should "return all devices" do
|
93
|
+
assert_equal(Loader.devices(:direction => :input), Input.all)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class UniMIDI::FunctionalTest < UniMIDI::TestCase
|
4
|
+
|
5
|
+
# ** these tests assume that TestOutput is connected to TestInput
|
6
|
+
context "UniMIDI" do
|
7
|
+
|
8
|
+
setup do
|
9
|
+
sleep(1)
|
10
|
+
TestDeviceHelper.setup
|
11
|
+
end
|
12
|
+
|
13
|
+
context "full IO" do
|
14
|
+
|
15
|
+
context "using Arrays" do
|
16
|
+
|
17
|
+
setup do
|
18
|
+
@messages = VariousMIDIMessages
|
19
|
+
@messages_arr = @messages.inject { |a,b| a+b }.flatten
|
20
|
+
@received_arr = []
|
21
|
+
@pointer = 0
|
22
|
+
end
|
23
|
+
|
24
|
+
should "do IO" do
|
25
|
+
TestDeviceHelper.devices[:output].open do |output|
|
26
|
+
TestDeviceHelper.devices[:input].open do |input|
|
27
|
+
|
28
|
+
input.buffer.clear
|
29
|
+
|
30
|
+
@messages.each do |msg|
|
31
|
+
|
32
|
+
$>.puts "sending: " + msg.inspect
|
33
|
+
|
34
|
+
output.puts(msg)
|
35
|
+
sleep(1)
|
36
|
+
received = input.gets.map { |m| m[:data] }.flatten
|
37
|
+
|
38
|
+
$>.puts "received: " + received.inspect
|
39
|
+
|
40
|
+
assert_equal(@messages_arr.slice(@pointer, received.length), received)
|
41
|
+
@pointer += received.length
|
42
|
+
@received_arr += received
|
43
|
+
end
|
44
|
+
assert_equal(@messages_arr.length, @received_arr.length)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "using byte Strings" do
|
52
|
+
|
53
|
+
setup do
|
54
|
+
@messages = VariousMIDIByteStrMessages
|
55
|
+
@messages_str = @messages.join
|
56
|
+
@received_str = ""
|
57
|
+
@pointer = 0
|
58
|
+
end
|
59
|
+
|
60
|
+
should "do IO" do
|
61
|
+
TestDeviceHelper.devices[:output].open do |output|
|
62
|
+
TestDeviceHelper.devices[:input].open do |input|
|
63
|
+
|
64
|
+
@messages.each do |msg|
|
65
|
+
|
66
|
+
$>.puts "sending: " + msg.inspect
|
67
|
+
|
68
|
+
output.puts(msg)
|
69
|
+
sleep(1)
|
70
|
+
received = input.gets_bytestr.map { |m| m[:data] }.flatten.join
|
71
|
+
$>.puts "received: " + received.inspect
|
72
|
+
|
73
|
+
assert_equal(@messages_str.slice(@pointer, received.length), received)
|
74
|
+
@pointer += received.length
|
75
|
+
@received_str += received
|
76
|
+
end
|
77
|
+
assert_equal(@messages_str, @received_str)
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
context "using MIDIMessages" do
|
88
|
+
|
89
|
+
setup do
|
90
|
+
@messages = VariousMIDIObjects
|
91
|
+
@messages_arr = @messages.map { |m| m.to_bytes }.flatten
|
92
|
+
@received_arr = []
|
93
|
+
@pointer = 0
|
94
|
+
end
|
95
|
+
|
96
|
+
should "do IO" do
|
97
|
+
TestDeviceHelper.devices[:output].open do |output|
|
98
|
+
TestDeviceHelper.devices[:input].open do |input|
|
99
|
+
|
100
|
+
#input.buffer.clear
|
101
|
+
|
102
|
+
@messages.each do |msg|
|
103
|
+
|
104
|
+
$>.puts "sending: " + msg.inspect
|
105
|
+
|
106
|
+
output.puts(msg)
|
107
|
+
sleep(1)
|
108
|
+
received = input.gets.map { |m| m[:data] }.flatten
|
109
|
+
|
110
|
+
$>.puts "received: " + received.inspect
|
111
|
+
|
112
|
+
assert_equal(@messages_arr.slice(@pointer, received.length), received)
|
113
|
+
@pointer += received.length
|
114
|
+
@received_arr += received
|
115
|
+
end
|
116
|
+
assert_equal(@messages_arr.length, @received_arr.length)
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|