midi-instrument 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4dc9ef33817d2595aa97bec2239f28f1b1779bb4
4
+ data.tar.gz: 05d6227e8b18d1a7a36b439661ea0d7204ec2770
5
+ SHA512:
6
+ metadata.gz: b377e4696a2ab9f80d0c76a10287891883a1ab04162d78b0b0d5c88ba6981f384577ee0b82a1a8fab8da1c8780797699b9cb3f3e3896eb57369354da1cade4cf
7
+ data.tar.gz: c3c7bf02d6ab64895a10b0319bd7ecf387d40885b0c0c184fd79922149eda6edc776d26715f573d83afa6006448b87d86ec7e24453715d44155d1c5eea6c38be
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2011-2014 Ari Russo
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,23 @@
1
+ #MIDI Instrument
2
+
3
+ Core MIDI instrument functionality
4
+
5
+ Used by [Diamond](http://github.com/arirusso/diamond)
6
+
7
+ ## Installation
8
+
9
+ `gem install midi-instrument`
10
+
11
+ or with Bundler, add this to your Gemfile
12
+
13
+ `gem "midi-instrument"`
14
+
15
+ ## Documentation
16
+
17
+ * [rdoc](http://rubydoc.info/github/arirusso/midi-instrument)
18
+
19
+ ## License
20
+
21
+ Licensed under Apache 2.0, See the file LICENSE
22
+
23
+ Copyright © 2011-2014 Ari Russo
@@ -0,0 +1,32 @@
1
+ #
2
+ # MIDI Instrument
3
+ # Core MIDI Instrument Functionality
4
+ #
5
+ # (c)2011-2014 Ari Russo
6
+ # Licensed under Apache 2.0
7
+ #
8
+
9
+ # libs
10
+ require "forwardable"
11
+ require "midi-eye"
12
+ require "midi-fx"
13
+ require "midi-message"
14
+ require "unimidi"
15
+
16
+ # modules
17
+ require "midi-instrument/api"
18
+ require "midi-instrument/device"
19
+ require "midi-instrument/message"
20
+
21
+ # classes
22
+ require "midi-instrument/input"
23
+ require "midi-instrument/listener"
24
+ require "midi-instrument/node"
25
+ require "midi-instrument/note_event"
26
+ require "midi-instrument/output"
27
+
28
+ module MIDIInstrument
29
+
30
+ VERSION = "0.4.1"
31
+
32
+ end
@@ -0,0 +1,14 @@
1
+ module MIDIInstrument
2
+
3
+ module API
4
+
5
+ def self.included(base)
6
+ base.send(:extend, Forwardable)
7
+ base.send(:def_delegators, :@input, :omni_on)
8
+ base.send(:def_delegators, :@output, :mute, :toggle_mute, :mute=, :muted?, :mute?)
9
+ end
10
+
11
+ end
12
+
13
+ end
14
+
@@ -0,0 +1,38 @@
1
+ module MIDIInstrument
2
+
3
+ # Manage MIDI Devices
4
+ module Device
5
+
6
+ extend self
7
+
8
+ # Partition UniMIDI devices into a hash of inputs and outputs
9
+ # @param [Array<UniMIDI::Input, UniMIDI::Output>, UniMIDI::Input, UniMIDI::Output] devices Input or output device(s).
10
+ # @return [Hash] Partitioned arrays of inputs and outputs.
11
+ def partition(devices)
12
+ devices = [(devices || [])].flatten
13
+ outputs = devices.select { |device| output?(device) }
14
+ inputs = devices.select { |device| input?(device) }
15
+ {
16
+ :input => inputs,
17
+ :output => outputs
18
+ }
19
+ end
20
+
21
+ private
22
+
23
+ # Is this device an output?
24
+ # @param [UniMIDI::Output, Object] device
25
+ # @return [Boolean]
26
+ def output?(device)
27
+ device.respond_to?(:puts)
28
+ end
29
+
30
+ # Is this device an input?
31
+ # @param [UniMIDI::Input, Object] device
32
+ # @return [Boolean]
33
+ def input?(device)
34
+ device.respond_to?(:type) && device.type == :input && device.respond_to?(:gets)
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,151 @@
1
+ module MIDIInstrument
2
+
3
+ # Enable a node to listen for MIDI messages on a MIDI input
4
+ class Input
5
+
6
+ extend Forwardable
7
+
8
+ attr_reader :devices, :channel
9
+ def_delegators :@listener, :join
10
+
11
+ # @param [Hash] options
12
+ # @option options [Array<UniMIDI::Input>, UniMIDI::Input] :sources
13
+ # @option options [Hash] :input_map
14
+ def initialize(options = {})
15
+ @listener = Listener.new(options[:sources])
16
+ @devices = InputContainer.new(@listener)
17
+ @channel = nil
18
+ @channel_filter = nil
19
+ end
20
+
21
+ # Add a MIDI input callback
22
+ # @param [Hash] match Matching spec
23
+ # @param [Proc] callback
24
+ # @return [Listen]
25
+ def receive(match = {}, &block)
26
+ if block_given?
27
+ @listener.receive(match) do |event|
28
+ event = filter_event(event)
29
+ yield(event)
30
+ end
31
+ end
32
+ self
33
+ end
34
+
35
+ def add(*messages)
36
+ messages = Message.to_messages([messages].flatten)
37
+ messages = messages.map { |message| filter_message(message) }.compact
38
+ @listener.add(*messages) unless messages.empty?
39
+ messages
40
+ end
41
+ alias_method :<<, :add
42
+
43
+ # Set the listener to acknowledge notes on all channels
44
+ # @return [Boolean]
45
+ def omni
46
+ @channel_filter = nil
47
+ @channel = nil
48
+ true
49
+ end
50
+ alias_method :omni_on, :omni
51
+
52
+ # Set the listener to only acknowledge notes from a specific channel
53
+ # @return [Boolean]
54
+ def channel=(channel)
55
+ @channel_filter = channel.nil? ? nil : MIDIFX::Filter.new(:channel, channel, :name => :input_channel)
56
+ @channel = channel
57
+ true
58
+ end
59
+
60
+ # Replace the devices with the given devices
61
+ # @param [Array<UniMIDI::Inputs>] devices
62
+ # @return [InputContainer]
63
+ def devices=(devices)
64
+ @devices.clear
65
+ @devices += devices
66
+ @devices
67
+ end
68
+
69
+ private
70
+
71
+ def filter_event(event)
72
+ if !@channel_filter.nil?
73
+ if !(message = filter_message(event[:message])).nil?
74
+ event[:message] = message
75
+ event
76
+ end
77
+ else
78
+ event
79
+ end
80
+ end
81
+
82
+ def filter_message(message)
83
+ message = @channel_filter.process(message) unless @channel_filter.nil?
84
+ message
85
+ end
86
+
87
+ # Container class that handles updating the listener when changes are made
88
+ class InputContainer < Array
89
+
90
+ # @param [Listener] listener
91
+ def initialize(listener)
92
+ @listener = listener
93
+ end
94
+
95
+ # Add an input
96
+ # @param [UniMIDI::Input] input
97
+ # @return [InputContainer]
98
+ def <<(input)
99
+ result = super
100
+ @listener.add_input(input)
101
+ result
102
+ end
103
+
104
+ # Add multiple devices
105
+ # @param [Array<UniMIDI::Input>] devices
106
+ # @return [InputContainer]
107
+ def +(devices)
108
+ result = super
109
+ @listener.add_input(devices)
110
+ result
111
+ end
112
+
113
+ # Add multiple devices
114
+ # @param [Array<UniMIDI::Input>] devices
115
+ # @return [InputContainer]
116
+ def concat(devices)
117
+ result = super
118
+ @listener.add_input(devices)
119
+ result
120
+ end
121
+
122
+ # Delete an input
123
+ # @param [UniMIDI::Input]
124
+ # @return [UniMIDI::Input]
125
+ def delete(input)
126
+ result = super
127
+ @listener.remove_input(input)
128
+ result
129
+ end
130
+
131
+ # Clear all devices
132
+ # @return [InputContainer]
133
+ def clear
134
+ @listener.inputs.each { |input| delete(input) }
135
+ super
136
+ end
137
+
138
+ # Delete multiple devices
139
+ # @param [Proc] block
140
+ # @return [InputContainer]
141
+ def delete_if(&block)
142
+ devices = super
143
+ @listener.remove_input(devices)
144
+ self
145
+ end
146
+
147
+ end
148
+
149
+ end
150
+
151
+ end
@@ -0,0 +1,61 @@
1
+ module MIDIInstrument
2
+
3
+ # A light wrapper for MIDIEye::Listener
4
+ class Listener
5
+
6
+ extend Forwardable
7
+
8
+ def_delegators :@listener, :add_input, :remove_input, :stop
9
+ def_delegator :@listener, :remove_listener, :delete_event
10
+
11
+ # @param [Array<UniMIDI::Input>, UniMIDI::Input] sources
12
+ def initialize(sources)
13
+ @listener = MIDIEye::Listener.new([sources].flatten)
14
+ end
15
+
16
+ # Manually add messages to the MIDI input buffer
17
+ # @param [Array<MIDIMessage>, MIDIMessage, *MIDIMessage] args
18
+ # @return [Array<MIDIMessage>]
19
+ def add(*messages)
20
+ [messages].flatten.map do |message|
21
+ report = {
22
+ :message => message,
23
+ :timestamp => Time.now.to_f
24
+ }
25
+ @listener.event.enqueue_all(report)
26
+ report
27
+ end
28
+ end
29
+ alias_method :<<, :add
30
+
31
+ # Join the listener thread
32
+ def join
33
+ start if !@listener.running?
34
+ @listener.join
35
+ end
36
+
37
+ # The active inputs
38
+ # @return [Array<UniMIDI::Input>]
39
+ def inputs
40
+ @listener.sources
41
+ end
42
+
43
+ # Start the listener
44
+ def start
45
+ @listener.start(:background => true)
46
+ end
47
+
48
+ # Bind a message callback
49
+ # @param [Hash] match
50
+ # @param [Proc] callback
51
+ # @return [Boolean]
52
+ def receive(match = {}, &callback)
53
+ @listener.listen_for(match, &callback)
54
+ start if !@listener.running?
55
+ true
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+
@@ -0,0 +1,87 @@
1
+ module MIDIInstrument
2
+
3
+ # Helper for converting MIDI messages
4
+ module Message
5
+
6
+ extend self
7
+
8
+ # Convert the input to MIDI messages
9
+ # @param [Array<MIDIMessage>, Array<String>, MIDIMessage, *MIDIMessage, String] args
10
+ # @return [Array<MIDIMessage>]
11
+ def to_messages(*args)
12
+ data = [args.dup].flatten
13
+ if data.all? { |item| note?(item) }
14
+ Message.to_note_ons(*data) # string note names
15
+ elsif data.all? { |item| message?(item) }
16
+ data # messages
17
+ end
18
+ end
19
+
20
+ # Convert the input to bytes
21
+ # @param [Array<Fixnum>, Array<MIDIMessage>, Array<String>, MIDIMessage, *MIDIMessage, String] args
22
+ # @return [Array<Fixnum>]
23
+ def to_bytes(*args)
24
+ messages = to_messages(*args)
25
+ if !messages.nil?
26
+ messages.map(&:to_bytes).flatten
27
+ elsif args.all? { |arg| bytes?(arg)}
28
+ args
29
+ end
30
+ end
31
+
32
+ # Convert the input to MIDI note on messages
33
+ # @param [*MIDIMessage::NoteOn, *String] args
34
+ # @param [Hash] options
35
+ # @option options [Fixnum] :default_channel
36
+ # @option options [Fixnum] :default_velocity
37
+ # @return [Array<MIDIMessage::NoteOn, nil>]
38
+ def to_note_ons(*args)
39
+ notes = [args.dup].flatten
40
+ options = notes.last.kind_of?(Hash) ? notes.pop : {}
41
+ notes.map do |note|
42
+ case note
43
+ when String then string_to_note_on(note, options) if note?(note)
44
+ when MIDIMessage::NoteOn then note
45
+ end
46
+ end
47
+ end
48
+
49
+ # Does this object look like a MIDI byte?
50
+ # @param [Object] object
51
+ # @return [Boolean]
52
+ def bytes?(object)
53
+ object.kind_of?(Fixnum) && object >= 0x00 && object <= 0xFF
54
+ end
55
+
56
+ private
57
+
58
+ # Is this object a MIDI message?
59
+ # @param [Object] object
60
+ # @return [Boolean]
61
+ def message?(object)
62
+ object.class.name.match(/\AMIDIMessage::[a-zA-Z]+\z/)
63
+ end
64
+
65
+ # Is this object a string note name? eg "A4"
66
+ # @param [Object] object
67
+ # @return [Boolean]
68
+ def note?(object)
69
+ object.kind_of?(String) && object.match(/\A[a-zA-Z]{1}(\#|b)?\-?\d{1}\z/)
70
+ end
71
+
72
+ # Convert the given string (eg "A4") to a note on message object
73
+ # @param [String] string
74
+ # @param [Hash] options
75
+ # @option options [Fixnum] :default_channel
76
+ # @option options [Fixnum] :default_velocity
77
+ # @return [MIDIMessage::NoteOn]
78
+ def string_to_note_on(string, options = {})
79
+ channel = options.fetch(:default_channel, 0)
80
+ velocity = options.fetch(:default_velocity, 100)
81
+ MIDIMessage::NoteOn[string].new(channel, velocity)
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+
@@ -0,0 +1,48 @@
1
+ module MIDIInstrument
2
+
3
+ # Can listen for and send MIDI messages
4
+ class Node
5
+
6
+ include API
7
+
8
+ attr_reader :input, :output
9
+
10
+ # @param [Hash] options
11
+ # @option options [Array<UniMIDI::Input>, UniMIDI::Input] :sources Inputs to listen for MIDI on
12
+ def initialize(options = {})
13
+ @input = Input.new(options)
14
+ @output = Output.new
15
+ end
16
+
17
+ def inputs
18
+ @input.devices
19
+ end
20
+
21
+ def outputs
22
+ @output.devices
23
+ end
24
+
25
+ def receive_channel
26
+ @input.channel
27
+ end
28
+ alias_method :rx_channel, :receive_channel
29
+
30
+ def receive_channel=(channel)
31
+ @input.channel = channel
32
+ end
33
+ alias_method :rx_channel=, :receive_channel=
34
+
35
+ def transmit_channel
36
+ @output.channel
37
+ end
38
+ alias_method :tx_channel, :transmit_channel
39
+
40
+ def transmit_channel=(channel)
41
+ @output.channel = channel
42
+ end
43
+ alias_method :tx_channel=, :transmit_channel=
44
+
45
+ end
46
+
47
+ end
48
+
@@ -0,0 +1,29 @@
1
+ module MIDIInstrument
2
+
3
+ # An NoteEvent is a pairing of a MIDI NoteOn and NoteOff message. Its duration can correspond to sequencer ticks
4
+ class NoteEvent
5
+
6
+ extend Forwardable
7
+
8
+ attr_reader :start,
9
+ :finish,
10
+ :length
11
+
12
+ alias_method :duration, :length
13
+
14
+ def_delegators :start, :note
15
+
16
+ # @param [MIDIMessage::NoteOn] start_message
17
+ # @param [Fixnum] duration
18
+ # @param [Hash] options
19
+ # @option options [MIDIMessage::NoteOff] :finish
20
+ def initialize(start_message, duration, options = {})
21
+ @start = start_message
22
+ @length = duration
23
+
24
+ @finish = options[:finish] || start_message.to_note_off
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,103 @@
1
+ module MIDIInstrument
2
+
3
+ # Send MIDI messages
4
+ class Output
5
+
6
+ attr_reader :devices, :channel
7
+ attr_writer :mute
8
+
9
+ def initialize
10
+ @channel = nil
11
+ @channel_filter = nil
12
+ @mute = false
13
+ @devices = []
14
+ end
15
+
16
+ # Set the output to convert all emitted notes to a specific channel
17
+ # @param [Fixnum, nil] channel
18
+ # @return [Boolean]
19
+ def channel=(channel)
20
+ @channel_filter = if channel.nil?
21
+ nil
22
+ else
23
+ MIDIFX::Limit.new(:channel, channel, :name => :output_channel)
24
+ end
25
+ @channel = channel
26
+ true
27
+ end
28
+
29
+ # Emit messages
30
+ # @param [*Fixnum, *MIDIMessage::NoteOn, *MIDIMessage::NoteOff, *String] args
31
+ # @return [Array<Fixnum>]
32
+ def puts(*args)
33
+ bytes = to_bytes(args)
34
+ if !@mute
35
+ @devices.each { |output| output.puts(*bytes) }
36
+ end
37
+ bytes
38
+ end
39
+ alias_method :<<, :puts
40
+
41
+ # Toggle muted output
42
+ # @return [Boolean]
43
+ def toggle_mute
44
+ @mute = !@mute
45
+ end
46
+
47
+ # Mute the output
48
+ # @return [TrueClass]
49
+ def mute
50
+ @mute = true
51
+ end
52
+
53
+ # Un-mute the output
54
+ # @return [FalseClass]
55
+ def unmute
56
+ @mute = false
57
+ end
58
+
59
+ # Is the output muted?
60
+ # @return [Boolean]
61
+ def mute?
62
+ @mute
63
+ end
64
+
65
+ # Replace the devices with the given devices
66
+ # @param [Array<UniMIDI::Outputs>] devices
67
+ # @return [OutputContainer]
68
+ def devices=(devices)
69
+ @devices.clear
70
+ @devices += devices
71
+ @devices
72
+ end
73
+
74
+ private
75
+
76
+ # Convert the input to MIDI message bytes
77
+ # @param [Array<Fixnum>, Array<MIDIMessage>] args
78
+ # @return [Array<Fixnum>]
79
+ def to_bytes(args)
80
+ data = [args.dup].flatten
81
+ messages = Message.to_messages(*data)
82
+ if !messages.nil?
83
+ messages = filter_output(messages)
84
+ messages.map(&:to_bytes).flatten
85
+ elsif Message.bytes?(data)
86
+ data
87
+ end
88
+ end
89
+
90
+ # Filter messages for output
91
+ # @param [Array<MIDIMessage::ChannelMessage>] messages
92
+ # @return [Array<MIDIMessage::ChannelMessage>]
93
+ def filter_output(messages)
94
+ if @channel_filter.nil?
95
+ messages
96
+ else
97
+ messages.map { |message| @channel_filter.process(message) }
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,26 @@
1
+ require "helper"
2
+
3
+ class MIDIInstrument::DeviceTest < Test::Unit::TestCase
4
+
5
+ context "Device" do
6
+
7
+ context ".partition" do
8
+
9
+ should "partition some devices" do
10
+ inputs = UniMIDI::Input.all
11
+ outputs = UniMIDI::Output.all
12
+ devices = inputs + outputs
13
+ result = MIDIInstrument::Device.partition(devices)
14
+ assert_not_nil result
15
+ assert result.kind_of?(Hash)
16
+ assert_not_nil result[:input]
17
+ assert_not_nil result[:output]
18
+ assert_equal inputs, result[:input]
19
+ assert_equal outputs, result[:output]
20
+ end
21
+
22
+ end
23
+ end
24
+
25
+ end
26
+
data/test/helper.rb ADDED
@@ -0,0 +1,18 @@
1
+ dir = File.dirname(File.expand_path(__FILE__))
2
+ $LOAD_PATH.unshift dir + "/../lib"
3
+
4
+ require "test/unit"
5
+ require "mocha/test_unit"
6
+ require "shoulda-context"
7
+ require "midi-instrument"
8
+
9
+ module TestHelper
10
+
11
+ def self.select_devices
12
+ $test_device ||= {}
13
+ { :input => UniMIDI::Input, :output => UniMIDI::Output }.each do |type, klass|
14
+ $test_device[type] = klass.gets
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,92 @@
1
+ require "helper"
2
+
3
+ class MIDIInstrument::InputTest < Test::Unit::TestCase
4
+
5
+ include Mocha::ParameterMatchers
6
+
7
+ context "Input" do
8
+
9
+ setup do
10
+ @input = MIDIInstrument::Input.new
11
+ end
12
+
13
+ context "#receive" do
14
+
15
+ should "add callback" do
16
+ match = { :class => MIDIMessage::NoteOn }
17
+ block = proc { puts "hello" }
18
+ MIDIInstrument::Listener.any_instance.expects(:receive).once.with(match).yields(block)
19
+ result = @input.receive(match, &block)
20
+ assert_equal @input, result
21
+ end
22
+
23
+ end
24
+
25
+ context "#add" do
26
+
27
+ should "accept strings" do
28
+ MIDIInstrument::Listener.any_instance.expects(:add).once.with(is_a(MIDIMessage::NoteOn), is_a(MIDIMessage::NoteOn), is_a(MIDIMessage::NoteOn))
29
+ @input.add("A3", "B4", "C5")
30
+ end
31
+
32
+ should "accept MIDI messages" do
33
+ MIDIInstrument::Listener.any_instance.expects(:add).once.with(is_a(MIDIMessage::NoteOn), is_a(MIDIMessage::NoteOn), is_a(MIDIMessage::NoteOn))
34
+ @input.add(MIDIMessage::NoteOn["C3"].new(3, 100), MIDIMessage::NoteOn["B4"].new(5, 100), MIDIMessage::NoteOn["D7"].new(7, 100))
35
+ end
36
+
37
+ should "not suppress unknown objects" do
38
+ MIDIInstrument::Listener.any_instance.expects(:add).never
39
+ assert_raise(NoMethodError) { @input.add("hello", "how", "are", "you") }
40
+ end
41
+
42
+ end
43
+
44
+ context "#omni" do
45
+
46
+ should "not have a receive channel" do
47
+ assert_nil @input.instance_variable_get("@channel_filter")
48
+ @input.channel = 4
49
+ assert_equal 4, @input.channel
50
+ @input.omni
51
+ assert_nil @input.channel
52
+ assert_nil @input.instance_variable_get("@channel_filter")
53
+ end
54
+
55
+ end
56
+
57
+ context "#channel=" do
58
+
59
+ should "have filter when channel is specified" do
60
+ assert_nil @input.instance_variable_get("@channel_filter")
61
+ @input.channel = 3
62
+ assert_not_nil @input.instance_variable_get("@channel_filter")
63
+ assert_equal 3, @input.channel
64
+ end
65
+
66
+ should "not have filter when channel is nil" do
67
+ @input.channel = 3
68
+ assert_not_nil @input.instance_variable_get("@channel_filter")
69
+ assert_equal 3, @input.channel
70
+ @input.channel = nil
71
+ assert_nil @input.instance_variable_get("@channel_filter")
72
+ assert_nil @input.channel
73
+ end
74
+
75
+ end
76
+
77
+ context "#devices=" do
78
+
79
+ should "replace devices" do
80
+ assert_empty @input.devices
81
+ inputs = UniMIDI::Input.all
82
+ @input.devices = inputs
83
+ assert_equal inputs, @input.devices
84
+ end
85
+
86
+ end
87
+
88
+ end
89
+
90
+ end
91
+
92
+
@@ -0,0 +1,31 @@
1
+ require "helper"
2
+
3
+ class MIDIInstrument::ListenerTest < Test::Unit::TestCase
4
+
5
+ context "Listener" do
6
+
7
+ setup do
8
+ MIDIInstrument::Listener.any_instance.unstub(:add)
9
+ @listener = MIDIInstrument::Listener.new(Object.new)
10
+ end
11
+
12
+ context "#add" do
13
+
14
+ should "output hashes with timestamps" do
15
+ notes = [
16
+ MIDIMessage::NoteOn["C3"].new(3, 100),
17
+ MIDIMessage::NoteOn["B4"].new(5, 100),
18
+ MIDIMessage::NoteOn["D7"].new(7, 100)
19
+ ]
20
+ result = @listener.add(*notes)
21
+ assert_not_nil result
22
+ assert_not_empty result
23
+ assert result.all? { |r| r.kind_of?(Hash) }
24
+ refute result.any? { |r| r[:timestamp].nil? }
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,106 @@
1
+ require "helper"
2
+
3
+ class MIDIInstrument::MessageTest < Test::Unit::TestCase
4
+
5
+ context "Message" do
6
+
7
+ context ".to_messages" do
8
+
9
+ should "convert strings to note on messages" do
10
+ names = ["A-1", "C#4", "F#5", "D6"]
11
+ result = MIDIInstrument::Message.to_messages(*names)
12
+ assert_not_nil result
13
+ assert_not_empty result
14
+ assert_equal names.size, result.size
15
+ assert result.all? { |item| item.is_a?(MIDIMessage::NoteOn) }
16
+ names.each_with_index { |name, i| assert_equal name, result[i].name }
17
+ end
18
+
19
+ should "pass MIDI messages" do
20
+ names = ["A-1", "C#4", "F#5", "D6"]
21
+ messages = MIDIInstrument::Message.to_messages(*names)
22
+ result = MIDIInstrument::Message.to_messages(*messages)
23
+ assert_not_nil result
24
+ assert_not_empty result
25
+ messages.each_with_index { |message,i| assert_equal message.note, result[i].note }
26
+ end
27
+
28
+ should "return nil for unknowns" do
29
+ assert_nil MIDIInstrument::Message.to_messages("bla", "blah")
30
+ end
31
+
32
+ end
33
+
34
+ context ".to_bytes" do
35
+
36
+ should "convert messages to bytes" do
37
+ names = ["A-1", "C#4", "F#5", "D6"]
38
+ messages = MIDIInstrument::Message.to_messages(*names)
39
+ result = MIDIInstrument::Message.to_bytes(*messages)
40
+ assert_not_nil result
41
+ assert_not_empty result
42
+ assert result.all? { |item| item.is_a?(Fixnum) }
43
+ assert_equal messages.map(&:to_a).flatten, result
44
+ end
45
+
46
+ should "convert strings to bytes" do
47
+ names = ["A-1", "C#4", "F#5", "D6"]
48
+ messages = MIDIInstrument::Message.to_messages(*names)
49
+ result = MIDIInstrument::Message.to_bytes(*names)
50
+ assert_not_nil result
51
+ assert_not_empty result
52
+ assert result.all? { |item| item.is_a?(Fixnum) }
53
+ assert_equal messages.map(&:to_a).flatten, result
54
+ end
55
+
56
+ should "pass bytes" do
57
+ bytes = [144, 9, 100, 144, 61, 100, 144, 78, 100, 144, 86, 100]
58
+ result = MIDIInstrument::Message.to_bytes(*bytes)
59
+ assert_not_nil result
60
+ assert_not_empty result
61
+ assert result.all? { |item| item.is_a?(Fixnum) }
62
+ assert_equal bytes, result
63
+ end
64
+
65
+ should "return nil for unknown" do
66
+ assert_nil MIDIInstrument::Message.to_bytes("this", "is", "something", "weird", 4560)
67
+ end
68
+
69
+ end
70
+
71
+ context ".to_note_ons" do
72
+
73
+ should "convert strings to note ons" do
74
+ names = ["A-1", "C#4", "F#5", "D6"]
75
+ result = MIDIInstrument::Message.to_note_ons(*names)
76
+ assert_not_nil result
77
+ assert_not_empty result
78
+ assert_equal names.size, result.size
79
+ assert result.all? { |item| item.is_a?(MIDIMessage::NoteOn) }
80
+ names.each_with_index { |name, i| assert_equal name, result[i].name }
81
+ end
82
+
83
+ should "pass note on messages" do
84
+ names = ["A-1", "C#4", "F#5", "D6"]
85
+ messages = MIDIInstrument::Message.to_messages(*names)
86
+ result = MIDIInstrument::Message.to_note_ons(*messages)
87
+ assert_not_nil result
88
+ assert_not_empty result
89
+ messages.each_with_index { |message,i| assert_equal message.note, result[i].note }
90
+ end
91
+
92
+ should "return nil for unknowns" do
93
+ result = MIDIInstrument::Message.to_note_ons("blah", "blah", 56904)
94
+ assert_not_nil result
95
+ assert_equal 3, result.size
96
+ assert_empty result.compact
97
+ end
98
+
99
+ end
100
+
101
+ end
102
+
103
+ end
104
+
105
+
106
+
@@ -0,0 +1,33 @@
1
+ require "helper"
2
+
3
+ class MIDIInstrument::NoteEventTest < Test::Unit::TestCase
4
+
5
+ context "NoteEvent" do
6
+
7
+ context ".new" do
8
+
9
+ should "create a note event" do
10
+ msg = MIDIMessage::NoteOn.new(0, 0x40, 0x40)
11
+ event = MIDIInstrument::NoteEvent.new(msg, 10)
12
+ assert_equal(msg, event.start)
13
+ assert_equal(10, event.duration)
14
+ assert_equal(MIDIMessage::NoteOff, event.finish.class)
15
+ assert_equal(msg.note, event.finish.note)
16
+ assert_equal(msg.note, event.note)
17
+ end
18
+
19
+ should "override finish" do
20
+ msg = MIDIMessage::NoteOn.new(0, 0x40, 0x40)
21
+ msg2 = MIDIMessage::NoteOff.new(0, 0x40, 127)
22
+ event = MIDIInstrument::NoteEvent.new(msg, 5, :finish => msg2)
23
+ assert_equal(msg, event.start)
24
+ assert_equal(5, event.duration)
25
+ assert_equal(0x40, event.start.velocity)
26
+ assert_equal(MIDIMessage::NoteOff, event.finish.class)
27
+ assert_equal(0x40, event.finish.note)
28
+ assert_equal(127, event.finish.velocity)
29
+ end
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,100 @@
1
+ require "helper"
2
+
3
+ class MIDIInstrument::OutputTest < Test::Unit::TestCase
4
+
5
+ include Mocha::ParameterMatchers
6
+
7
+ context "Output" do
8
+
9
+ setup do
10
+ @output = MIDIInstrument::Output.new
11
+ end
12
+
13
+ context "#puts" do
14
+
15
+ setup do
16
+ @output.devices << Object.new
17
+ end
18
+
19
+ should "output string notes" do
20
+ names = ["A-1", "C#4", "F#5", "D6"]
21
+ @output.devices.first.expects(:puts).times(1)
22
+ result = @output.puts(*names)
23
+ end
24
+
25
+ should "output MIDI messages" do
26
+ @output.devices.first.expects(:puts).times(1)
27
+ result = @output.puts(MIDIMessage::NoteOn["C3"].new(0, 100), MIDIMessage::NoteOn["D3"].new(0, 110))
28
+ end
29
+
30
+ should "output to multiple devices" do
31
+ 4.times do
32
+ @output.devices << Object.new
33
+ end
34
+ names = ["A-1", "C#4", "F#5", "D6"]
35
+ @output.devices.each { |device| device.expects(:puts).once }
36
+ result = @output.puts(*names)
37
+ end
38
+
39
+ end
40
+
41
+ context "#mute" do
42
+
43
+ setup do
44
+ @output.devices << Object.new
45
+ end
46
+
47
+ should "output when not muted" do
48
+ names = ["A-1", "C#4", "F#5", "D6"]
49
+ refute @output.mute?
50
+ @output.devices.first.expects(:puts).times(1)
51
+ result = @output.puts(*names)
52
+ end
53
+
54
+ should "not output when muted" do
55
+ names = ["A-1", "C#4", "F#5", "D6"]
56
+ @output.mute
57
+ assert @output.mute?
58
+ @output.devices.first.expects(:puts).never
59
+ result = @output.puts(*names)
60
+ end
61
+
62
+ end
63
+
64
+ context "#channel=" do
65
+
66
+ should "have filter when channel is specified" do
67
+ assert_nil @output.instance_variable_get("@channel_filter")
68
+ @output.channel = 3
69
+ assert_not_nil @output.instance_variable_get("@channel_filter")
70
+ assert_equal 3, @output.channel
71
+ end
72
+
73
+ should "not have filter when channel is nil" do
74
+ @output.channel = 3
75
+ assert_not_nil @output.instance_variable_get("@channel_filter")
76
+ assert_equal 3, @output.channel
77
+ @output.channel = nil
78
+ assert_nil @output.instance_variable_get("@channel_filter")
79
+ assert_nil @output.channel
80
+ end
81
+
82
+ end
83
+
84
+ context "#devices=" do
85
+
86
+ should "replace devices" do
87
+ assert_empty @output.devices
88
+ outputs = UniMIDI::Output.all
89
+ @output.devices = outputs
90
+ assert_equal outputs, @output.devices
91
+ end
92
+
93
+ end
94
+
95
+ end
96
+
97
+ end
98
+
99
+
100
+
metadata ADDED
@@ -0,0 +1,142 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: midi-instrument
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.1
5
+ platform: ruby
6
+ authors:
7
+ - Ari Russo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: midi-eye
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.1.3
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.1.3
33
+ - !ruby/object:Gem::Dependency
34
+ name: midi-fx
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 0.0.3
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 0.0.3
53
+ - !ruby/object:Gem::Dependency
54
+ name: midi-message
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 0.3.1
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 0.3.1
73
+ - !ruby/object:Gem::Dependency
74
+ name: unimidi
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 0.2.5
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 0.2.5
93
+ description: Core MIDI instrument functionality in Ruby
94
+ email:
95
+ - ari.russo@gmail.com
96
+ executables: []
97
+ extensions: []
98
+ extra_rdoc_files: []
99
+ files:
100
+ - LICENSE
101
+ - README.md
102
+ - lib/midi-instrument.rb
103
+ - lib/midi-instrument/api.rb
104
+ - lib/midi-instrument/device.rb
105
+ - lib/midi-instrument/input.rb
106
+ - lib/midi-instrument/listener.rb
107
+ - lib/midi-instrument/message.rb
108
+ - lib/midi-instrument/node.rb
109
+ - lib/midi-instrument/note_event.rb
110
+ - lib/midi-instrument/output.rb
111
+ - test/device_test.rb
112
+ - test/helper.rb
113
+ - test/input_test.rb
114
+ - test/listener_test.rb
115
+ - test/message_test.rb
116
+ - test/note_event_test.rb
117
+ - test/output_test.rb
118
+ homepage: http://github.com/arirusso/midi-instrument
119
+ licenses:
120
+ - Apache 2.0
121
+ metadata: {}
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: 1.3.6
136
+ requirements: []
137
+ rubyforge_project: midi-instrument
138
+ rubygems_version: 2.2.2
139
+ signing_key:
140
+ specification_version: 4
141
+ summary: Core MIDI instrument functionality
142
+ test_files: []