micromidi 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f2f1df337f1568e1e5b327664ee14734eae8095a
4
- data.tar.gz: 45fa44750f71f646414883b7103ccadf9162c7d6
3
+ metadata.gz: d899af108dba8fc0eb7b830cdc53ecf65f8a9946
4
+ data.tar.gz: 947eb84dfbba8084a55359b460effd7da0bfa881
5
5
  SHA512:
6
- metadata.gz: 9afb9c4ef330f7cb29b9baf02d37371055487f27f6833f46d99dc46c81161fe0dba982f27572f00422b8dfa20566d6901fbd34069df83ac65399c9e9e38e3a8d
7
- data.tar.gz: fa2eb27bdb3162c12a8fac1f6ba72edba97e5e1e6891a43df5c7ca20afe8f006b64ebb41239e544d15bc6495c5fafb9f0e896060b6ab7015c3ebfdc206899e25
6
+ metadata.gz: 738e6469b03feff4fdf5f64027ca32f2d6341c18f0f2767b0dcc810b265c16cf4290fc13ac862ec0eeedc339ef43d6379ba590c93612fccce2d68e15450a697e
7
+ data.tar.gz: c9b87df40f972394d975d23d3ba35de7b8bf145efce20857504f069d02857e4c53401ce9358a7e88d1b401c09d1d536734a7f5efc84360db143ad0c4430586a4
data/lib/micromidi.rb CHANGED
@@ -3,8 +3,8 @@
3
3
  #
4
4
  # A Ruby DSL for MIDI
5
5
  #
6
- # (c)2011-2014 Ari Russo
7
- # licensed under the Apache 2.0 License
6
+ # (c)2011-2014 Ari Russo
7
+ # Apache 2.0 License
8
8
  #
9
9
 
10
10
  # libs
@@ -15,6 +15,7 @@ require "midi-message"
15
15
  require "unimidi"
16
16
 
17
17
  # modules
18
+ require "micromidi/device"
18
19
  require "micromidi/instructions/composite"
19
20
  require "micromidi/module_methods"
20
21
 
@@ -32,9 +33,8 @@ require "micromidi/instructions/sysex"
32
33
  require "micromidi/instructions/shorthand"
33
34
 
34
35
  module MicroMIDI
35
-
36
- VERSION = "0.1.3"
36
+
37
+ VERSION = "0.1.4"
37
38
 
38
39
  end
39
40
  MIDI = MicroMIDI
40
-
@@ -1,41 +1,54 @@
1
1
  module MicroMIDI
2
-
2
+
3
+ # The DSL context
3
4
  class Context
4
-
5
+
5
6
  include Instructions::Composite
6
7
  extend Forwardable
7
-
8
+
8
9
  attr_reader :state
9
-
10
+
10
11
  def_delegator :state, :output_cache, :cache
11
-
12
- def initialize(ins, outs, &block)
13
-
14
- @state = State.new(ins, outs)
15
-
12
+
13
+ # @param [Array<UniMIDI::Input>, UniMIDI::Input] inputs
14
+ # @param [Array<UniMIDI::Output, IO>, IO, UniMIDI::Output] outputs
15
+ # @param [Proc] block
16
+ def initialize(inputs, outputs, &block)
17
+
18
+ @state = State.new(inputs, outputs)
19
+
16
20
  @instructions = {
17
21
  :process => Instructions::Process.new(@state),
18
- :input => Instructions::Input.new(@state),
22
+ :input => Instructions::Input.new(@state),
19
23
  :message => Instructions::Message.new(@state),
20
24
  :output => Instructions::Output.new(@state),
21
25
  :sticky => Instructions::Sticky.new(@state),
22
26
  :sysex => Instructions::SysEx.new(@state)
23
27
  }
24
-
25
- edit(&block) unless block.nil?
28
+
29
+ edit(&block) if block_given?
26
30
  end
27
-
28
- # Open a block for editing/live coding in this Context
31
+
32
+ # Eval a block for editing/live coding in this context
33
+ # @param [Proc] block
34
+ # @return [Object]
29
35
  def edit(&block)
30
36
  instance_eval(&block)
31
37
  end
32
-
38
+
33
39
  # Repeat the last instruction in the history
40
+ # @return [Object]
34
41
  def repeat
35
- send(@state.last_command[:method], *@state.last_command[:args]) unless @state.last_command.nil?
42
+ unless @state.last_command.nil?
43
+ send(@state.last_command[:method], *@state.last_command[:args])
44
+ end
36
45
  end
37
-
46
+
38
47
  # Delegates a command to one of the instruction classes
