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 +4 -4
- data/lib/micromidi.rb +5 -5
- data/lib/micromidi/context.rb +38 -19
- data/lib/micromidi/device.rb +37 -0
- data/lib/micromidi/instructions/composite.rb +28 -17
- data/lib/micromidi/instructions/input.rb +81 -38
- data/lib/micromidi/instructions/message.rb +65 -18
- data/lib/micromidi/instructions/output.rb +5 -4
- data/lib/micromidi/instructions/process.rb +53 -9
- data/lib/micromidi/instructions/shorthand.rb +16 -15
- data/lib/micromidi/instructions/sticky.rb +53 -24
- data/lib/micromidi/instructions/sysex.rb +39 -18
- data/lib/micromidi/module_methods.rb +17 -19
- data/lib/micromidi/state.rb +84 -41
- data/lib/midi.rb +6 -5
- data/test/composite_test.rb +23 -28
- data/test/context_test.rb +13 -18
- data/test/effect_test.rb +9 -13
- data/test/helper.rb +16 -9
- data/test/input_test.rb +4 -5
- data/test/message_test.rb +30 -35
- data/test/output_test.rb +3 -8
- data/test/state_test.rb +7 -12
- data/test/sticky_test.rb +37 -42
- data/test/sysex_test.rb +14 -17
- metadata +103 -16
@@ -1,39 +1,60 @@
|
|
1
1
|
module MicroMIDI
|
2
2
|
|
3
3
|
module Instructions
|
4
|
-
|
4
|
+
|
5
5
|
class SysEx
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
# @param [State] state
|
9
8
|
def initialize(state)
|
10
9
|
@state = state
|
11
10
|
end
|
12
|
-
|
13
|
-
#
|
11
|
+
|
12
|
+
# Create a sysex command message
|
13
|
+
# @param [Fixnum] address
|
14
|
+
# @param [Array<Fixnum>] data
|
15
|
+
# @param [Hash] options
|
16
|
+
# @option options [MIDIMessage::SystemExclusive::Node] :node (also :sysex_node)
|
17
|
+
# @return [MIDIMessage::SystemExclusive::Command]
|
14
18
|
def sysex_command(address, data, options = {})
|
15
|
-
|
16
|
-
|
17
|
-
SystemExclusive::Command.new(address, data, :node => props[:sysex_node])
|
19
|
+
properties = sysex_properties(options)
|
20
|
+
MIDIMessage::SystemExclusive::Command.new(address, data, :node => properties[:sysex_node])
|
18
21
|
end
|
19
22
|
alias_method :command, :sysex_command
|
20
|
-
|
21
|
-
#
|
23
|
+
|
24
|
+
# Create a sysex request message
|
25
|
+
# @param [Fixnum] address
|
26
|
+
# @param [Fixnum] size
|
27
|
+
# @param [Hash] options
|
28
|
+
# @option options [MIDIMessage::SystemExclusive::Node] :node (also :sysex_node)
|
29
|
+
# @return [MIDIMessage::SystemExclusive::Request]
|
22
30
|
def sysex_request(address, size, options = {})
|
23
|
-
|
24
|
-
|
25
|
-
SystemExclusive::Request.new(address, size, :node => props[:sysex_node])
|
31
|
+
properties = sysex_properties(options)
|
32
|
+
MIDIMessage::SystemExclusive::Request.new(address, size, :node => properties[:sysex_node])
|
26
33
|
end
|
27
34
|
alias_method :request, :sysex_request
|
28
|
-
|
29
|
-
#
|
35
|
+
|
36
|
+
# Create a generic sysex message
|
37
|
+
# @param [Array<Fixnum>] data
|
38
|
+
# @param [Hash] options
|
39
|
+
# @option options [MIDIMessage::SystemExclusive::Node] :node (also :sysex_node)
|
40
|
+
# @return [MIDIMessage::SystemExclusive::Message]
|
30
41
|
def sysex_message(data, options = {})
|
31
|
-
|
32
|
-
|
33
|
-
SystemExclusive::Message.new(data, :node => props[:sysex_node])
|
42
|
+
properties = sysex_properties(options)
|
43
|
+
MIDIMessage::SystemExclusive::Message.new(data, :node => properties[:sysex_node])
|
34
44
|
end
|
35
45
|
alias_method :sysex, :sysex_message
|
36
46
|
|
47
|
+
private
|
48
|
+
|
49
|
+
# Get the message properties given the options hash
|
50
|
+
# @param [Hash] options
|
51
|
+
# @return [Hash]
|
52
|
+
def sysex_properties(options)
|
53
|
+
sysex_options = options.dup
|
54
|
+
sysex_options[:sysex_node] ||= options.delete(:node)
|
55
|
+
@state.message_properties(sysex_options, :sysex_node)
|
56
|
+
end
|
57
|
+
|
37
58
|
end
|
38
59
|
|
39
60
|
end
|
@@ -1,34 +1,32 @@
|
|
1
1
|
module MicroMIDI
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
extend self
|
4
|
+
|
5
|
+
# Shortcut to create a new context
|
6
|
+
# @param [*Object] args
|
7
|
+
# @param [Proc] block
|
8
|
+
# @return [Context]
|
9
|
+
def new(*args, &block)
|
10
|
+
inputs = Device.get_inputs(args)
|
11
|
+
outputs = Device.get_outputs(args)
|
12
|
+
Context.new(inputs, outputs, &block)
|
5
13
|
end
|
6
14
|
|
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
15
|
class << self
|
13
|
-
alias_method :
|
16
|
+
alias_method :message, :new
|
17
|
+
alias_method :using, :new
|
14
18
|
end
|
15
19
|
|
16
20
|
module IO
|
17
21
|
|
22
|
+
# Shortcut to create a new context
|
23
|
+
# @param [*Object] args
|
24
|
+
# @param [Proc] block
|
25
|
+
# @return [Context]
|
18
26
|
def self.new(*args, &block)
|
19
|
-
MicroMIDI.
|
27
|
+
MicroMIDI.new(*args, &block)
|
20
28
|
end
|
21
29
|
|
22
30
|
end
|
23
31
|
|
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
32
|
end
|
data/lib/micromidi/state.rb
CHANGED
@@ -1,86 +1,129 @@
|
|
1
1
|
module MicroMIDI
|
2
2
|
|
3
|
+
# The DSL state
|
3
4
|
class State
|
4
|
-
|
5
|
+
|
5
6
|
DEFAULT = {
|
6
7
|
:channel => 0,
|
7
8
|
:octave => 2,
|
8
9
|
:velocity => 100
|
9
10
|
}
|
10
|
-
|
11
|
+
|
11
12
|
attr_accessor :auto_output,
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
:channel,
|
14
|
+
:last_note,
|
15
|
+
:octave,
|
16
|
+
:sysex_node,
|
17
|
+
:super_sticky,
|
18
|
+
:velocity
|
19
|
+
|
19
20
|
attr_reader :inputs,
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
:last_command,
|
22
|
+
:listeners,
|
23
|
+
:outputs,
|
24
|
+
:output_cache,
|
25
|
+
:start_time,
|
26
|
+
:thru_listeners
|
27
|
+
|
28
|
+
# @param [Array<UniMIDI::Input>, UniMIDI::Input] inputs
|
29
|
+
# @param [Array<UniMIDI::Output, IO>, IO, UniMIDI::Output] outputs
|
30
|
+
# @param [Hash] options
|
31
|
+
# @option options [Fixnum] :channel
|
32
|
+
# @option options [Fixnum] :octave
|
33
|
+
# @option options [Fixnum] :velocity
|
27
34
|
def initialize(inputs, outputs, options = {})
|
28
|
-
@inputs = inputs
|
29
|
-
@outputs = outputs
|
35
|
+
@inputs = [inputs].flatten
|
36
|
+
@outputs = [outputs].flatten
|
30
37
|
|
31
38
|
@channel = options[:channel] || DEFAULT[:channel]
|
32
39
|
@velocity = options[:velocity] || DEFAULT[:velocity]
|
33
|
-
@octave = options[:octave] || DEFAULT[:octave]
|
40
|
+
@octave = options[:octave] || DEFAULT[:octave]
|
34
41
|
|
35
42
|
@auto_output = true
|
36
43
|
@last_command = nil
|
37
|
-
@last_note = nil
|
44
|
+
@last_note = nil
|
38
45
|
@listeners = []
|
39
46
|
@thru_listeners = []
|
40
47
|
@output_cache = []
|
41
48
|
@start_time = Time.now.to_f
|
42
49
|
@super_sticky = false
|
43
50
|
end
|
44
|
-
|
45
|
-
|
51
|
+
|
52
|
+
# Record that a command was used
|
53
|
+
# @param [Symbol, String] method
|
54
|
+
# @param [Array<Object>] args
|
55
|
+
# @param [Proc] block
|
56
|
+
# @param [Object] result
|
57
|
+
def record(method, args, block, result)
|
46
58
|
timestamp = now
|
47
|
-
message = {
|
48
|
-
:message =>
|
49
|
-
:timestamp => timestamp
|
59
|
+
message = {
|
60
|
+
:message => result,
|
61
|
+
:timestamp => timestamp
|
50
62
|
}
|
51
63
|
@output_cache << message
|
52
|
-
@last_command = {
|
53
|
-
:method => method,
|
54
|
-
:args => args,
|
55
|
-
:block => block,
|
56
|
-
:timestamp => timestamp
|
64
|
+
@last_command = {
|
65
|
+
:method => method,
|
66
|
+
:args => args,
|
67
|
+
:block => block,
|
68
|
+
:timestamp => timestamp
|
57
69
|
}
|
58
70
|
end
|
59
|
-
|
71
|
+
|
72
|
+
#
|
73
|
+
# Toggles super_sticky mode, a mode where any explicit values used to create MIDI messages
|
74
|
+
# automatically become sticky. Normally the explicit value would only be used for
|
75
|
+
# the current message.
|
76
|
+
#
|
77
|
+
# For example, while in super sticky mode
|
78
|
+
#
|
79
|
+
# ```ruby
|
80
|
+
# note "C4", :channel => 5
|
81
|
+
# note "C3"
|
82
|
+
# ```
|
83
|
+
#
|
84
|
+
# will have the same results as
|
85
|
+
#
|
86
|
+
# ```ruby
|
87
|
+
# channel 5
|
88
|
+
# note "C4"
|
89
|
+
# note "C3"
|
90
|
+
# ```
|
91
|
+
#
|
92
|
+
# @return [Boolean]
|
60
93
|
def toggle_super_sticky
|
61
94
|
@super_sticky = !@super_sticky
|
62
95
|
end
|
63
|
-
|
96
|
+
|
97
|
+
# Toggles auto-output mode. In auto-output mode, any messages that are instantiated are sent to
|
98
|
+
# any available MIDI outputs.
|
99
|
+
# @return [Boolean]
|
64
100
|
def toggle_auto_output
|
65
101
|
@auto_output = !@auto_output
|
66
102
|
end
|
67
103
|
|
104
|
+
# Return message properties with regard to the current state
|
105
|
+
# @param [Hash] options
|
106
|
+
# @param [*Symbol] properties
|
107
|
+
# @return [Hash]
|
68
108
|
def message_properties(options, *properties)
|
69
|
-
|
109
|
+
result = {}
|
70
110
|
properties.each do |property|
|
71
|
-
|
72
|
-
if !
|
73
|
-
send("#{property.to_s}=",
|
111
|
+
result[property] = options[property]
|
112
|
+
if !result[property].nil? && (send(property).nil? || @super_sticky)
|
113
|
+
send("#{property.to_s}=", result[property])
|
74
114
|
end
|
75
|
-
|
115
|
+
result[property] ||= send(property.to_s)
|
76
116
|
end
|
77
|
-
|
117
|
+
result
|
78
118
|
end
|
79
|
-
|
119
|
+
|
80
120
|
private
|
81
|
-
|
121
|
+
|
122
|
+
# A timestamp
|
123
|
+
# @return [Float]
|
82
124
|
def now
|
83
|
-
|
125
|
+
time = Time.now.to_f - @start_time
|
126
|
+
time * 1000
|
84
127
|
end
|
85
128
|
|
86
129
|
end
|
data/lib/midi.rb
CHANGED
@@ -2,14 +2,15 @@
|
|
2
2
|
# MicroMIDI
|
3
3
|
# A Ruby DSL for MIDI
|
4
4
|
#
|
5
|
-
# (c)2011-2014 Ari Russo
|
6
|
-
#
|
5
|
+
# (c)2011-2014 Ari Russo
|
6
|
+
# Apache 2.0 License
|
7
7
|
#
|
8
8
|
|
9
|
-
# The purpose of this file is
|
9
|
+
# The purpose of this file is to allow:
|
10
10
|
#
|
11
|
-
# <em>require "micromidi"</em>
|
12
|
-
# and
|
13
11
|
# <em>require "midi"</em>
|
12
|
+
# in addition to
|
13
|
+
# <em>require "micromidi"</em>
|
14
|
+
#
|
14
15
|
|
15
16
|
require "micromidi"
|
data/test/composite_test.rb
CHANGED
@@ -1,78 +1,73 @@
|
|
1
1
|
require "helper"
|
2
2
|
|
3
|
-
class CompositeTest < Test
|
4
|
-
|
5
|
-
include MicroMIDI
|
6
|
-
include MIDIMessage
|
7
|
-
include TestHelper
|
3
|
+
class CompositeTest < Minitest::Test
|
8
4
|
|
9
5
|
def test_play
|
10
6
|
m = MicroMIDI.message
|
11
7
|
start = Time.now
|
12
8
|
msg = m.play "C0", 0.5
|
13
|
-
|
9
|
+
|
14
10
|
finish = Time.now
|
15
11
|
dif = finish - start
|
16
12
|
assert_equal(true, dif >= 0.5)
|
17
|
-
|
18
|
-
assert_equal(NoteOn, msg.class)
|
13
|
+
|
14
|
+
assert_equal(MIDIMessage::NoteOn, msg.class)
|
19
15
|
assert_equal(12, msg.note)
|
20
16
|
assert_equal(0, msg.channel)
|
21
17
|
assert_equal(2, m.state.output_cache.size)
|
22
|
-
|
18
|
+
|
23
19
|
off_msg = m.state.output_cache.last[:message]
|
24
|
-
assert_equal(NoteOff, off_msg.class)
|
20
|
+
assert_equal(MIDIMessage::NoteOff, off_msg.class)
|
25
21
|
assert_equal(12, off_msg.note)
|
26
22
|
assert_equal(0, off_msg.channel)
|
27
23
|
end
|
28
|
-
|
24
|
+
|
29
25
|
def test_play_chord
|
30
26
|
m = MicroMIDI.message
|
31
27
|
start = Time.now
|
32
28
|
msgs = m.play "C0", "E1", "G2", 0.5
|
33
|
-
|
29
|
+
|
34
30
|
finish = Time.now
|
35
31
|
dif = finish - start
|
36
32
|
assert_equal(true, dif >= 0.5)
|
37
|
-
|
33
|
+
|
38
34
|
msg = msgs.first
|
39
|
-
|
40
|
-
assert_equal(NoteOn, msg.class)
|
35
|
+
|
36
|
+
assert_equal(MIDIMessage::NoteOn, msg.class)
|
41
37
|
assert_equal(12, msg.note)
|
42
38
|
assert_equal(0, msg.channel)
|
43
39
|
assert_equal(6, m.state.output_cache.size)
|
44
|
-
|
40
|
+
|
45
41
|
off_msg = m.state.output_cache.last[:message]
|
46
|
-
assert_equal(NoteOff, off_msg.class)
|
42
|
+
assert_equal(MIDIMessage::NoteOff, off_msg.class)
|
47
43
|
assert_equal(43, off_msg.note)
|
48
44
|
assert_equal(0, off_msg.channel)
|
49
45
|
end
|
50
|
-
|
46
|
+
|
51
47
|
def test_play_chord_array
|
52
48
|
m = MicroMIDI.message
|
53
49
|
start = Time.now
|
54
50
|
msgs = m.play ["C0", "E1", "G2"], 0.5
|
55
|
-
|
51
|
+
|
56
52
|
finish = Time.now
|
57
53
|
dif = finish - start
|
58
54
|
assert_equal(true, dif >= 0.5)
|
59
|
-
|
55
|
+
|
60
56
|
msg = msgs.first
|
61
|
-
|
62
|
-
assert_equal(NoteOn, msg.class)
|
57
|
+
|
58
|
+
assert_equal(MIDIMessage::NoteOn, msg.class)
|
63
59
|
assert_equal(12, msg.note)
|
64
60
|
assert_equal(0, msg.channel)
|
65
61
|
assert_equal(6, m.state.output_cache.size)
|
66
|
-
|
62
|
+
|
67
63
|
off_msg = m.state.output_cache.last[:message]
|
68
|
-
assert_equal(NoteOff, off_msg.class)
|
64
|
+
assert_equal(MIDIMessage::NoteOff, off_msg.class)
|
69
65
|
assert_equal(43, off_msg.note)
|
70
66
|
assert_equal(0, off_msg.channel)
|
71
67
|
end
|
72
|
-
|
68
|
+
|
73
69
|
def test_all_off
|
74
|
-
|
70
|
+
|
75
71
|
end
|
76
|
-
|
77
|
-
end
|
78
72
|
|
73
|
+
end
|
data/test/context_test.rb
CHANGED
@@ -1,52 +1,47 @@
|
|
1
1
|
require "helper"
|
2
2
|
|
3
|
-
class ContextTest < Test
|
3
|
+
class ContextTest < Minitest::Test
|
4
4
|
|
5
|
-
include MicroMIDI
|
6
|
-
include MIDIMessage
|
7
|
-
include TestHelper
|
8
|
-
|
9
5
|
def test_new_with_block
|
10
6
|
msg = nil
|
11
7
|
MIDI::IO.new do
|
12
8
|
msg = note "C0"
|
13
9
|
end
|
14
|
-
assert_equal(NoteOn, msg.class)
|
10
|
+
assert_equal(MIDIMessage::NoteOn, msg.class)
|
15
11
|
assert_equal(12, msg.note)
|
16
|
-
assert_equal(0, msg.channel)
|
12
|
+
assert_equal(0, msg.channel)
|
17
13
|
end
|
18
|
-
|
14
|
+
|
19
15
|
def test_new_with_no_block
|
20
16
|
m = MIDI::IO.new
|
21
17
|
msg = m.note "C0"
|
22
|
-
assert_equal(NoteOn, msg.class)
|
18
|
+
assert_equal(MIDIMessage::NoteOn, msg.class)
|
23
19
|
assert_equal(12, msg.note)
|
24
20
|
assert_equal(0, msg.channel)
|
25
21
|
end
|
26
|
-
|
22
|
+
|
27
23
|
def test_edit
|
28
24
|
msg = nil
|
29
|
-
m = MIDI::IO.new
|
25
|
+
m = MIDI::IO.new
|
30
26
|
m.edit do
|
31
27
|
msg = m.note "C0"
|
32
28
|
end
|
33
|
-
assert_equal(NoteOn, msg.class)
|
29
|
+
assert_equal(MIDIMessage::NoteOn, msg.class)
|
34
30
|
assert_equal(12, msg.note)
|
35
|
-
assert_equal(0, msg.channel)
|
31
|
+
assert_equal(0, msg.channel)
|
36
32
|
end
|
37
33
|
|
38
34
|
def test_repeat
|
39
35
|
m = MicroMIDI.message
|
40
36
|
msg = m.note "C0"
|
41
|
-
assert_equal(NoteOn, msg.class)
|
37
|
+
assert_equal(MIDIMessage::NoteOn, msg.class)
|
42
38
|
assert_equal(12, msg.note)
|
43
39
|
assert_equal(0, msg.channel)
|
44
|
-
|
40
|
+
|
45
41
|
r_msg = m.repeat
|
46
|
-
assert_equal(NoteOn, r_msg.class)
|
42
|
+
assert_equal(MIDIMessage::NoteOn, r_msg.class)
|
47
43
|
assert_equal(12, r_msg.note)
|
48
44
|
assert_equal(0, r_msg.channel)
|
49
45
|
end
|
50
|
-
|
51
|
-
end
|
52
46
|
|
47
|
+
end
|