midi 0.0.7
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.
- data/LICENSE +13 -0
- data/README.rdoc +103 -0
- data/TODO +8 -0
- data/lib/micromidi.rb +67 -0
- data/lib/micromidi/context.rb +57 -0
- data/lib/micromidi/instructions/composite.rb +35 -0
- data/lib/micromidi/instructions/input.rb +114 -0
- data/lib/micromidi/instructions/message.rb +100 -0
- data/lib/micromidi/instructions/output.rb +28 -0
- data/lib/micromidi/instructions/process.rb +57 -0
- data/lib/micromidi/instructions/shorthand.rb +79 -0
- data/lib/micromidi/instructions/sticky.rb +59 -0
- data/lib/micromidi/instructions/sysex.rb +43 -0
- data/lib/micromidi/state.rb +79 -0
- data/lib/midi.rb +15 -0
- data/test/helper.rb +32 -0
- data/test/test_composite.rb +36 -0
- data/test/test_context.rb +33 -0
- data/test/test_effect.rb +135 -0
- data/test/test_input.rb +23 -0
- data/test/test_message.rb +123 -0
- data/test/test_output.rb +21 -0
- data/test/test_state.rb +32 -0
- data/test/test_sticky.rb +134 -0
- data/test/test_sysex.rb +60 -0
- metadata +103 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
module MicroMIDI
|
4
|
+
|
5
|
+
module Instructions
|
6
|
+
|
7
|
+
class Output
|
8
|
+
|
9
|
+
def initialize(state)
|
10
|
+
@state = state
|
11
|
+
end
|
12
|
+
|
13
|
+
def output(msg)
|
14
|
+
auto_output(msg) if msg === false || msg === true
|
15
|
+
@state.outputs.each { |o| o.puts(msg) } unless msg.nil?
|
16
|
+
msg
|
17
|
+
end
|
18
|
+
|
19
|
+
# toggle mode where messages are automatically outputted
|
20
|
+
def auto_output(mode = nil)
|
21
|
+
mode.nil? ? @state.toggle_auto_output : @state.auto_output = mode
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
module MicroMIDI
|
4
|
+
|
5
|
+
module Instructions
|
6
|
+
|
7
|
+
class Process
|
8
|
+
|
9
|
+
include MIDIMessage
|
10
|
+
include MIDIMessage::Process
|
11
|
+
|
12
|
+
def initialize(state)
|
13
|
+
@state = state
|
14
|
+
end
|
15
|
+
|
16
|
+
def transpose(message, property, factor, options = {})
|
17
|
+
Transpose.process(message, property, factor, options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def limit(message, property, range, options = {})
|
21
|
+
Limit.process(message, property, range, options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def filter(message, property, bandwidth, options = {})
|
25
|
+
Filter.process(message, property, bandwidth, options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def high_pass_filter(message, property, min, options = {})
|
29
|
+
HighPassFilter.process(message, property, min, options)
|
30
|
+
end
|
31
|
+
alias_method :only_above, :high_pass_filter
|
32
|
+
alias_method :except_below, :high_pass_filter
|
33
|
+
|
34
|
+
def low_pass_filter(message, property, max, options = {})
|
35
|
+
LowPassFilter.process(message, property, max, options)
|
36
|
+
end
|
37
|
+
alias_method :only_below, :low_pass_filter
|
38
|
+
alias_method :except_above, :low_pass_filter
|
39
|
+
|
40
|
+
def band_pass_filter(message, property, bandwidth, options = {})
|
41
|
+
BandPassFilter.process(message, property, bandwidth, options)
|
42
|
+
end
|
43
|
+
alias_method :only_in, :band_pass_filter
|
44
|
+
alias_method :only, :band_pass_filter
|
45
|
+
|
46
|
+
def notch_filter(message, property, bandwidth, options = {})
|
47
|
+
NotchFilter.process(message, property, bandwidth, options)
|
48
|
+
end
|
49
|
+
alias_method :band_reject_filter, :notch_filter
|
50
|
+
alias_method :except_in, :notch_filter
|
51
|
+
alias_method :except, :notch_filter
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
|
4
|
+
module MicroMIDI
|
5
|
+
|
6
|
+
alias l loop
|
7
|
+
|
8
|
+
class << self
|
9
|
+
alias_method :m, :message
|
10
|
+
end
|
11
|
+
|
12
|
+
class Context
|
13
|
+
alias_method :r, :repeat
|
14
|
+
end
|
15
|
+
|
16
|
+
module Instructions
|
17
|
+
|
18
|
+
module Composite
|
19
|
+
alias_method :p, :play
|
20
|
+
alias_method :q!, :all_off
|
21
|
+
alias_method :x, :all_off
|
22
|
+
end
|
23
|
+
|
24
|
+
class Input
|
25
|
+
alias_method :j, :join
|
26
|
+
alias_method :rc, :receive
|
27
|
+
alias_method :rcu, :receive_unless
|
28
|
+
alias_method :t, :thru
|
29
|
+
alias_method :te, :thru_except
|
30
|
+
alias_method :tu, :thru_unless
|
31
|
+
alias_method :w, :wait_for_input
|
32
|
+
end
|
33
|
+
|
34
|
+
class Message
|
35
|
+
alias_method :c, :control_change
|
36
|
+
alias_method :cc, :control_change
|
37
|
+
alias_method :n, :note
|
38
|
+
alias_method :no, :note_off
|
39
|
+
alias_method :o, :off
|
40
|
+
alias_method :pc, :program_change
|
41
|
+
end
|
42
|
+
|
43
|
+
class Output
|
44
|
+
alias_method :out, :output
|
45
|
+
end
|
46
|
+
|
47
|
+
class Process
|
48
|
+
alias_method :bp, :band_pass_filter
|
49
|
+
alias_method :bpf, :band_pass_filter
|
50
|
+
alias_method :br, :notch_filter
|
51
|
+
alias_method :f, :filter
|
52
|
+
alias_method :hp, :high_pass_filter
|
53
|
+
alias_method :hpf, :high_pass_filter
|
54
|
+
alias_method :l, :limit
|
55
|
+
alias_method :lp, :low_pass_filter
|
56
|
+
alias_method :lpf, :low_pass_filter
|
57
|
+
alias_method :mbf, :filter
|
58
|
+
alias_method :nf, :notch_filter
|
59
|
+
alias_method :tp, :transpose
|
60
|
+
end
|
61
|
+
|
62
|
+
class Sticky
|
63
|
+
alias_method :ch, :channel
|
64
|
+
alias_method :ss, :super_sticky
|
65
|
+
alias_method :v, :velocity
|
66
|
+
end
|
67
|
+
|
68
|
+
class SysEx
|
69
|
+
alias_method :sc, :sysex_command
|
70
|
+
alias_method :sr, :sysex_request
|
71
|
+
alias_method :sx, :sysex_message
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def M(*a, &block)
|
78
|
+
MIDI.message(*a, &block)
|
79
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
module MicroMIDI
|
4
|
+
|
5
|
+
module Instructions
|
6
|
+
|
7
|
+
class Sticky
|
8
|
+
|
9
|
+
def initialize(state)
|
10
|
+
@state = state
|
11
|
+
end
|
12
|
+
|
13
|
+
# sets the sticky channel for the current block
|
14
|
+
def channel(val = nil)
|
15
|
+
val.nil? ? @state.channel : @state.channel = val
|
16
|
+
end
|
17
|
+
|
18
|
+
# sets the octave for the current block
|
19
|
+
def octave(val = nil)
|
20
|
+
val.nil? ? @state.octave : @state.octave = val
|
21
|
+
end
|
22
|
+
|
23
|
+
# sets the sysex node for the current block
|
24
|
+
def sysex_node(*args)
|
25
|
+
options = args.last.kind_of?(Hash) ? args.last : {}
|
26
|
+
args.empty? ? @state.sysex_node : @state.sysex_node = MIDIMessage::SystemExclusive::Node.new(args.first, options)
|
27
|
+
end
|
28
|
+
alias_method :node, :sysex_node
|
29
|
+
|
30
|
+
# sets the sticky velocity for the current block
|
31
|
+
def velocity(val = nil)
|
32
|
+
val.nil? ? @state.velocity : @state.velocity = val
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# toggles super_sticky mode, a mode where any explicit values used to create MIDI messages
|
37
|
+
# automatically become sticky -- whereas normally the explicit value would only be used for
|
38
|
+
# the current message.
|
39
|
+
#
|
40
|
+
# e.g.
|
41
|
+
#
|
42
|
+
# note "C4", :channel => 5
|
43
|
+
#
|
44
|
+
# will have the exact same effect as
|
45
|
+
#
|
46
|
+
# channel 5
|
47
|
+
# note "C4"
|
48
|
+
#
|
49
|
+
# while in super sticky mode
|
50
|
+
#
|
51
|
+
def super_sticky
|
52
|
+
@state.toggle_super_sticky
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
module MicroMIDI
|
4
|
+
|
5
|
+
module Instructions
|
6
|
+
|
7
|
+
class SysEx
|
8
|
+
|
9
|
+
include MIDIMessage
|
10
|
+
|
11
|
+
def initialize(state)
|
12
|
+
@state = state
|
13
|
+
end
|
14
|
+
|
15
|
+
# create a sysex command
|
16
|
+
def sysex_command(address, data, options = {})
|
17
|
+
options[:sysex_node] ||= options[:node]
|
18
|
+
props = @state.message_properties(options, :sysex_node)
|
19
|
+
SystemExclusive::Command.new(address, data, :node => props[:sysex_node])
|
20
|
+
end
|
21
|
+
alias_method :command, :sysex_command
|
22
|
+
|
23
|
+
# create a sysex request
|
24
|
+
def sysex_request(address, size, options = {})
|
25
|
+
options[:sysex_node] ||= options[:node]
|
26
|
+
props = @state.message_properties(options, :sysex_node)
|
27
|
+
SystemExclusive::Request.new(address, size, :node => props[:sysex_node])
|
28
|
+
end
|
29
|
+
alias_method :request, :sysex_request
|
30
|
+
|
31
|
+
# create an indeterminate sysex message
|
32
|
+
def sysex_message(data, options = {})
|
33
|
+
options[:sysex_node] ||= options[:node]
|
34
|
+
props = @state.message_properties(options, :sysex_node)
|
35
|
+
SystemExclusive::Message.new(data, :node => props[:sysex_node])
|
36
|
+
end
|
37
|
+
alias_method :sysex, :sysex_message
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
module MicroMIDI
|
4
|
+
|
5
|
+
class State
|
6
|
+
|
7
|
+
Default = {
|
8
|
+
:channel => 0,
|
9
|
+
:octave => 2,
|
10
|
+
:velocity => 100
|
11
|
+
}
|
12
|
+
|
13
|
+
attr_accessor :auto_output,
|
14
|
+
:channel,
|
15
|
+
:last_note,
|
16
|
+
:octave,
|
17
|
+
:sysex_node,
|
18
|
+
:super_sticky,
|
19
|
+
:velocity
|
20
|
+
|
21
|
+
attr_reader :inputs,
|
22
|
+
:last_command,
|
23
|
+
:listeners,
|
24
|
+
:outputs,
|
25
|
+
:output_cache,
|
26
|
+
:start_time,
|
27
|
+
:thru_listeners
|
28
|
+
|
29
|
+
def initialize(ins, outs, options = {})
|
30
|
+
@auto_output = true
|
31
|
+
@last_command = nil
|
32
|
+
@last_note = nil
|
33
|
+
@listeners = []
|
34
|
+
@thru_listeners = []
|
35
|
+
@output_cache = []
|
36
|
+
@start_time = Time.now.to_f
|
37
|
+
@super_sticky = false
|
38
|
+
|
39
|
+
@channel = options[:channel] || Default[:channel]
|
40
|
+
@velocity = options[:velocity] || Default[:velocity]
|
41
|
+
@octave = options[:octave] || Default[:octave]
|
42
|
+
|
43
|
+
@inputs = ins
|
44
|
+
@outputs = outs
|
45
|
+
end
|
46
|
+
|
47
|
+
def record(m, a, b, outp)
|
48
|
+
ts = now
|
49
|
+
@output_cache << { :message => outp, :timestamp => ts }
|
50
|
+
@last_command = { :method => m, :args => a, :block => b, :timestamp => ts }
|
51
|
+
end
|
52
|
+
|
53
|
+
def toggle_super_sticky
|
54
|
+
@super_sticky = !@super_sticky
|
55
|
+
end
|
56
|
+
|
57
|
+
def toggle_auto_output
|
58
|
+
@auto_output = !@auto_output
|
59
|
+
end
|
60
|
+
|
61
|
+
def message_properties(opts, *props)
|
62
|
+
output = {}
|
63
|
+
props.each do |prop|
|
64
|
+
output[prop] = opts[prop]
|
65
|
+
self.send("#{prop.to_s}=", output[prop]) if !output[prop].nil? && (self.send(prop).nil? || @super_sticky)
|
66
|
+
output[prop] ||= self.send(prop.to_s)
|
67
|
+
end
|
68
|
+
output
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def now
|
74
|
+
((Time.now.to_f - @start_time) * 1000)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
data/lib/midi.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# micromidi
|
4
|
+
# A Ruby DSL for MIDI
|
5
|
+
#
|
6
|
+
# (c)2011 Ari Russo
|
7
|
+
# licensed under the Apache 2.0 License
|
8
|
+
#
|
9
|
+
|
10
|
+
# the purpose of this file is just to allow both
|
11
|
+
# <em>require "micromidi"</em>
|
12
|
+
# and
|
13
|
+
# <em>require "midi"</em>
|
14
|
+
|
15
|
+
require 'micromidi'
|
data/test/helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
dir = File.dirname(File.expand_path(__FILE__))
|
4
|
+
$LOAD_PATH.unshift dir + '/../lib'
|
5
|
+
|
6
|
+
require 'test/unit'
|
7
|
+
require 'midi'
|
8
|
+
|
9
|
+
module TestHelper
|
10
|
+
|
11
|
+
def self.select_devices
|
12
|
+
$test_device ||= {}
|
13
|
+
{ :input => UniMIDI::Input.all, :output => UniMIDI::Output.all }.each do |type, devs|
|
14
|
+
puts ""
|
15
|
+
puts "select an #{type.to_s}..."
|
16
|
+
while $test_device[type].nil?
|
17
|
+
devs.each do |device|
|
18
|
+
puts device.pretty_name
|
19
|
+
end
|
20
|
+
selection = $stdin.gets.chomp
|
21
|
+
if selection != ""
|
22
|
+
selection = selection.to_i
|
23
|
+
$test_device[type] = devs.find { |d| d.id == selection }
|
24
|
+
puts "selected #{selection} for #{type.to_s}" unless $test_device[type]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
TestHelper.select_devices
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'helper'
|
4
|
+
|
5
|
+
class CompositeTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
include MicroMIDI
|
8
|
+
include MIDIMessage
|
9
|
+
include TestHelper
|
10
|
+
|
11
|
+
def test_play
|
12
|
+
m = MicroMIDI.message
|
13
|
+
start = Time.now
|
14
|
+
msg = m.play "C0", 0.5
|
15
|
+
|
16
|
+
finish = Time.now
|
17
|
+
dif = finish - start
|
18
|
+
assert_equal(true, dif >= 0.5)
|
19
|
+
|
20
|
+
assert_equal(NoteOn, msg.class)
|
21
|
+
assert_equal(12, msg.note)
|
22
|
+
assert_equal(0, msg.channel)
|
23
|
+
assert_equal(2, m.state.output_cache.size)
|
24
|
+
|
25
|
+
off_msg = m.state.output_cache.last[:message]
|
26
|
+
assert_equal(NoteOff, off_msg.class)
|
27
|
+
assert_equal(12, off_msg.note)
|
28
|
+
assert_equal(0, off_msg.channel)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_all_off
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'helper'
|
4
|
+
|
5
|
+
class ContextTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
include MicroMIDI
|
8
|
+
include MIDIMessage
|
9
|
+
include TestHelper
|
10
|
+
|
11
|
+
def test_no_block
|
12
|
+
m = MIDI::IO.new
|
13
|
+
msg = m.note "C0"
|
14
|
+
assert_equal(NoteOn, msg.class)
|
15
|
+
assert_equal(12, msg.note)
|
16
|
+
assert_equal(0, msg.channel)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_repeat
|
20
|
+
m = MicroMIDI.message
|
21
|
+
msg = m.note "C0"
|
22
|
+
assert_equal(NoteOn, msg.class)
|
23
|
+
assert_equal(12, msg.note)
|
24
|
+
assert_equal(0, msg.channel)
|
25
|
+
|
26
|
+
r_msg = m.repeat
|
27
|
+
assert_equal(NoteOn, r_msg.class)
|
28
|
+
assert_equal(12, r_msg.note)
|
29
|
+
assert_equal(0, r_msg.channel)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|