micromidi 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|