micromidi 0.1.3 → 0.1.4

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