midi-instrument 0.4.1

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 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: []