unimidi 0.3.5 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/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
|