micromidi 0.1.2 → 0.1.3
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 +4 -4
- data/lib/micromidi.rb +10 -39
- data/lib/micromidi/context.rb +34 -21
- data/lib/micromidi/instructions/composite.rb +25 -15
- data/lib/micromidi/instructions/input.rb +67 -43
- data/lib/micromidi/instructions/message.rb +43 -39
- data/lib/micromidi/instructions/output.rb +20 -7
- data/lib/micromidi/instructions/shorthand.rb +2 -2
- data/lib/micromidi/module_methods.rb +34 -0
- data/lib/micromidi/state.rb +30 -19
- data/lib/midi.rb +2 -2
- data/test/input_test.rb +0 -4
- data/test/sysex_test.rb +24 -26
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f2f1df337f1568e1e5b327664ee14734eae8095a
|
|
4
|
+
data.tar.gz: 45fa44750f71f646414883b7103ccadf9162c7d6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9afb9c4ef330f7cb29b9baf02d37371055487f27f6833f46d99dc46c81161fe0dba982f27572f00422b8dfa20566d6901fbd34069df83ac65399c9e9e38e3a8d
|
|
7
|
+
data.tar.gz: fa2eb27bdb3162c12a8fac1f6ba72edba97e5e1e6891a43df5c7ca20afe8f006b64ebb41239e544d15bc6495c5fafb9f0e896060b6ab7015c3ebfdc206899e25
|
data/lib/micromidi.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#
|
|
2
|
-
#
|
|
2
|
+
# MicroMIDI
|
|
3
3
|
#
|
|
4
4
|
# A Ruby DSL for MIDI
|
|
5
5
|
#
|
|
@@ -14,46 +14,9 @@ require "midi-fx"
|
|
|
14
14
|
require "midi-message"
|
|
15
15
|
require "unimidi"
|
|
16
16
|
|
|
17
|
-
module MicroMIDI
|
|
18
|
-
|
|
19
|
-
VERSION = "0.1.2"
|
|
20
|
-
|
|
21
|
-
module Instructions
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def self.new(*a, &block)
|
|
25
|
-
message(*a, &block)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def self.message(*args, &block)
|
|
29
|
-
ins, outs = *process_devices(args)
|
|
30
|
-
MicroMIDI::Context.new(ins, outs, &block)
|
|
31
|
-
end
|
|
32
|
-
class << self
|
|
33
|
-
alias_method :using, :message
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
module IO
|
|
37
|
-
|
|
38
|
-
def self.new(*args, &block)
|
|
39
|
-
MicroMIDI.message(*args, &block)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
private
|
|
45
|
-
|
|
46
|
-
def self.process_devices(args)
|
|
47
|
-
ins = args.find_all { |d| d.respond_to?(:type) && d.type == :input && d.respond_to?(:gets) }
|
|
48
|
-
outs = args.find_all { |d| d.respond_to?(:puts) }
|
|
49
|
-
[ins, outs]
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
end
|
|
53
|
-
MIDI = MicroMIDI
|
|
54
|
-
|
|
55
17
|
# modules
|
|
56
18
|
require "micromidi/instructions/composite"
|
|
19
|
+
require "micromidi/module_methods"
|
|
57
20
|
|
|
58
21
|
# classes
|
|
59
22
|
require "micromidi/context"
|
|
@@ -67,3 +30,11 @@ require "micromidi/instructions/sysex"
|
|
|
67
30
|
|
|
68
31
|
# extension
|
|
69
32
|
require "micromidi/instructions/shorthand"
|
|
33
|
+
|
|
34
|
+
module MicroMIDI
|
|
35
|
+
|
|
36
|
+
VERSION = "0.1.3"
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
MIDI = MicroMIDI
|
|
40
|
+
|
data/lib/micromidi/context.rb
CHANGED
|
@@ -25,37 +25,50 @@ module MicroMIDI
|
|
|
25
25
|
edit(&block) unless block.nil?
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
#
|
|
28
|
+
# Open a block for editing/live coding in this Context
|
|
29
29
|
def edit(&block)
|
|
30
|
-
|
|
30
|
+
instance_eval(&block)
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
# Repeat the last instruction in the history
|
|
33
34
|
def repeat
|
|
34
|
-
|
|
35
|
+
send(@state.last_command[:method], *@state.last_command[:args]) unless @state.last_command.nil?
|
|
35
36
|
end
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
# Delegates a command to one of the instruction classes
|
|
39
|
+
def method_missing(method, *args, &block)
|
|
40
|
+
results = delegate(method, args, &block)
|
|
41
|
+
if results.empty?
|
|
42
|
+
super
|
|
43
|
+
else
|
|
44
|
+
messages = results.map do |result|
|
|
45
|
+
@state.record(method, args, block, result[:message])
|
|
46
|
+
@instructions[:output].output(result[:message]) if output?(result[:instruction_type])
|
|
47
|
+
result[:message]
|
|
47
48
|
end
|
|
49
|
+
messages.compact.first
|
|
48
50
|
end
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
# Should a message that resulted from the given instruction type be outputted?
|
|
56
|
+
def output?(instruction_type)
|
|
57
|
+
@state.auto_output && [:sysex, :message, :process].include?(instruction_type)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Delegate a command
|
|
61
|
+
def delegate(method, args, &block)
|
|
62
|
+
results = @instructions.map do |key, instruction|
|
|
63
|
+
if instruction.respond_to?(method)
|
|
64
|
+
message = instruction.send(method, *args, &block)
|
|
65
|
+
{
|
|
66
|
+
:instruction_type => key,
|
|
67
|
+
:message => message
|
|
68
|
+
}
|
|
55
69
|
end
|
|
56
70
|
end
|
|
57
|
-
|
|
58
|
-
delegated ? outp : super
|
|
71
|
+
results.compact
|
|
59
72
|
end
|
|
60
73
|
|
|
61
74
|
end
|
|
@@ -5,31 +5,24 @@ module MicroMIDI
|
|
|
5
5
|
module Composite
|
|
6
6
|
|
|
7
7
|
#
|
|
8
|
-
#
|
|
8
|
+
# Plays a note or notes, for a certain duration.
|
|
9
9
|
#
|
|
10
|
-
#
|
|
10
|
+
# The first argument must be a note name (String), MIDIMessage::NoteOn object, or array of either
|
|
11
11
|
# the last argument must be a Numeric (representing the duration)
|
|
12
12
|
#
|
|
13
|
-
#
|
|
14
|
-
# be played as a chord with the first argument
|
|
13
|
+
# Additional arguments should be note names or MIDIMessage::NoteOn objects and will
|
|
14
|
+
# be played as a chord with the first argument.
|
|
15
15
|
#
|
|
16
16
|
def play(*args)
|
|
17
|
-
raise "
|
|
17
|
+
raise "Last argument must be a Numeric duration" unless args.last.kind_of?(Numeric)
|
|
18
18
|
|
|
19
19
|
duration = args.pop
|
|
20
20
|
note_or_notes = [args].flatten
|
|
21
|
-
|
|
22
|
-
msgs = note_or_notes.map do |n|
|
|
23
|
-
case n
|
|
24
|
-
when Numeric, String then note(n)
|
|
25
|
-
when MIDIMessage then n
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
21
|
+
messages = as_note_messages(note_or_notes)
|
|
29
22
|
sleep(duration)
|
|
30
|
-
|
|
23
|
+
send_note_offs(messages)
|
|
31
24
|
|
|
32
|
-
|
|
25
|
+
messages.count > 1 ? messages : messages.first
|
|
33
26
|
end
|
|
34
27
|
|
|
35
28
|
# sends a note off message for every note on every channel
|
|
@@ -42,6 +35,23 @@ module MicroMIDI
|
|
|
42
35
|
true
|
|
43
36
|
end
|
|
44
37
|
alias_method :quiet!, :all_off
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def send_note_offs(messages)
|
|
42
|
+
messages.each do |message|
|
|
43
|
+
note_off(message.note, :channel => message.channel, :velocity => message.velocity)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
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
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
45
55
|
|
|
46
56
|
end
|
|
47
57
|
|
|
@@ -3,18 +3,21 @@ module MicroMIDI
|
|
|
3
3
|
module Instructions
|
|
4
4
|
|
|
5
5
|
class Input
|
|
6
|
-
|
|
7
|
-
include MIDIMessage
|
|
8
|
-
|
|
6
|
+
|
|
9
7
|
def initialize(state)
|
|
10
8
|
@state = state
|
|
11
9
|
end
|
|
12
10
|
|
|
13
11
|
# bind an event that will be called every time a message is received
|
|
14
|
-
def receive(*
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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) }
|
|
17
|
+
end
|
|
18
|
+
listener(match, options) do |event|
|
|
19
|
+
yield(event[:message], event[:timestamp])
|
|
20
|
+
end
|
|
18
21
|
end
|
|
19
22
|
alias_method :gets, :receive
|
|
20
23
|
alias_method :handle, :receive
|
|
@@ -22,10 +25,13 @@ module MicroMIDI
|
|
|
22
25
|
alias_method :listen_for, :receive
|
|
23
26
|
alias_method :when_receive, :receive
|
|
24
27
|
|
|
25
|
-
def receive_unless(*
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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|
|
|
33
|
+
yield(event[:message], event[:timestamp]) unless match.include?(event[:message].class)
|
|
34
|
+
end
|
|
29
35
|
end
|
|
30
36
|
alias_method :handle_unless, :receive_unless
|
|
31
37
|
alias_method :listen_unless, :receive_unless
|
|
@@ -38,22 +44,22 @@ module MicroMIDI
|
|
|
38
44
|
end
|
|
39
45
|
|
|
40
46
|
# send input messages thru to the outputs if it has a specific class
|
|
41
|
-
def thru_if(*
|
|
42
|
-
|
|
43
|
-
receive(*
|
|
47
|
+
def thru_if(*args)
|
|
48
|
+
receive_options = thru_options(args)
|
|
49
|
+
receive(*receive_options) { |message, timestamp| output(message) }
|
|
44
50
|
end
|
|
45
51
|
|
|
46
52
|
# send input messages thru to the outputs unless of a specific class
|
|
47
|
-
def thru_unless(*
|
|
48
|
-
|
|
49
|
-
receive_unless(*
|
|
53
|
+
def thru_unless(*args)
|
|
54
|
+
receive_options = thru_options(args)
|
|
55
|
+
receive_unless(*receive_options) { |message, timestamp| output(message) }
|
|
50
56
|
end
|
|
51
57
|
|
|
52
58
|
# like <em>thru_unless</em> except a block can be passed that will be called when
|
|
53
59
|
# notes specified as the <em>unless</em> arrive
|
|
54
|
-
def thru_except(*
|
|
55
|
-
thru_unless(*
|
|
56
|
-
receive(*
|
|
60
|
+
def thru_except(*args, &block)
|
|
61
|
+
thru_unless(*args)
|
|
62
|
+
receive(*args, &block)
|
|
57
63
|
end
|
|
58
64
|
|
|
59
65
|
# wait for input on the last input passed in
|
|
@@ -69,38 +75,56 @@ module MicroMIDI
|
|
|
69
75
|
|
|
70
76
|
protected
|
|
71
77
|
|
|
72
|
-
def listener(match
|
|
78
|
+
def listener(match, options = {}, &block)
|
|
73
79
|
inputs = options[:from] || @state.inputs
|
|
74
|
-
|
|
80
|
+
do_thru = options.fetch(:thru, false)
|
|
81
|
+
should_start = options.fetch(:start, true)
|
|
75
82
|
match ||= {}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
@state.thru_listeners.each { |l| l.stop }
|
|
81
|
-
@state.thru_listeners.clear
|
|
82
|
-
@state.thru_listeners << listener
|
|
83
|
-
else
|
|
84
|
-
@state.listeners << listener
|
|
85
|
-
end
|
|
86
|
-
listener.start(:background => true) unless !options[:start].nil? && !options[:start]
|
|
83
|
+
|
|
84
|
+
listeners = inputs.map { |input| initialize_listener(input, match, do_thru, &block) }
|
|
85
|
+
if should_start
|
|
86
|
+
listeners.each { |listener| listener.start(:background => true) }
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
private
|
|
91
91
|
|
|
92
|
-
def
|
|
92
|
+
def initialize_listener(input, match, do_thru, &block)
|
|
93
|
+
listener = MIDIEye::Listener.new(input)
|
|
94
|
+
listener.listen_for(match, &block)
|
|
95
|
+
if do_thru
|
|
96
|
+
@state.thru_listeners.each(&:stop)
|
|
97
|
+
@state.thru_listeners.clear
|
|
98
|
+
@state.thru_listeners << listener
|
|
99
|
+
else
|
|
100
|
+
@state.listeners << listener
|
|
101
|
+
end
|
|
102
|
+
listener
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# The options for using thru
|
|
106
|
+
def thru_options(args)
|
|
107
|
+
receive_options = args.dup
|
|
108
|
+
if receive_options.last.kind_of?(Hash)
|
|
109
|
+
receive_options.last[:thru] = true
|
|
110
|
+
else
|
|
111
|
+
receive_options << { :thru => true }
|
|
112
|
+
end
|
|
113
|
+
receive_options
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def message_classes(list)
|
|
93
117
|
list.map do |type|
|
|
94
118
|
case type
|
|
95
|
-
when :aftertouch, :pressure, :aft then [ChannelAftertouch, PolyphonicAftertouch]
|
|
96
|
-
when :channel_aftertouch, :channel_pressure, :ca, :cp then ChannelAftertouch
|
|
97
|
-
when :control_change, :cc, :c then ControlChange
|
|
98
|
-
when :note, :n then [NoteOn, NoteOff]
|
|
99
|
-
when :note_on, :nn then NoteOn
|
|
100
|
-
when :note_off, :no, :off then NoteOff
|
|
101
|
-
when :pitch_bend, :pb then PitchBend
|
|
102
|
-
when :polyphonic_aftertouch, :poly_aftertouch, :poly_pressure, :polyphonic_pressure, :pa, :pp then PolyphonicAftertouch
|
|
103
|
-
when :program_change, :pc, :p then ProgramChange
|
|
119
|
+
when :aftertouch, :pressure, :aft then [MIDIMessage::ChannelAftertouch, MIDIMessage::PolyphonicAftertouch]
|
|
120
|
+
when :channel_aftertouch, :channel_pressure, :ca, :cp then MIDIMessage::ChannelAftertouch
|
|
121
|
+
when :control_change, :cc, :c then MIDIMessage::ControlChange
|
|
122
|
+
when :note, :n then [MIDIMessage::NoteOn, MIDIMessage::NoteOff]
|
|
123
|
+
when :note_on, :nn then MIDIMessage::NoteOn
|
|
124
|
+
when :note_off, :no, :off then MIDIMessage::NoteOff
|
|
125
|
+
when :pitch_bend, :pb then MIDIMessage::PitchBend
|
|
126
|
+
when :polyphonic_aftertouch, :poly_aftertouch, :poly_pressure, :polyphonic_pressure, :pa, :pp then MIDIMessage::PolyphonicAftertouch
|
|
127
|
+
when :program_change, :pc, :p then MIDIMessage::ProgramChange
|
|
104
128
|
end
|
|
105
129
|
end.flatten.compact
|
|
106
130
|
end
|
|
@@ -3,41 +3,33 @@ module MicroMIDI
|
|
|
3
3
|
module Instructions
|
|
4
4
|
|
|
5
5
|
class Message
|
|
6
|
-
|
|
7
|
-
include MIDIMessage
|
|
8
6
|
|
|
9
7
|
def initialize(state)
|
|
10
8
|
@state = state
|
|
11
9
|
end
|
|
12
10
|
|
|
13
11
|
# create a control change message
|
|
14
|
-
def control_change(id, value,
|
|
15
|
-
|
|
16
|
-
id.kind_of?(Numeric)
|
|
12
|
+
def control_change(id, value, options = {})
|
|
13
|
+
properties = @state.message_properties(options, :channel)
|
|
14
|
+
if id.kind_of?(Numeric)
|
|
15
|
+
MIDIMessage::ControlChange.new(properties[:channel], id, value)
|
|
16
|
+
else
|
|
17
|
+
MIDIMessage::ControlChange[id].new(properties[:channel], value)
|
|
18
|
+
end
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
# create a note message
|
|
20
|
-
def note(id,
|
|
21
|
-
|
|
22
|
-
note =
|
|
23
|
-
NoteOn.new(props[:channel], id, props[:velocity])
|
|
24
|
-
elsif id.kind_of?(String) || id.kind_of?(Symbol)
|
|
25
|
-
note_string = parse_note_name(id)
|
|
26
|
-
NoteOn[note_string].new(props[:channel], props[:velocity])
|
|
27
|
-
end
|
|
22
|
+
def note(id, options = {})
|
|
23
|
+
properties = @state.message_properties(options, :channel, :velocity)
|
|
24
|
+
note = note_message(MIDIMessage::NoteOn, id, properties)
|
|
28
25
|
@state.last_note = note
|
|
29
26
|
note
|
|
30
27
|
end
|
|
31
28
|
|
|
32
29
|
# create a note off message
|
|
33
|
-
def note_off(id,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
NoteOff.new(props[:channel], id, props[:velocity])
|
|
37
|
-
elsif id.kind_of?(String) || id.kind_of?(Symbol)
|
|
38
|
-
note_string = parse_note_name(id)
|
|
39
|
-
NoteOff[parse_note_name(id)].new(props[:channel], props[:velocity])
|
|
40
|
-
end
|
|
30
|
+
def note_off(id, options = {})
|
|
31
|
+
properties = @state.message_properties(options, :channel, :velocity)
|
|
32
|
+
note_message(MIDIMessage::NoteOff, id, properties)
|
|
41
33
|
end
|
|
42
34
|
|
|
43
35
|
# create a MIDI message from a byte string, array of bytes, or list of bytes
|
|
@@ -46,37 +38,37 @@ module MicroMIDI
|
|
|
46
38
|
end
|
|
47
39
|
|
|
48
40
|
# create a program change message
|
|
49
|
-
def program_change(program,
|
|
50
|
-
|
|
51
|
-
MIDIMessage::ProgramChange.new(
|
|
41
|
+
def program_change(program, options = {})
|
|
42
|
+
properties = @state.message_properties(options, :channel)
|
|
43
|
+
MIDIMessage::ProgramChange.new(properties[:channel], program)
|
|
52
44
|
end
|
|
53
45
|
|
|
54
46
|
# create a note-off message from the last note-on message
|
|
55
47
|
def off
|
|
56
|
-
|
|
48
|
+
note_off = @state.last_note.to_note_off unless @state.last_note.nil?
|
|
57
49
|
@state.last_note = nil
|
|
58
|
-
|
|
50
|
+
note_off
|
|
59
51
|
end
|
|
60
52
|
|
|
61
53
|
# create a channel pressure message
|
|
62
|
-
def channel_aftertouch(value,
|
|
63
|
-
|
|
64
|
-
MIDIMessage::ChannelAftertouch.new(
|
|
54
|
+
def channel_aftertouch(value, options = {})
|
|
55
|
+
properties = @state.message_properties(options, :channel)
|
|
56
|
+
MIDIMessage::ChannelAftertouch.new(properties[:channel], value)
|
|
65
57
|
end
|
|
66
58
|
alias_method :channel_pressure, :channel_aftertouch
|
|
67
59
|
|
|
68
60
|
# create a poly pressure message
|
|
69
|
-
def polyphonic_aftertouch(note, value,
|
|
70
|
-
|
|
71
|
-
MIDIMessage::PolyphonicAftertouch.new(
|
|
61
|
+
def polyphonic_aftertouch(note, value, options = {})
|
|
62
|
+
properties = @state.message_properties(options, :channel)
|
|
63
|
+
MIDIMessage::PolyphonicAftertouch.new(properties[:channel], note, value)
|
|
72
64
|
end
|
|
73
65
|
alias_method :poly_aftertouch, :polyphonic_aftertouch
|
|
74
66
|
alias_method :polyphonic_pressure, :polyphonic_aftertouch
|
|
75
67
|
alias_method :poly_pressure, :polyphonic_aftertouch
|
|
76
68
|
|
|
77
|
-
def pitch_bend(low, high,
|
|
78
|
-
|
|
79
|
-
MIDIMessage::PitchBend.new(
|
|
69
|
+
def pitch_bend(low, high, options = {})
|
|
70
|
+
properties = @state.message_properties(options, :channel)
|
|
71
|
+
MIDIMessage::PitchBend.new(properties[:channel], low, high)
|
|
80
72
|
end
|
|
81
73
|
alias_method :bend, :pitch_bend
|
|
82
74
|
alias_method :pitchbend, :pitch_bend
|
|
@@ -84,11 +76,23 @@ module MicroMIDI
|
|
|
84
76
|
protected
|
|
85
77
|
|
|
86
78
|
def parse_note_name(name)
|
|
87
|
-
name = name.to_s
|
|
88
|
-
|
|
79
|
+
name = name.to_s
|
|
80
|
+
octave = name.scan(/-?\d\z/).first
|
|
81
|
+
string_options = { :octave => octave }
|
|
89
82
|
note = name.split(/-?\d\z/).first
|
|
90
|
-
|
|
91
|
-
"#{note}#{
|
|
83
|
+
string_properties = @state.message_properties(string_options, :octave)
|
|
84
|
+
"#{note}#{string_properties[:octave].to_s}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def note_message(klass, id, properties)
|
|
90
|
+
if id.kind_of?(Numeric)
|
|
91
|
+
klass.new(properties[:channel], id, properties[:velocity])
|
|
92
|
+
elsif id.kind_of?(String) || id.kind_of?(Symbol)
|
|
93
|
+
note_name = parse_note_name(id)
|
|
94
|
+
klass[note_name].new(properties[:channel], properties[:velocity])
|
|
95
|
+
end
|
|
92
96
|
end
|
|
93
97
|
|
|
94
98
|
end
|
|
@@ -4,19 +4,32 @@ module MicroMIDI
|
|
|
4
4
|
|
|
5
5
|
class Output
|
|
6
6
|
|
|
7
|
+
extend Forwardable
|
|
8
|
+
|
|
9
|
+
def_delegators :@state, :toggle_auto_output
|
|
10
|
+
alias_method :auto_output, :toggle_auto_output
|
|
11
|
+
|
|
12
|
+
# @param [State] state
|
|
7
13
|
def initialize(state)
|
|
8
14
|
@state = state
|
|
9
15
|
end
|
|
10
16
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
# Output a message or toggle the auto output mode
|
|
18
|
+
# @param [MIDIMessage, Boolean] message A MIDI message to output, or a boolean to toggle auto-output mode
|
|
19
|
+
# @return [BIDIMessage]
|
|
20
|
+
def output(message)
|
|
21
|
+
set_auto_output(message) if !!message === message # check for boolean
|
|
22
|
+
unless message.nil?
|
|
23
|
+
@state.outputs.each { |output| output.puts(message) }
|
|
24
|
+
end
|
|
25
|
+
message
|
|
15
26
|
end
|
|
16
27
|
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
# Set mode where messages are automatically outputted
|
|
29
|
+
# @param [Boolean] is_on Whether to set the auto output mode to ON
|
|
30
|
+
# @return [Boolean]
|
|
31
|
+
def set_auto_output(is_on)
|
|
32
|
+
@state.auto_output = is_on
|
|
20
33
|
end
|
|
21
34
|
|
|
22
35
|
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module MicroMIDI
|
|
2
|
+
|
|
3
|
+
def self.new(*args, &block)
|
|
4
|
+
message(*args, &block)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def self.message(*args, &block)
|
|
8
|
+
inputs = get_inputs(args)
|
|
9
|
+
outputs = get_outputs(args)
|
|
10
|
+
MicroMIDI::Context.new(inputs, outputs, &block)
|
|
11
|
+
end
|
|
12
|
+
class << self
|
|
13
|
+
alias_method :using, :message
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module IO
|
|
17
|
+
|
|
18
|
+
def self.new(*args, &block)
|
|
19
|
+
MicroMIDI.message(*args, &block)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def self.get_inputs(args)
|
|
27
|
+
args.find_all { |device| device.respond_to?(:type) && device.type == :input && device.respond_to?(:gets) }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.get_outputs(args)
|
|
31
|
+
args.find_all { |device| device.respond_to?(:puts) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
data/lib/micromidi/state.rb
CHANGED
|
@@ -2,7 +2,7 @@ module MicroMIDI
|
|
|
2
2
|
|
|
3
3
|
class State
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
DEFAULT = {
|
|
6
6
|
:channel => 0,
|
|
7
7
|
:octave => 2,
|
|
8
8
|
:velocity => 100
|
|
@@ -24,7 +24,14 @@ module MicroMIDI
|
|
|
24
24
|
:start_time,
|
|
25
25
|
:thru_listeners
|
|
26
26
|
|
|
27
|
-
def initialize(
|
|
27
|
+
def initialize(inputs, outputs, options = {})
|
|
28
|
+
@inputs = inputs
|
|
29
|
+
@outputs = outputs
|
|
30
|
+
|
|
31
|
+
@channel = options[:channel] || DEFAULT[:channel]
|
|
32
|
+
@velocity = options[:velocity] || DEFAULT[:velocity]
|
|
33
|
+
@octave = options[:octave] || DEFAULT[:octave]
|
|
34
|
+
|
|
28
35
|
@auto_output = true
|
|
29
36
|
@last_command = nil
|
|
30
37
|
@last_note = nil
|
|
@@ -33,19 +40,21 @@ module MicroMIDI
|
|
|
33
40
|
@output_cache = []
|
|
34
41
|
@start_time = Time.now.to_f
|
|
35
42
|
@super_sticky = false
|
|
36
|
-
|
|
37
|
-
@channel = options[:channel] || Default[:channel]
|
|
38
|
-
@velocity = options[:velocity] || Default[:velocity]
|
|
39
|
-
@octave = options[:octave] || Default[:octave]
|
|
40
|
-
|
|
41
|
-
@inputs = ins
|
|
42
|
-
@outputs = outs
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
def record(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
def record(method, args, block, output)
|
|
46
|
+
timestamp = now
|
|
47
|
+
message = {
|
|
48
|
+
:message => output,
|
|
49
|
+
:timestamp => timestamp
|
|
50
|
+
}
|
|
51
|
+
@output_cache << message
|
|
52
|
+
@last_command = {
|
|
53
|
+
:method => method,
|
|
54
|
+
:args => args,
|
|
55
|
+
:block => block,
|
|
56
|
+
:timestamp => timestamp
|
|
57
|
+
}
|
|
49
58
|
end
|
|
50
59
|
|
|
51
60
|
def toggle_super_sticky
|
|
@@ -56,12 +65,14 @@ module MicroMIDI
|
|
|
56
65
|
@auto_output = !@auto_output
|
|
57
66
|
end
|
|
58
67
|
|
|
59
|
-
def message_properties(
|
|
68
|
+
def message_properties(options, *properties)
|
|
60
69
|
output = {}
|
|
61
|
-
|
|
62
|
-
output[
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
properties.each do |property|
|
|
71
|
+
output[property] = options[property]
|
|
72
|
+
if !output[property].nil? && (send(property).nil? || @super_sticky)
|
|
73
|
+
send("#{property.to_s}=", output[property])
|
|
74
|
+
end
|
|
75
|
+
output[property] ||= send(property.to_s)
|
|
65
76
|
end
|
|
66
77
|
output
|
|
67
78
|
end
|
|
@@ -69,7 +80,7 @@ module MicroMIDI
|
|
|
69
80
|
private
|
|
70
81
|
|
|
71
82
|
def now
|
|
72
|
-
(
|
|
83
|
+
(Time.now.to_f - @start_time) * 1000
|
|
73
84
|
end
|
|
74
85
|
|
|
75
86
|
end
|
data/lib/midi.rb
CHANGED
data/test/input_test.rb
CHANGED
data/test/sysex_test.rb
CHANGED
|
@@ -2,56 +2,54 @@ require "helper"
|
|
|
2
2
|
|
|
3
3
|
class SysexTest < Test::Unit::TestCase
|
|
4
4
|
|
|
5
|
-
include MicroMIDI
|
|
6
5
|
include MIDIMessage
|
|
7
|
-
include TestHelper
|
|
8
6
|
|
|
9
7
|
def test_command
|
|
10
8
|
m = MicroMIDI.message
|
|
11
9
|
m.sysex_node 0x41, :model_id => 0x42, :device_id => 0x10
|
|
12
|
-
|
|
13
|
-
assert_equal(SystemExclusive::Command,
|
|
14
|
-
assert_equal([0x40, 0x7F, 0x00],
|
|
15
|
-
assert_equal(0,
|
|
16
|
-
assert_equal([0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x7F, 0x00, 0x00, 0x41, 0xF7],
|
|
10
|
+
message = m.sysex_command [0x40, 0x7F, 0x00], 0x00
|
|
11
|
+
assert_equal(SystemExclusive::Command, message.class)
|
|
12
|
+
assert_equal([0x40, 0x7F, 0x00], message.address)
|
|
13
|
+
assert_equal(0, message.data)
|
|
14
|
+
assert_equal([0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x7F, 0x00, 0x00, 0x41, 0xF7], message.to_bytes)
|
|
17
15
|
end
|
|
18
16
|
|
|
19
17
|
def test_request
|
|
20
18
|
m = MicroMIDI.message
|
|
21
19
|
m.sysex_node 0x41, :model_id => 0x42, :device_id => 0x10
|
|
22
|
-
|
|
23
|
-
assert_equal(SystemExclusive::Request,
|
|
24
|
-
assert_equal([0x40, 0x7F, 0x00],
|
|
25
|
-
assert_equal([0x0, 0x0, 0x02],
|
|
26
|
-
assert_equal([0xF0, 0x41, 0x10, 0x42, 0x11, 0x40, 0x7F, 0x00, 0x0, 0x0, 0x02, 0x3F, 0xF7],
|
|
20
|
+
message = m.sysex_request [0x40, 0x7F, 0x00], 0x02
|
|
21
|
+
assert_equal(SystemExclusive::Request, message.class)
|
|
22
|
+
assert_equal([0x40, 0x7F, 0x00], message.address)
|
|
23
|
+
assert_equal([0x0, 0x0, 0x02], message.size)
|
|
24
|
+
assert_equal([0xF0, 0x41, 0x10, 0x42, 0x11, 0x40, 0x7F, 0x00, 0x0, 0x0, 0x02, 0x3F, 0xF7], message.to_bytes)
|
|
27
25
|
end
|
|
28
26
|
|
|
29
27
|
def test_message
|
|
30
28
|
m = MicroMIDI.message
|
|
31
|
-
|
|
32
|
-
assert_equal(SystemExclusive::Message,
|
|
33
|
-
assert_equal([0x42, 0x11, 0x40, 0x7F, 0x00, 0x0, 0x0, 0x02],
|
|
34
|
-
assert_equal([0xF0, 0x42, 0x11, 0x40, 0x7F, 0x00, 0x0, 0x0, 0x02, 0xF7],
|
|
29
|
+
message = m.sysex_message [0x42, 0x11, 0x40, 0x7F, 0x00, 0x0, 0x0, 0x02]
|
|
30
|
+
assert_equal(SystemExclusive::Message, message.class)
|
|
31
|
+
assert_equal([0x42, 0x11, 0x40, 0x7F, 0x00, 0x0, 0x0, 0x02], message.data)
|
|
32
|
+
assert_equal([0xF0, 0x42, 0x11, 0x40, 0x7F, 0x00, 0x0, 0x0, 0x02, 0xF7], message.to_bytes)
|
|
35
33
|
end
|
|
36
34
|
|
|
37
35
|
def test_message_with_node
|
|
38
36
|
m = MicroMIDI.message
|
|
39
37
|
m.sysex_node 0x41, :model_id => 0x42, :device_id => 0x10
|
|
40
|
-
|
|
41
|
-
assert_equal(SystemExclusive::Message,
|
|
42
|
-
assert_equal([0x42, 0x11, 0x40, 0x7F, 0x00, 0x0, 0x0, 0x02],
|
|
43
|
-
assert_equal([0xF0, 0x41, 0x10, 0x42, 0x42, 0x11, 0x40, 0x7F, 0x00, 0x0, 0x0, 0x02, 0xF7],
|
|
38
|
+
message = m.sysex_message [0x42, 0x11, 0x40, 0x7F, 0x00, 0x0, 0x0, 0x02]
|
|
39
|
+
assert_equal(SystemExclusive::Message, message.class)
|
|
40
|
+
assert_equal([0x42, 0x11, 0x40, 0x7F, 0x00, 0x0, 0x0, 0x02], message.data)
|
|
41
|
+
assert_equal([0xF0, 0x41, 0x10, 0x42, 0x42, 0x11, 0x40, 0x7F, 0x00, 0x0, 0x0, 0x02, 0xF7], message.to_bytes)
|
|
44
42
|
end
|
|
45
43
|
|
|
46
44
|
def test_no_model_id
|
|
47
45
|
m = MicroMIDI.message
|
|
48
46
|
m.sysex_node 0x41, :device_id => 0x10
|
|
49
|
-
|
|
50
|
-
assert_equal(SystemExclusive::Command,
|
|
51
|
-
assert_equal([0x41, 0x10],
|
|
52
|
-
assert_equal([0x40, 0x7F, 0x00],
|
|
53
|
-
assert_equal(0,
|
|
54
|
-
assert_equal([0xF0, 0x41, 0x10, 0x12, 0x40, 0x7F, 0x00, 0x00, 0x41, 0xF7],
|
|
47
|
+
message = m.sysex_command [0x40, 0x7F, 0x00], 0x00
|
|
48
|
+
assert_equal(SystemExclusive::Command, message.class)
|
|
49
|
+
assert_equal([0x41, 0x10], message.node.to_a)
|
|
50
|
+
assert_equal([0x40, 0x7F, 0x00], message.address)
|
|
51
|
+
assert_equal(0, message.data)
|
|
52
|
+
assert_equal([0xF0, 0x41, 0x10, 0x12, 0x40, 0x7F, 0x00, 0x00, 0x41, 0xF7], message.to_bytes)
|
|
55
53
|
end
|
|
56
54
|
|
|
57
55
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: micromidi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ari Russo
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2014-09-
|
|
11
|
+
date: 2014-09-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: midi-eye
|
|
@@ -103,6 +103,7 @@ files:
|
|
|
103
103
|
- lib/micromidi/instructions/shorthand.rb
|
|
104
104
|
- lib/micromidi/instructions/sticky.rb
|
|
105
105
|
- lib/micromidi/instructions/sysex.rb
|
|
106
|
+
- lib/micromidi/module_methods.rb
|
|
106
107
|
- lib/micromidi/state.rb
|
|
107
108
|
- lib/midi.rb
|
|
108
109
|
- test/composite_test.rb
|