48
+ # @param [Symbol] method
49
+ # @param [*Object] args
50
+ # @param [Proc] block
51
+ # @return [Object]
39
52
  def method_missing(method, *args, &block)
40
53
  results = delegate(method, args, &block)
41
54
  if results.empty?
@@ -53,11 +66,17 @@ module MicroMIDI
53
66
  private
54
67
 
55
68
  # Should a message that resulted from the given instruction type be outputted?
69
+ # @param [Symbol] instruction_type
70
+ # @return [Boolean]
56
71
  def output?(instruction_type)
57
72
  @state.auto_output && [:sysex, :message, :process].include?(instruction_type)
58
73
  end
59
74
 
60
- # Delegate a command
75
+ # Delegate a command
76
+ # @param [Symbol] method
77
+ # @param [*Object] args
78
+ # @param [Proc] block
79
+ # @return [Object]
61
80
  def delegate(method, args, &block)
62
81
  results = @instructions.map do |key, instruction|
63
82
  if instruction.respond_to?(method)
@@ -70,6 +89,6 @@ module MicroMIDI
70
89
  end
71
90
  results.compact
72
91
  end
73
-
92
+
74
93
  end
75
94
  end
@@ -0,0 +1,37 @@
1
+ module MicroMIDI
2
+
3
+ # Deal with MIDI devices
4
+ module Device
5
+
6
+ extend self
7
+
8
+ # Is the given device a MIDI input?
9
+ # @param [Object] device
10
+ # @return [Boolean]
11
+ def input?(device)
12
+ device.respond_to?(:type) && device.type == :input && device.respond_to?(:gets)
13
+ end
14
+
15
+ # Is the given device a MIDI output?
16
+ # @param [Object] device
17
+ # @return [Boolean]
18
+ def output?(device)
19
+ device.respond_to?(:puts)
20
+ end
21
+
22
+ # Select the MIDI inputs from the given objects
23
+ # @params [*Object] args
24
+ # @return [Array<UniMIDI::Input>]
25
+ def get_inputs(*args)
26
+ [args].flatten.select { |device| input?(device) }
27
+ end
28
+
29
+ # Select the MIDI outputs from the given objects
30
+ # @params [*Object] args
31
+ # @return [Array<UniMIDI::Output, IO>]
32
+ def get_outputs(*args)
33
+ [args].flatten.select { |device| output?(device) }
34
+ end
35
+
36
+ end
37
+ end
@@ -1,31 +1,35 @@
1
1
  module MicroMIDI
2
2
 
3
3
  module Instructions
4
-
4
+
5
+ # Commands that are composites of other commands
5
6
  module Composite
6
-
7
+
7
8
  #
8
- # Plays a note or notes, for a certain duration.
9
+ # Play a note or notes, for the given duration.
9
10
  #
10
- # The first argument must be a note name (String), MIDIMessage::NoteOn object, or array of either
11
- # the last argument must be a Numeric (representing the duration)
11
+ # The first argument must be: [Array<String>, Array<MIDIMessage::NoteOn>, String, MIDIMessage::NoteOn]
12
+ # The last argument must be [Numeric] representing the duration
12
13
  #
13
- # Additional arguments should be note names or MIDIMessage::NoteOn objects and will
14
- # be played as a chord with the first argument.
14
+ # Additional arguments can be [Array<String>, Array<MIDIMessage::NoteOn>, String, MIDIMessage::NoteOn] and will
15
+ # be played as a chord simultaneously with the first argument.
15
16
  #
17
+ # @param [*Object] args
18
+ # @return [Array<MIDIMessage::NoteOn>, MIDIMessage::NoteOn]
16
19
  def play(*args)
17
20
  raise "Last argument must be a Numeric duration" unless args.last.kind_of?(Numeric)
18
-
21
+ args = args.dup
19
22
  duration = args.pop
20
23
  note_or_notes = [args].flatten
21
24
  messages = as_note_messages(note_or_notes)
22
25
  sleep(duration)
23
26
  send_note_offs(messages)
24
-
27
+
25
28
  messages.count > 1 ? messages : messages.first
26
29
  end
27
-
28
- # sends a note off message for every note on every channel
30
+
31
+ # Send a note off message for every note on every channel
32
+ # @return [Boolean]
29
33
  def all_off
30
34
  (0..15).each do |channel|
31
35
  (0..127).each do |note_num|
@@ -38,21 +42,28 @@ module MicroMIDI
38
42
 
39
43
  private
40
44
 
45
+ # Send note off messages for the given note messages
46
+ # @param [Array<MIDIMessage::NoteOn, MIDIMessage::NoteOff>] messages
47
+ # @return [Boolean]
41
48
  def send_note_offs(messages)
