midi-eye 0.2.2 → 0.3.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: 2944fec81520048f22479d8fa72484928e3dcf65
4
+ data.tar.gz: bcab4013133af07447f36d6731e8840e9023b0d2
5
+ SHA512:
6
+ metadata.gz: 4b91db15538087d57aa6ab1d7fd250f69ff5f09d12ebfd5c7200813fc6e6db38815d45563cc5d5c5b50ddf7a22ce3b6b17990b398c255f19b53eaa9fce270ccb
7
+ data.tar.gz: 1c4f0fa96f717e7f179ad4fbdf9608d8ccb76e17d30d08f96d4a75590a7e2f409ff8d29598424bcc853dd17983f570c67aecc979d248f763c89190f38b9bb994
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2011 Ari Russo
1
+ Copyright 2011-2014 Ari Russo
2
2
 
3
3
  Licensed under the Apache License, Version 2.0 (the "License");
4
4
  you may not use this file except in compliance with the License.
@@ -10,4 +10,4 @@ Unless required by applicable law or agreed to in writing, software
10
10
  distributed under the License is distributed on an "AS IS" BASIS,
11
11
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  See the License for the specific language governing permissions and
13
- limitations under the License.
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # MIDI EYE
2
+
3
+ MIDI input event listener for Ruby
4
+
5
+ ## Install
6
+
7
+ `gem install midi-eye`
8
+
9
+ or using Bundler, add this to your Gemfile
10
+
11
+ `gem "midi-eye"`
12
+
13
+ ## Usage
14
+
15
+ ```ruby
16
+ require 'midi-eye'
17
+ ```
18
+
19
+ The following is an example that takes any note messages received from a unimidi input, transposes them up one octave and then sends them to an output
20
+
21
+ First, pick some MIDI IO ports
22
+
23
+ ```ruby
24
+ @input = UniMIDI::Input.gets
25
+ @output = UniMIDI::Output.gets
26
+ ```
27
+
28
+ Then create a listener for the input port
29
+
30
+ ```ruby
31
+ transpose = MIDIEye::Listener.new(@input)
32
+ ```
33
+
34
+ You can bind an event to the listener using `Listener#listen_for`
35
+
36
+ The listener will try to positively match the parameters you pass in to the properties of the messages it receives.
37
+
38
+ In this example, we specify that the listener listens for note on/off messages, which are identifiable by their class.
39
+
40
+ ```ruby
41
+ transpose.listen_for(:class => [MIDIMessage::NoteOn, MIDIMessage::NoteOff]) do |event|
42
+
43
+ # raise the note value by an octave
44
+ event[:message].note += 12
45
+
46
+ # send the altered note message to the output you chose earlier
47
+ @output.puts(event[:message])
48
+
49
+ end
50
+ ```
51
+
52
+ There is also the option of leaving out the parameters altogether and including using conditional if/unless/case/etc statements in the callback.
53
+
54
+ You can bind as many events to a listener as you wish by repeatedly calling `Listener#listen_for`
55
+
56
+ Once all the events are bound, start the listener
57
+
58
+ ```ruby
59
+ transpose.run
60
+ ```
61
+
62
+ A listener can also be run in a background thread by passing in `:background => true`.
63
+
64
+ ```ruby
65
+ transpose.run(:background => true)
66
+
67
+ transpose.join # join the background thread later
68
+ ```
69
+
70
+ ## Documentation
71
+
72
+ * [examples](http://github.com/arirusso/midi-eye/tree/master/examples)
73
+ * [rdoc](http://rdoc.info/gems/midi-eye)
74
+
75
+ ## Author
76
+
77
+ * [Ari Russo](http://github.com/arirusso) <ari.russo at gmail.com>
78
+
79
+ ## License
80
+
81
+ Apache 2.0, See the file LICENSE
82
+
83
+ Copyright (c) 2011-2014 Ari Russo
@@ -1,65 +1,69 @@
1
- #!/usr/bin/env ruby
2
1
  module MIDIEye
3
2
 
4
3
  class Listener
5
4
 
6
5
  attr_reader :events
7
6
  attr_accessor :sources
8
-
9
- @input_types = []
10
-
11
- class << self
12
- # a registry of input types
13
- attr_reader :input_types
14
- end
15
7
 
16
- def initialize(input, options = {})
8
+ # @param [Array<UniMIDI::Input>, UniMIDI::Input] inputs Input(s) to add to the list of sources for this listener
9
+ def initialize(inputs)
17
10
  @sources = []
18
11
  @event_queue = []
19
12
  @events = []
20
13
 
21
- add_input(input)
14
+ add_input(inputs)
22
15
  end
23
16
 
24
- # does this listener use <em>input</em>?
17
+ # Does this listener use the given input?
18
+ # @param [UniMIDI::Input] input
19
+ # @return [Boolean]
25
20
  def uses_input?(input)
26
- !@sources.find_all { |source| source.uses?(input) }.empty?
21
+ @sources.any? { |source| source.uses?(input) }
27
22
  end
28
23
 
29
- # add a source
30
- # takes a raw input or array of
31
- def add_input(input)
32
- @sources += [input].flatten.map do |i|
33
- klass = self.class.input_types.find { |type| type.is_compatible?(i) }
34
- raise "Input class type #{i.class.name} not compatible" if klass.nil?
35
- klass.new(i) unless uses_input?(i)
36
- end.compact
24
+ # Add a MIDI source
25
+ # @param [Array<UniMIDI::Input>, UniMIDI::Input] inputs Input(s) to add to the list of sources for this listener
26
+ # @return [Array<MIDIEye::Source>] The updated list of sources for this listener
27
+ def add_input(inputs)
28
+ inputs = [inputs].flatten.compact
29
+ new_sources = inputs.map do |input|
30
+ Source.new(input) unless uses_input?(input)
31
+ end
32
+ @sources += new_sources.compact
33
+ @sources
37
34
  end
35
+ alias_method :add_inputs, :add_input
38
36
 
39
- # remove a source
40
- # takes a raw input or array of
37
+ # Remove a MIDI source
38
+ # @param [Array<UniMIDI::Input>, UniMIDI::Input] inputs Input(s) to remove from the list of sources for this listener
39
+ # @return [Array<MIDIEye::Source>] The updated list of sources for this listener
41
40
  def remove_input(inputs)
42
- to_remove = [inputs].flatten
43
- to_remove.each do |input|
41
+ inputs = [inputs].flatten.compact
42
+ inputs.each do |input|
44
43
  @sources.delete_if { |source| source.uses?(input) }
45
44
  end
45
+ @sources
46
46
  end
47
-
47
+ alias_method :remove_inputs, :remove_input
48
+
49
+ # @param [Symbol] name
48
50
  def delete_event(name)
49
- @events.delete_if { |e| e[:listener_name] == name }
51
+ @events.delete_if { |event| event[:listener_name] == name }
50
52
  end
51
53
 
52
- # start the listener. pass in :background => true to run only in a background thread. returns self
54
+ # Start listening for MIDI messages
55
+ # @params [Hash] options
56
+ # @option options [Boolean] :background Run in a background thread
57
+ # @return [MIDIEye::Listener] self
53
58
  def run(options = {})
54
- listen!
55
- unless options[:background]
56
- @listener.join
57
- end
59
+ listen
60
+ join unless !!options[:background]
58
61
  self
59
62
  end
60
63
  alias_method :start, :run
61
64
 
62
- # stop the listener. returns self
65
+ # Stop listening for MIDI messages.
66
+ # @return [MIDIEye::Listener] self
63
67
  def close
64
68
  @listener.kill unless @listener.nil?
65
69
  @events.clear
@@ -69,31 +73,45 @@ module MIDIEye
69
73
  end
70
74
  alias_method :stop, :close
71
75
 
72
- # join the listener if it's being run in the background. returns self
76
+ # Join the listener if it's being run in the background.
77
+ # @return [MIDIEye::Listener] self
73
78
  def join
74
- @listener.join
79
+ begin
80
+ @listener.join
81
+ rescue SystemExit, Interrupt
82
+ @listener.kill
83
+ raise
84
+ end
75
85
  self
76
86
  end
77
87
 
78
- # add an event to listen for. returns self
79
- def listen_for(options = {}, &proc)
80
- raise 'listener must have a block' if proc.nil?
88
+ # Add an event to listen for
89
+ # @param [Hash] options
90
+ # @return [MIDIEye::Listener] self
91
+ def listen_for(options = {}, &callback)
92
+ raise "Listener must have a block" if callback.nil?
81
93
  name = options[:listener_name]
82
94
  options.delete(:listener_name)
83
- @events << { :conditions => options, :proc => proc, :listener_name => name }
95
+ event = {
96
+ :conditions => options,
97
+ :proc => callback,
98
+ :listener_name => name
99
+ }
100
+ @events << event
84
101
  self
85
102
  end
86
103
  alias_method :on_message, :listen_for
87
104
  alias_method :listen, :listen_for
88
105
 
89
- # poll the input source for new input. this will normally be done by the background thread
106
+ # Poll the input source for new input. This will normally be done by the background thread
90
107
  def poll
91
108
  @sources.each do |input|
92
109
  input.poll do |objs|
93
110
  objs.each do |batch|
94
- [batch[:messages]].flatten.each do |single_message|
95
- unless single_message.nil?
96
- data = { :message => single_message, :timestamp => batch[:timestamp] }
111
+ messages = [batch[:messages]].flatten.compact
112
+ messages.each do |message|
113
+ unless message.nil?
114
+ data = { :message => message, :timestamp => batch[:timestamp] }
97
115
  @events.each { |name| queue_event(name, data) }
98
116
  end
99
117
  end
@@ -103,48 +121,67 @@ module MIDIEye
103
121
  end
104
122
 
105
123
  private
106
-
107
- # start the background listener thread
108
- def listen!
109
- t = 1.0/1000
110
- @listener = Thread.fork do
111
- Thread.abort_on_exception = true
112
- loop do
113
- poll
114
- trigger_queued_events unless @event_queue.empty?
115
- sleep(t)
116
- end
124
+
125
+ # A loop that runs while the listener is active
126
+ def listen_loop
127
+ interval = 1.0/1000
128
+ loop do
129
+ poll
130
+ trigger_queued_events unless @event_queue.empty?
131
+ sleep(interval)
117
132
  end
118
133
  end
119
134
 
120
- # trigger all queued events
135
+ # Start the background listener thread
136
+ def listen
137
+ @listener = Thread.new { listen_loop }
138
+ @listener.abort_on_exception = true
139
+ true
140
+ end
141
+
142
+ # Trigger all queued events
121
143
  def trigger_queued_events
122
144
  @event_queue.length.times { trigger_event(@event_queue.shift) }
123
145
  end
124
146
 
125
- # does <em>message</em> meet <em>conditions</em>?
147
+ # Does the given message meet the given conditions?
126
148
  def meets_conditions?(conditions, message)
127
- !conditions.map do |key, value|
128
- message.respond_to?(key) && (value.kind_of?(Array) ? value.include?(message.send(key)) : value.eql?(message.send(key)))
129
- end.include?(false)
149
+ results = conditions.map do |key, value|
150
+ if message.respond_to?(key)
151
+ if value.kind_of?(Array)
152
+ value.include?(message.send(key))
153
+ else
154
+ value.eql?(message.send(key))
155
+ end
156
+ else
157
+ false
158
+ end
159
+ end
160
+ results.all?
130
161
  end
131
162
 
132
- # trigger an event
163
+ # Trigger an event
133
164
  def trigger_event(event)
134
- begin
135
- action = event[:action]
136
- if meets_conditions?(action[:conditions], event[:message][:message]) || action[:conditions].nil?
165
+ action = event[:action]
166
+ conditions = action[:conditions]
167
+ if conditions.nil? || meets_conditions?(conditions, event[:message][:message])
168
+ begin
137
169
  action[:proc].call(event[:message])
170
+ rescue
171
+ # help
138
172
  end
139
- rescue
140
173
  end
141
174
  end
142
175
 
143
- # add an event to the trigger queue
144
- def queue_event(event, message)
145
- @event_queue << { :action => event, :message => message }
176
+ # Add an event to the trigger queue
177
+ def queue_event(action, message)
178
+ event = {
179
+ :action => action,
180
+ :message => message
181
+ }
182
+ @event_queue << event
146
183
  end
147
184
 
148
185
  end
149
186
 
150
- end
187
+ end
@@ -0,0 +1,44 @@
1
+ module MIDIEye
2
+
3
+ # Retrieves new messages from a unimidi input buffer
4
+ class Source
5
+
6
+ attr_reader :device, :pointer
7
+
8
+ # @param [UniMIDI::Input] input
9
+ def initialize(input)
10
+ @parser = Nibbler.new
11
+ @pointer = 0
12
+ @device = input
13
+ end
14
+
15
+ # Grabs new messages from the input buffer
16
+ def poll(&block)
17
+ messages = @device.buffer.slice(@pointer, @device.buffer.length - @pointer)
18
+ @pointer = @device.buffer.length
19
+ messages.each do |raw_message|
20
+ unless raw_message.nil?
21
+ parsed_messages = @parser.parse(raw_message[:data], :timestamp => raw_message[:timestamp]) rescue nil
22
+ objects = [parsed_messages].flatten.compact
23
+ yield(objects)
24
+ end
25
+ end
26
+ end
27
+
28
+ # Whether the given object is a UniMIDI input
29
+ # @param [Object] input
30
+ # @return [Boolean]
31
+ def self.compatible?(input)
32
+ input.respond_to?(:gets) && input.respond_to?(:buffer)
33
+ end
34
+
35
+ # If this source was created from the given input
36
+ # @param [UniMIDI::Input] input
37
+ # @return [Boolean]
38
+ def uses?(input)
39
+ @device == input
40
+ end
41
+
42
+ end
43
+
44
+ end
data/lib/midi-eye.rb CHANGED
@@ -1,20 +1,22 @@
1
- #!/usr/bin/env ruby
2
1
  #
3
2
  # midi-eye
4
- # Transparent MIDI event listener for Ruby
5
- # (c)2011 Ari Russo
6
- # licensed under the Apache 2.0 License
3
+ #
4
+ # MIDI input event listener for Ruby
5
+ # (c)2011-2014 Ari Russo
6
+ # Licensed under the Apache 2.0 License
7
7
  #
8
8
 
9
+ # libs
9
10
  require 'midi-message'
10
11
  require 'nibbler'
11
12
  require 'unimidi'
12
13
 
14
+ # classes
13
15
  require 'midi-eye/listener'
14
- require 'midi-eye/unimidi_input'
16
+ require 'midi-eye/source'
15
17
 
16
18
  module MIDIEye
17
19
 
18
- VERSION = "0.2.2"
20
+ VERSION = "0.3.1"
19
21
 
20
- end
22
+ end
@@ -1,6 +1,4 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'helper'
1
+ require "helper"
4
2
 
5
3
  class ListenerTest < Test::Unit::TestCase
6
4
 
@@ -98,7 +96,7 @@ class ListenerTest < Test::Unit::TestCase
98
96
  input = $test_device[:input]
99
97
  output = $test_device[:output]
100
98
  listener = Listener.new(input)
101
- assert_equal(UniMIDIInput, listener.sources.first.class)
99
+ assert_equal(Source, listener.sources.first.class)
102
100
  close_all(input, output, listener)
103
101
  end
104
102
 
@@ -153,4 +151,4 @@ class ListenerTest < Test::Unit::TestCase
153
151
  listener.join
154
152
  end
155
153
 
156
- end
154
+ end
metadata CHANGED
@@ -1,85 +1,93 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: midi-eye
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
5
- prerelease:
4
+ version: 0.3.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Ari Russo
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2011-10-05 00:00:00.000000000Z
11
+ date: 2014-08-30 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: midi-message
16
- requirement: &70223965375620 !ruby/object:Gem::Requirement
17
- none: false
15
+ requirement: !ruby/object:Gem::Requirement
18
16
  requirements:
19
- - - ! '>='
17
+ - - "~>"
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :runtime
23
21
  prerelease: false
24
- version_requirements: *70223965375620
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
25
27
  - !ruby/object:Gem::Dependency
26
28
  name: midi-nibbler
27
- requirement: &70223965375080 !ruby/object:Gem::Requirement
28
- none: false
29
+ requirement: !ruby/object:Gem::Requirement
29
30
  requirements:
30
- - - ! '>='
31
+ - - "~>"
31
32
  - !ruby/object:Gem::Version
32
33
  version: '0'
33
34
  type: :runtime
34
35
  prerelease: false
35
- version_requirements: *70223965375080
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
36
41
  - !ruby/object:Gem::Dependency
37
42
  name: unimidi
38
- requirement: &70223965374640 !ruby/object:Gem::Requirement
39
- none: false
43
+ requirement: !ruby/object:Gem::Requirement
40
44
  requirements:
41
- - - ! '>='
45
+ - - "~>"
42
46
  - !ruby/object:Gem::Version
43
47
  version: '0'
44
48
  type: :runtime
45
49
  prerelease: false
46
- version_requirements: *70223965374640
47
- description: MIDI event listener for Ruby
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: A utility for dealing with MIDI input in Ruby.
48
56
  email:
49
57
  - ari.russo@gmail.com
50
58
  executables: []
51
59
  extensions: []
52
60
  extra_rdoc_files: []
53
61
  files:
54
- - lib/midi-eye/listener.rb
55
- - lib/midi-eye/unimidi_input.rb
62
+ - LICENSE
63
+ - README.md
56
64
  - lib/midi-eye.rb
65
+ - lib/midi-eye/listener.rb
66
+ - lib/midi-eye/source.rb
57
67
  - test/helper.rb
58
- - test/test_listener.rb
59
- - LICENSE
60
- - README.rdoc
68
+ - test/listener_test.rb
61
69
  homepage: http://github.com/arirusso/midi-eye
62
- licenses: []
70
+ licenses:
71
+ - Apache 2.0
72
+ metadata: {}
63
73
  post_install_message:
64
74
  rdoc_options: []
65
75
  require_paths:
66
76
  - lib
67
77
  required_ruby_version: !ruby/object:Gem::Requirement
68
- none: false
69
78
  requirements:
70
- - - ! '>='
79
+ - - ">="
71
80
  - !ruby/object:Gem::Version
72
81
  version: '0'
73
82
  required_rubygems_version: !ruby/object:Gem::Requirement
74
- none: false
75
83
  requirements:
76
- - - ! '>='
84
+ - - ">="
77
85
  - !ruby/object:Gem::Version
78
86
  version: 1.3.6
79
87
  requirements: []
80
88
  rubyforge_project: midi-eye
81
- rubygems_version: 1.8.6
89
+ rubygems_version: 2.2.2
82
90
  signing_key:
83
- specification_version: 3
91
+ specification_version: 4
84
92
  summary: MIDI event listener for Ruby
85
93
  test_files: []
data/README.rdoc DELETED
@@ -1,73 +0,0 @@
1
- = midi-eye
2
-
3
- A transparent MIDI input event listener for Ruby
4
-
5
- == Requirements
6
-
7
- * {midi-message}[http://github.com/arirusso/midi-message]
8
- * {nibbler}[http://github.com/arirusso/nibbler]
9
- * {unimidi}[http://github.com/arirusso/unimidi]
10
-
11
- == Install
12
-
13
- gem install midi-eye
14
-
15
- == Usage
16
-
17
- require 'midi-eye'
18
-
19
- The following is an example that takes any note messages received from a unimidi input, transposes them up one octave and then sends them to an output
20
-
21
- First, initialize the MIDI IO ports
22
-
23
- @input = UniMIDI::Input.gets
24
- @output = UniMIDI::Output.gets
25
-
26
- Then create a listener for the input port
27
-
28
- transpose = MIDIEye::Listener.new(@input)
29
-
30
- You can bind an event to the listener using Listener#listen_for
31
-
32
- The listener will try to positively match the parameters you pass in to the properties of the messages it receives.
33
-
34
- In this example, we will tell the listener to listen for note on/off messages which are easily identifiable by their class
35
-
36
- You also have the option of leaving out the parameters altogether and including using conditional if/unless/case/etc statements in your callback
37
-
38
- transpose.listen_for(:class => [MIDIMessage::NoteOn, MIDIMessage::NoteOff]) do |event|
39
-
40
- # raise the note value by an octave
41
- event[:message].note += 12
42
-
43
- # send the altered note message to the output you chose earlier
44
- @output.puts(event[:message])
45
-
46
- end
47
-
48
- You can bind as many events to a listener as you wish, just keep calling Listener#listen_for
49
-
50
- Once all the events are bound, start the listener
51
-
52
- transpose.run
53
-
54
- A listener can also be run in a background thread by passing in :background => true.
55
-
56
- transpose.run(:background => true)
57
-
58
- transpose.join # join the background thread later
59
-
60
- == Documentation
61
-
62
- * {examples}[http://github.com/arirusso/midi-eye/tree/master/examples]
63
- * {rdoc}[http://rdoc.info/gems/midi-eye]
64
-
65
- == Author
66
-
67
- * {Ari Russo}[http://github.com/arirusso] <ari.russo at gmail.com>
68
-
69
- == License
70
-
71
- Apache 2.0, See the file LICENSE
72
-
73
- Copyright (c) 2011 Ari Russo
@@ -1,44 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #
3
- module MIDIEye
4
-
5
- # this class deals with retrieving new messages from
6
- # a unimidi input buffer
7
- class UniMIDIInput
8
-
9
- attr_reader :device, :pointer
10
-
11
- def initialize(input)
12
- @parser = Nibbler.new
13
- @pointer = 0
14
- @device = input
15
- end
16
-
17
- # this grabs new messages from the unimidi buffer
18
- def poll(&block)
19
- msgs = @device.buffer.slice(@pointer, @device.buffer.length - @pointer)
20
- @pointer = @device.buffer.length
21
- msgs.each do |raw_msg|
22
- unless raw_msg.nil?
23
- objs = [@parser.parse(raw_msg[:data], :timestamp => raw_msg[:timestamp])].flatten.compact rescue []
24
- yield(objs)
25
- end
26
- end
27
- end
28
-
29
- # if <em>input</em> looks like a unimidi input, this returns true
30
- def self.is_compatible?(input)
31
- input.respond_to?(:gets) && input.respond_to?(:buffer)
32
- end
33
-
34
- # if this source was created from <em>input</em>
35
- def uses?(input)
36
- @device == input
37
- end
38
-
39
- # add this class to the Listener class' known input types
40
- Listener.input_types << self
41
-
42
- end
43
-
44
- end