42
- messages.each do |message|
49
+ messages.each do |message|
43
50
  note_off(message.note, :channel => message.channel, :velocity => message.velocity)
44
51
  end
52
+ true
45
53
  end
46
54
 
55
+ # MIDI note message objects for the given arguments
56
+ # @param [Array<MIDIMessage::NoteOn, MIDIMessage::NoteOff, Fixnum, String>] note_or_notes
57
+ # @return [Array<MIDIMessage::NoteOn>]
47
58
  def as_note_messages(note_or_notes)
48
- note_or_notes.map do |note|
49
- case note
50
- when Numeric, String then note(note)
51
- when MIDIMessage then note
59
+ note_or_notes.map do |item|
60
+ case item
61
+ when Fixnum, String then note(item)
62
+ when MIDIMessage then item
52
63
  end
53
64
  end
54
65
  end
55
-
66
+
56
67
  end
57
68
 
58
69
  end
@@ -1,23 +1,29 @@
1
1
  module MicroMIDI
2
2
 
3
3
  module Instructions
4
-
4
+
5
+ # Commands dealing with MIDI input
5
6
  class Input
6
-
7
+
8
+ # @param [State] state
7
9
  def initialize(state)
8
10
  @state = state
9
11
  end
10
12
 
11
- # bind an event that will be called every time a message is received
12
- def receive(*args, &block)
13
- message_options = args.dup
14
- options = message_options.last.kind_of?(Hash) ? message_options.pop : {}
15
- unless message_options.empty?
16
- match = { :class => message_classes(message_options) }
13
+ # Bind an event that will be fired when a message is received
14
+ # @param [*Object] args Types of messages to filter on eg :note_on, :control_change
15
+ # @param [Proc] callback The event callback
16
+ # @return [Boolean]
17
+ def receive(*args, &callback)
18
+ args = args.dup
19
+ options = args.last.kind_of?(Hash) ? args.pop : {}
20
+ unless args.empty?
21
+ match = { :class => message_classes(args) }
17
22
  end
18
- listener(match, options) do |event|
23
+ listener(match, options) do |event|
19
24
  yield(event[:message], event[:timestamp])
20
25
  end
26
+ true
21
27
  end
22
28
  alias_method :gets, :receive
23
29
  alias_method :handle, :receive
@@ -25,11 +31,15 @@ module MicroMIDI
25
31
  alias_method :listen_for, :receive
26
32
  alias_method :when_receive, :receive
27
33
 
28
- def receive_unless(*args, &block)
29
- message_options = args.dup
30
- options = message_options.last.kind_of?(Hash) ? message_options.pop : {}
31
- match = message_classes(message_options)
32
- listener(nil, options) do |event|
34
+ # Bind an event that will be fired when a message is received
35
+ # @param [*Object] args Types of messages to filter out eg :note_on, :control_change
36
+ # @param [Proc] callback The event callback
37
+ # @return [Boolean]
38
+ def receive_unless(*args, &callback)
39
+ args = args.dup
40
+ options = args.last.kind_of?(Hash) ? args.pop : {}
41
+ match = message_classes(args)
42
+ listener(nil, options) do |event|
33
43
  yield(event[:message], event[:timestamp]) unless match.include?(event[:message].class)
34
44
  end
35
45
  end
@@ -38,45 +48,66 @@ module MicroMIDI
38
48
  alias_method :listen_for_unless, :receive_unless
39
49
  alias_method :unless_receive, :receive_unless
40
50
 
41
- # send input messages thru to the outputs
51
+ # Send input messages thru to the outputs
42
52
  def thru
43
53
  thru_if
44
54
  end
45
55
 
46
- # send input messages thru to the outputs if it has a specific class
56
+ # Send input messages thru to the outputs if they have a specified class
57
+ # @param [*Object] args
58
+ # @return [Boolean]
47
59
  def thru_if(*args)
48
- receive_options = thru_options(args)
60
+ receive_options = thru_arguments(args)
49
61
  receive(*receive_options) { |message, timestamp| output(message) }
62
+ true
50
63
  end
51
64
 
52
- # send input messages thru to the outputs unless of a specific class
65
+ # Send input messages thru to the outputs unless they're of the specified class
66
+ # @param [*Object] args
67
+ # @return [Boolean]
53
68
  def thru_unless(*args)
54
- receive_options = thru_options(args)
69
+ receive_options = thru_arguments(args)
55
70
  receive_unless(*receive_options) { |message, timestamp| output(message) }
56
71
  end
57
-
58
- # like <em>thru_unless</em> except a block can be passed that will be called when
59
- # notes specified as the <em>unless</em> arrive
60
- def thru_except(*args, &block)
72
+
73
+ # Similar to Input#thru_unless except a callback can be passed that will be fired when notes specified arrive
74
+ # @param [*Object] args
75
+ # @param [Proc] callback
76
+ # @return [Boolean]
77
+ def thru_except(*args, &callback)
61
78
  thru_unless(*args)
62
- receive(*args, &block)
79
+ receive(*args, &callback)
63
80
  end
64
-
65
- # wait for input on the last input passed in
66
- # can pass the option :from => [an input] to specify which one to wait on
81
+
82
+ # Wait for input on the last input passed in (blocking)
83
+ # Can pass the option :from => [an input] to specify which one to wait on
84
+ # @param [Hash] options
85
+ # @option options [UniMIDI::Input] :from
86
+ # @return [Boolean]
67
87
  def wait_for_input(options = {})
68
88
  listener = options[:from] || @state.listeners.last || @state.thru_listeners.last
69
89
  listener.join
90
+ true
70
91
  end
71
-
92
+
93
+ # Join the listener thread
94
+ # @return [Boolean]
72
95
  def join
73
96
  loop { wait_for_input }
97
+ true
74
98
  end
75
99
 
76
100
  protected
77
101
 
102
+ # Initialize input event listeners for the given inputs and options
103
+ # @param [Hash] match
104
+ # @param [Hash] options
105
+ # option options [Array<UniMIDI::Input>, UniMIDI::Input] :from
106
+ # option options [Boolean] :start
107
+ # option options [Boolean] :thru
78
108
  def listener(match, options = {}, &block)
79
109
  inputs = options[:from] || @state.inputs
110
+ inputs = [inputs].flatten
80
111
  do_thru = options.fetch(:thru, false)
81
112
  should_start = options.fetch(:start, true)
82
113
  match ||= {}
@@ -89,21 +120,29 @@ module MicroMIDI
89
120
 
90
121
  private
91
122
 
92
- def initialize_listener(input, match, do_thru, &block)
123
+ # Initialize an input event listener
124
+ # @param [UniMIDI::Input] input
125
+ # @param [Hash] match
126
+ # @param [Boolean] do_thru
127
+ # @param [Proc] callback
128
+ # @return [MIDIEye::Listener]
129
+ def initialize_listener(input, match, do_thru, &callback)
93
130
  listener = MIDIEye::Listener.new(input)
94
- listener.listen_for(match, &block)
131
+ listener.listen_for(match, &callback)
95
132
  if do_thru
96
133
  @state.thru_listeners.each(&:stop)
97
134
  @state.thru_listeners.clear
98
135
  @state.thru_listeners << listener
99
- else
136
+ else
100
137
  @state.listeners << listener
101
- end
138
+ end
102
139
  listener
103
140
  end
104
141
 
105
- # The options for using thru
106
- def thru_options(args)
142
+ # The arguments for using thru
143
+ # @param [Array<Object>] args
144
+ # @return [Array<Object, Hash>]
145
+ def thru_arguments(args)
107
146
  receive_options = args.dup
108
147
  if receive_options.last.kind_of?(Hash)
109
148
  receive_options.last[:thru] = true
@@ -113,9 +152,12 @@ module MicroMIDI
113
152
  receive_options
114
153
  end
115
154
 
116
- def message_classes(list)
117
- list.map do |type|
118
- case type
155
+ # Get the MIDIMessage class for the given note name
156
+ # @param [Array<String, Symbol>] type_list
157
+ # @return [Array<Class>]
158
+ def message_classes(type_list)
159
+ classes = type_list.map do |type|
160
+ case type.to_sym
119
161
  when :aftertouch, :pressure, :aft then [MIDIMessage::ChannelAftertouch, MIDIMessage::PolyphonicAftertouch]
120
162
  when :channel_aftertouch, :channel_pressure, :ca, :cp then MIDIMessage::ChannelAftertouch
121
163
  when :control_change, :cc, :c then MIDIMessage::ControlChange
@@ -126,7 +168,8 @@ module MicroMIDI
126
168
  when :polyphonic_aftertouch, :poly_aftertouch, :poly_pressure, :polyphonic_pressure, :pa, :pp then MIDIMessage::PolyphonicAftertouch
127
169
  when :program_change, :pc, :p then MIDIMessage::ProgramChange
128
170
  end
129
- end.flatten.compact
171
+ end
172
+ classes.flatten.compact
130
173
  end
131
174
 
132
175
  end