patch 0.4.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +13 -0
- data/README.md +176 -0
- data/bin/patchrb +40 -0
- data/lib/patch/config.rb +124 -0
- data/lib/patch/em_patch.rb +47 -0
- data/lib/patch/hub.rb +68 -0
- data/lib/patch/io/midi/action.rb +42 -0
- data/lib/patch/io/midi/input.rb +110 -0
- data/lib/patch/io/midi/message.rb +112 -0
- data/lib/patch/io/midi/output.rb +58 -0
- data/lib/patch/io/midi.rb +45 -0
- data/lib/patch/io/module.rb +35 -0
- data/lib/patch/io/osc/action.rb +43 -0
- data/lib/patch/io/osc/client.rb +60 -0
- data/lib/patch/io/osc/message.rb +109 -0
- data/lib/patch/io/osc/server.rb +159 -0
- data/lib/patch/io/osc.rb +43 -0
- data/lib/patch/io/websocket/node.rb +103 -0
- data/lib/patch/io/websocket/socket.rb +103 -0
- data/lib/patch/io/websocket.rb +27 -0
- data/lib/patch/io.rb +15 -0
- data/lib/patch/log.rb +97 -0
- data/lib/patch/message.rb +67 -0
- data/lib/patch/node/container.rb +69 -0
- data/lib/patch/node/map.rb +71 -0
- data/lib/patch/node.rb +10 -0
- data/lib/patch/patch.rb +59 -0
- data/lib/patch/report.rb +132 -0
- data/lib/patch/thread.rb +19 -0
- data/lib/patch.rb +42 -0
- data/test/config/nodes.yml +16 -0
- data/test/config/patches.yml +41 -0
- data/test/config_test.rb +216 -0
- data/test/helper.rb +20 -0
- data/test/hub_test.rb +49 -0
- data/test/io/midi/action_test.rb +82 -0
- data/test/io/midi/input_test.rb +130 -0
- data/test/io/midi/message_test.rb +54 -0
- data/test/io/midi/output_test.rb +44 -0
- data/test/io/midi_test.rb +94 -0
- data/test/io/module_test.rb +21 -0
- data/test/io/osc/action_test.rb +76 -0
- data/test/io/osc/client_test.rb +49 -0
- data/test/io/osc/message_test.rb +53 -0
- data/test/io/osc/server_test.rb +116 -0
- data/test/io/osc_test.rb +111 -0
- data/test/io/websocket/node_test.rb +96 -0
- data/test/io/websocket_test.rb +37 -0
- data/test/js/logger.js +67 -0
- data/test/js/message.js +62 -0
- data/test/js/qunit-1.18.0.js +3828 -0
- data/test/js/qunit.css +291 -0
- data/test/js/test.html +15 -0
- data/test/js/websocket.js +12 -0
- data/test/log_test.rb +96 -0
- data/test/message_test.rb +109 -0
- data/test/node/container_test.rb +104 -0
- data/test/node/map_test.rb +50 -0
- data/test/node_test.rb +14 -0
- data/test/patch_test.rb +57 -0
- data/test/report_test.rb +37 -0
- metadata +320 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
module Patch
|
2
|
+
|
3
|
+
module IO
|
4
|
+
|
5
|
+
module MIDI
|
6
|
+
|
7
|
+
# Convert between MIDI message objects and Patch::Message objects
|
8
|
+
module Message
|
9
|
+
|
10
|
+
extend self
|
11
|
+
|
12
|
+
# @param [::Patch::Patch] patch
|
13
|
+
# @param [Array<::Patch::Message>, ::Patch::Message] messages
|
14
|
+
# @return [Array<::MIDIMessage>]
|
15
|
+
def to_midi_messages(patch, patch_messages)
|
16
|
+
patch_messages = [patch_messages].flatten
|
17
|
+
midi_messages = patch_messages.map do |patch_message|
|
18
|
+
unless (action = patch.actions.at(patch_message.index)).nil?
|
19
|
+
to_midi_message(action, patch_message) unless action[:midi].nil?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
midi_messages.compact
|
23
|
+
end
|
24
|
+
|
25
|
+
# Convert the given MIDI message to Patch::Message objects using the context of the given patch
|
26
|
+
# @param [::Patch::Patch] patch
|
27
|
+
# @param [Array<MIDIMessage>, MIDIMessage] midi_messages
|
28
|
+
# @return [Array<::Patch::Message>]
|
29
|
+
def to_patch_messages(patch, midi_messages)
|
30
|
+
midi_messages = [midi_messages].flatten
|
31
|
+
patch_messages = midi_messages.map do |midi_message|
|
32
|
+
unless (action = Action.find_by_index(patch.actions, midi_message.index)).nil?
|
33
|
+
index = patch.actions.index(action)
|
34
|
+
to_patch_message(action, index, patch.name, midi_message)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
patch_messages.compact
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# @param [Hash] action An action to contextualize the conversion
|
43
|
+
# @param [::Patch::Message] patch_message
|
44
|
+
def get_midi_value_from_action(action, patch_message)
|
45
|
+
to = action[:midi][:scale]
|
46
|
+
to ||= 0..127
|
47
|
+
from = action[:default][:scale] unless action[:default].nil?
|
48
|
+
from ||= to
|
49
|
+
get_value(patch_message.value, from, to)
|
50
|
+
end
|
51
|
+
|
52
|
+
# @param [Hash] action An action to contextualize the conversion
|
53
|
+
# @param [MIDIMessage] midi_message
|
54
|
+
def get_patch_values_from_action(action, midi_message)
|
55
|
+
from = action[:midi][:scale]
|
56
|
+
from ||= 0..127
|
57
|
+
to = action[:default][:scale] unless action[:default].nil?
|
58
|
+
to ||= from
|
59
|
+
get_value(midi_message.value, from, to)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Convert a patch message to a MIDI message
|
63
|
+
# @param [Hash] action An action to contextualize the conversion
|
64
|
+
# @param [::Patch::Message] patch_message
|
65
|
+
# @return [::MIDIMessage::ControlChange, nil]
|
66
|
+
def to_midi_message(action, patch_message)
|
67
|
+
if !action[:midi].nil?
|
68
|
+
index = action[:midi][:index] || patch_message.index
|
69
|
+
channel = action[:midi][:channel] || 0
|
70
|
+
value = get_midi_value_from_action(action, patch_message)
|
71
|
+
MIDIMessage::ControlChange.new(channel, index, value)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Convert a MIDI message to a patch message
|
76
|
+
# @param [Hash] action An action to contextualize the conversion
|
77
|
+
# @param [Fixnum] index The index of the message
|
78
|
+
# @param [Symbol] patch_name A patch name
|
79
|
+
# @param [::MIDIMessage::ControlChange] midi_message
|
80
|
+
# @return [::Patch::Message, nil]
|
81
|
+
def to_patch_message(action, index, patch_name, midi_message)
|
82
|
+
if action[:midi][:channel].nil? || action[:midi][:channel] == midi_message.channel
|
83
|
+
value = get_patch_values_from_action(action, midi_message)
|
84
|
+
properties = {
|
85
|
+
:index => index,
|
86
|
+
:patch_name => patch_name,
|
87
|
+
:value => value
|
88
|
+
}
|
89
|
+
::Patch::Message.new(properties)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Translate a value
|
94
|
+
# @param [Fixnum] value
|
95
|
+
# @param [Range] from
|
96
|
+
# @param [Range] to
|
97
|
+
# @return [Fixnum]
|
98
|
+
def get_value(value, from, to)
|
99
|
+
if from == to
|
100
|
+
value
|
101
|
+
else
|
102
|
+
Scale.transform(value).from(from).to(to)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Patch
|
2
|
+
|
3
|
+
module IO
|
4
|
+
|
5
|
+
module MIDI
|
6
|
+
|
7
|
+
# MIDI Output functions
|
8
|
+
class Output
|
9
|
+
|
10
|
+
attr_reader :id, :device
|
11
|
+
|
12
|
+
# @param [Fixnum] id
|
13
|
+
# @param [String, UniMIDI::Output] device
|
14
|
+
# @param [Hash] options
|
15
|
+
# @option options [Debug] :log
|
16
|
+
def initialize(id, device, options = {})
|
17
|
+
@log = options[:log]
|
18
|
+
@id = id
|
19
|
+
@device = get_output(device)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Convert Patch::Message objects to MIDI and send
|
23
|
+
# @param [Patch::Patch] patch Context
|
24
|
+
# @param [Array<Patch::Message>, Patch::Message] messages Message(s) to send via MIDI
|
25
|
+
# @return [Array<MIDIMessage>]
|
26
|
+
def puts(patch, patch_messages)
|
27
|
+
patch_messages = [patch_messages].flatten
|
28
|
+
messages = ::Patch::IO::MIDI::Message.to_midi_messages(patch, patch_messages)
|
29
|
+
@device.puts(messages) unless messages.empty?
|
30
|
+
messages
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# Initialize the output device given a name or device object. If the name of the device is the string "choose",
|
36
|
+
# the user is prompted to select an availble MIDI output.
|
37
|
+
# @param [String, UniMIDI::Output, nil] device
|
38
|
+
# @return [UniMIDI::Output]
|
39
|
+
def get_output(device)
|
40
|
+
if device.kind_of?(String)
|
41
|
+
if device == "choose"
|
42
|
+
UniMIDI::Output.gets
|
43
|
+
else
|
44
|
+
UniMIDI::Output.find_by_name(device)
|
45
|
+
end
|
46
|
+
elsif device.respond_to?(:puts)
|
47
|
+
device.open if device.kind_of?(UniMIDI::Output)
|
48
|
+
device
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Modules
|
2
|
+
require "patch/io/midi/action"
|
3
|
+
require "patch/io/midi/message"
|
4
|
+
# Classes
|
5
|
+
require "patch/io/midi/input"
|
6
|
+
require "patch/io/midi/output"
|
7
|
+
|
8
|
+
module Patch
|
9
|
+
|
10
|
+
module IO
|
11
|
+
|
12
|
+
# MIDI IO
|
13
|
+
module MIDI
|
14
|
+
|
15
|
+
# Key that will be used by Patch to identify the module
|
16
|
+
KEY = :midi
|
17
|
+
extend self
|
18
|
+
::Patch::IO::Module.add(self)
|
19
|
+
|
20
|
+
# Instantiate a MIDI device based on the given config
|
21
|
+
# @param [Hash] config
|
22
|
+
# @param [Hash] options
|
23
|
+
# @option options [Log] :log
|
24
|
+
# @return [MIDI::Input, MIDI::Output]
|
25
|
+
def new_from_config(config, options = {})
|
26
|
+
klass = get_direction_class(config[:direction])
|
27
|
+
klass.new(config[:id], config[:name], :log => options[:log])
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# Get the direction class for the given key
|
33
|
+
# @param [Symbol] key
|
34
|
+
# @return [Class]
|
35
|
+
def get_direction_class(key)
|
36
|
+
case key.to_sym
|
37
|
+
when :input then Input
|
38
|
+
when :output then Output
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Patch
|
2
|
+
|
3
|
+
module IO
|
4
|
+
|
5
|
+
# Manage node modules
|
6
|
+
class Module
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# Find an IO module by its key
|
11
|
+
# @param [Symbol] key
|
12
|
+
# @return [Module]
|
13
|
+
def find_by_key(key)
|
14
|
+
all.find { |mod| mod::KEY === key }
|
15
|
+
end
|
16
|
+
|
17
|
+
# Add an IO module to the list of modules available to Patch
|
18
|
+
# @param [Module] mod
|
19
|
+
# @return [Array<Module>]
|
20
|
+
def add(mod)
|
21
|
+
@modules ||= []
|
22
|
+
@modules << mod
|
23
|
+
end
|
24
|
+
|
25
|
+
# Mapping of node modules and names
|
26
|
+
# @return [Array<Module>]
|
27
|
+
def all
|
28
|
+
@modules ||= []
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Patch
|
2
|
+
|
3
|
+
module IO
|
4
|
+
|
5
|
+
module OSC
|
6
|
+
|
7
|
+
# Find and identify OSC Actions
|
8
|
+
module Action
|
9
|
+
|
10
|
+
extend self
|
11
|
+
|
12
|
+
# Is the given action OSC?
|
13
|
+
# @param [Hash] action
|
14
|
+
# @return [Boolean]
|
15
|
+
def osc?(action)
|
16
|
+
!action[:osc].nil?
|
17
|
+
end
|
18
|
+
|
19
|
+
# Filter the given actions only to return OSC actions
|
20
|
+
# @param [Array<Hash>] actions
|
21
|
+
# @return [Array<Hash>]
|
22
|
+
def osc_actions(actions)
|
23
|
+
actions.select { |action| osc?(action) }
|
24
|
+
end
|
25
|
+
|
26
|
+
# Find an action by its OSC address
|
27
|
+
# @param [Array<Hash>] actions
|
28
|
+
# @param [String] address
|
29
|
+
# @return [Hash]
|
30
|
+
def find_by_address(actions, address)
|
31
|
+
osc_actions(actions).find do |action|
|
32
|
+
regex = Regexp.new(action[:osc][:address])
|
33
|
+
address.match(regex)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Patch
|
2
|
+
|
3
|
+
module IO
|
4
|
+
|
5
|
+
module OSC
|
6
|
+
|
7
|
+
# OSC Client
|
8
|
+
class Client
|
9
|
+
|
10
|
+
attr_reader :id
|
11
|
+
|
12
|
+
# @param [String] host
|
13
|
+
# @param [Fixnum] port
|
14
|
+
# @param [Hash] options
|
15
|
+
# @option options [Fixnum] :id
|
16
|
+
# @option options [Log] :log
|
17
|
+
def initialize(host, port, options = {})
|
18
|
+
@id = options[:id]
|
19
|
+
@log = options[:log]
|
20
|
+
@client = ::OSC::Client.new(host, port)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Convert message objects to OSC and send
|
24
|
+
# @param [Patch::Patch] patch Context
|
25
|
+
# @param [Array<Patch::Message, ::OSC::Message>, ::OSC::Message, Patch::Message] messages Message(s) to send
|
26
|
+
# @return [Array<::OSC::Message>]]
|
27
|
+
def puts(patch, messages)
|
28
|
+
osc_messages = get_osc_messages(patch, messages)
|
29
|
+
osc_messages.each { |osc_message| @client.send(osc_message) }
|
30
|
+
osc_messages
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# @param [::Patch::Patch] patch
|
36
|
+
# @param [Array<Patch::Message, ::OSC::Message>, ::OSC::Message, Patch::Message] messages Message(s) to send
|
37
|
+
# @return [Array<::OSC::Message>]]
|
38
|
+
def get_osc_messages(patch, messages)
|
39
|
+
messages = [messages].flatten
|
40
|
+
messages.map { |message| ensure_osc_message(patch, message) }
|
41
|
+
end
|
42
|
+
|
43
|
+
# @param [::Patch::Patch] patch
|
44
|
+
# @param [::OSC::Message, Patch::Message] message
|
45
|
+
# @return [::OSC::Message]
|
46
|
+
def ensure_osc_message(patch, message)
|
47
|
+
unless message.kind_of?(::OSC::Message)
|
48
|
+
osc_message = ::Patch::IO::OSC::Message.to_osc_messages(patch, message)
|
49
|
+
end
|
50
|
+
osc_message ||= message
|
51
|
+
osc_message
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Patch
|
2
|
+
|
3
|
+
module IO
|
4
|
+
|
5
|
+
module OSC
|
6
|
+
|
7
|
+
# Convert between OSC message and Patch::Message objects
|
8
|
+
module Message
|
9
|
+
|
10
|
+
extend self
|
11
|
+
|
12
|
+
# Convert a message object to an OSC message given the context of the given patch
|
13
|
+
# @param [::Patch::Patch] patch
|
14
|
+
# @param [::Patch::Message] message
|
15
|
+
# @return [Array<::OSC::Message>]
|
16
|
+
def to_osc_messages(patch, patch_message)
|
17
|
+
messages = []
|
18
|
+
unless (action = get_osc_action(patch.actions, patch_message)).nil?
|
19
|
+
messages << get_osc_message(action, patch_message)
|
20
|
+
end
|
21
|
+
messages
|
22
|
+
end
|
23
|
+
|
24
|
+
# Convert the given OSC message to Patch::Message objects using the context of the given patch
|
25
|
+
# @param [::Patch::Patch] patch
|
26
|
+
# @param [Object] raw_osc
|
27
|
+
# @return [Array<::Patch::Message>]
|
28
|
+
def to_patch_messages(patch, raw_osc)
|
29
|
+
messages = []
|
30
|
+
unless (action = Action.find_by_address(patch.actions, raw_osc.address)).nil?
|
31
|
+
messages << get_patch_message(patch, action, raw_osc)
|
32
|
+
end
|
33
|
+
messages
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# @param [::Patch::Patch] patch
|
39
|
+
# @param [Hash] action
|
40
|
+
# @param [Object] raw_osc
|
41
|
+
# @return [::Patch::Message]
|
42
|
+
def get_patch_message(patch, action, raw_osc)
|
43
|
+
index = patch.actions.index(action)
|
44
|
+
values = get_patch_values_from_action(raw_osc, action)
|
45
|
+
properties = {
|
46
|
+
:index => index,
|
47
|
+
:patch_name => patch.name,
|
48
|
+
:value => values[0]
|
49
|
+
}
|
50
|
+
::Patch::Message.new(properties)
|
51
|
+
end
|
52
|
+
|
53
|
+
# @param [Hash] action
|
54
|
+
# @param [::Patch::Message] patch_message
|
55
|
+
# @return [::OSC::Message]
|
56
|
+
def get_osc_message(action, patch_message)
|
57
|
+
address = action[:osc][:address]
|
58
|
+
value = get_osc_value_from_action(patch_message.value, action)
|
59
|
+
::OSC::Message.new(address, value)
|
60
|
+
end
|
61
|
+
|
62
|
+
# @param [Object] raw_osc
|
63
|
+
# @param [Hash] action
|
64
|
+
# @return [Array<Object>]
|
65
|
+
def get_patch_values_from_action(raw_osc, action)
|
66
|
+
from = action[:osc][:scale]
|
67
|
+
to = action[:default][:scale] unless action[:default].nil?
|
68
|
+
to ||= from
|
69
|
+
raw_osc.to_a.map { |value| get_value(value.to_f, from, to) }
|
70
|
+
end
|
71
|
+
|
72
|
+
# @param [Object] value
|
73
|
+
# @param [Hash] action
|
74
|
+
# @return [Object]
|
75
|
+
def get_osc_value_from_action(value, action)
|
76
|
+
to = action[:osc][:scale]
|
77
|
+
from = action[:default][:scale] unless action[:default].nil?
|
78
|
+
from ||= to
|
79
|
+
get_value(value, from, to)
|
80
|
+
end
|
81
|
+
|
82
|
+
# @param [Array<Hash>] actions
|
83
|
+
# @param [::Patch::Message] patch_message
|
84
|
+
# @return [Hash]
|
85
|
+
def get_osc_action(actions, patch_message)
|
86
|
+
action = actions.at(patch_message.index)
|
87
|
+
action unless action.nil? || action[:osc].nil?
|
88
|
+
end
|
89
|
+
|
90
|
+
# Translate a value
|
91
|
+
# @param [Fixnum] value
|
92
|
+
# @param [Range] from
|
93
|
+
# @param [Range] to
|
94
|
+
# @return [Fixnum]
|
95
|
+
def get_value(value, from, to)
|
96
|
+
if from == to
|
97
|
+
value
|
98
|
+
else
|
99
|
+
Scale.transform(value).from(from).to(to)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
module Patch
|
2
|
+
|
3
|
+
module IO
|
4
|
+
|
5
|
+
module OSC
|
6
|
+
|
7
|
+
# OSC server
|
8
|
+
class Server
|
9
|
+
|
10
|
+
attr_reader :id
|
11
|
+
|
12
|
+
# @param [Fixnum] id
|
13
|
+
# @param [Fixnum] port
|
14
|
+
# @param [Hash] options
|
15
|
+
# @option options [Hash] :echo
|
16
|
+
# @option options [Log] :log
|
17
|
+
def initialize(id, port, options = {})
|
18
|
+
@log = options[:log]
|
19
|
+
@server = nil
|
20
|
+
@active = false
|
21
|
+
@id = id
|
22
|
+
@is_failsafe = true
|
23
|
+
|
24
|
+
configure(port, options)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Is the server active?
|
28
|
+
# @return [Boolean]
|
29
|
+
def active?
|
30
|
+
@active
|
31
|
+
end
|
32
|
+
|
33
|
+
# Start the server
|
34
|
+
# @return [Boolean] Whether the server was started
|
35
|
+
def start
|
36
|
+
@active = true
|
37
|
+
@connection = @server.run
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
41
|
+
# Stop the server
|
42
|
+
# @return [Boolean]
|
43
|
+
def stop
|
44
|
+
@active = false
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
# Disable the message handlers
|
49
|
+
# @return [Boolean]
|
50
|
+
def disable(patch)
|
51
|
+
addresses = get_addresses(patch)
|
52
|
+
addresses.select { |address| @server.remove_method(address) }.any?
|
53
|
+
end
|
54
|
+
|
55
|
+
# Listen for messages
|
56
|
+
# @param [::Patch::Patch] patch The patch to use for context
|
57
|
+
# @param [Proc] callback A callback to fire when messages are received
|
58
|
+
# @return [Boolean] Whether any actions were configured
|
59
|
+
def listen(patch, &callback)
|
60
|
+
addresses = get_addresses(patch)
|
61
|
+
addresses.select { |address| listen_for(address, patch, &callback) }.any?
|
62
|
+
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
|
66
|
+
# @param [::Patch::Patch] patch The patch to use for context
|
67
|
+
# @return [Array<String, Regexp>]
|
68
|
+
def get_addresses(patch)
|
69
|
+
actions = ::Patch::IO::OSC::Action.osc_actions(patch.actions)
|
70
|
+
actions.map { |action| action[:osc][:address] }.compact.uniq
|
71
|
+
end
|
72
|
+
|
73
|
+
# Handle a new message
|
74
|
+
# @param [::Patch::Patch] patch A patch for context
|
75
|
+
# @param [OSC::Message] message The OSC message object
|
76
|
+
# @param [Proc] callback A callback to fire when a message or messages is received
|
77
|
+
# @return [Array<Patch::Message>]
|
78
|
+
def handle_message_received(patch, raw_osc, &callback)
|
79
|
+
messages = ::Patch::IO::OSC::Message.to_patch_messages(patch, raw_osc)
|
80
|
+
echo(patch, raw_osc) if echo?
|
81
|
+
# yield to custom behavior
|
82
|
+
yield(messages) if block_given?
|
83
|
+
messages
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
# @param [Fixnum] port
|
89
|
+
# @param [Hash] options
|
90
|
+
# @option options [Hash] :echo
|
91
|
+
# @return [Boolean]
|
92
|
+
def configure(port, options = {})
|
93
|
+
configure_server(port)
|
94
|
+
unless options[:echo].nil?
|
95
|
+
configure_echo(options[:echo][:host], options[:echo][:port])
|
96
|
+
end
|
97
|
+
true
|
98
|
+
end
|
99
|
+
|
100
|
+
# Listen for messages on the given address
|
101
|
+
# @param [::Patch::Patch] patch The patch to use for context
|
102
|
+
# @param [Proc] callback A callback to fire when messages are received
|
103
|
+
# @return [Boolean] Whether an action was configured
|
104
|
+
def listen_for(address, patch, &callback)
|
105
|
+
@server.add_method(address) do |message|
|
106
|
+
handle_message_received(patch, message, &callback)
|
107
|
+
end
|
108
|
+
true
|
109
|
+
end
|
110
|
+
|
111
|
+
# Configure the underlying server
|
112
|
+
# @param [Fixnum] port
|
113
|
+
# @return [::OSC::Server]
|
114
|
+
def configure_server(port)
|
115
|
+
@server = ::OSC::EMServer.new(port)
|
116
|
+
if @log
|
117
|
+
@server.add_method(/.*/) { |message| @log.puts("Received: #{message.address}") }
|
118
|
+
end
|
119
|
+
@server
|
120
|
+
end
|
121
|
+
|
122
|
+
# Will received messages be echoed back to the client?
|
123
|
+
# @return [Boolean]
|
124
|
+
def echo?
|
125
|
+
!@client.nil?
|
126
|
+
end
|
127
|
+
|
128
|
+
# Echo a message back to the client to update the UI or whatever
|
129
|
+
# @param [::Patch::Patch] patch
|
130
|
+
# @param [OSC::Message] osc_message
|
131
|
+
# @return [Boolean] Whether the echo occurred
|
132
|
+
def echo(patch, osc_message)
|
133
|
+
begin
|
134
|
+
@client.puts(patch, osc_message)
|
135
|
+
true
|
136
|
+
rescue Exception => exception # failsafe
|
137
|
+
@log.exception(exception) if @log
|
138
|
+
::Thread.main.raise(exception) unless @is_failsafe
|
139
|
+
false
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Configure the echo client
|
144
|
+
# @param [String] host
|
145
|
+
# @param [Fixnum] echo
|
146
|
+
# @param [Hash] options
|
147
|
+
# @param [Log] :log
|
148
|
+
# @return [::Patch::IO::OSC::Client]
|
149
|
+
def configure_echo(host, port, options = {})
|
150
|
+
@client = Client.new(host, port, :log => options.fetch(:log, @log))
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
data/lib/patch/io/osc.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Modules
|
2
|
+
require "patch/io/osc/action"
|
3
|
+
require "patch/io/osc/message"
|
4
|
+
# Classes
|
5
|
+
require "patch/io/osc/client"
|
6
|
+
require "patch/io/osc/server"
|
7
|
+
|
8
|
+
module Patch
|
9
|
+
|
10
|
+
module IO
|
11
|
+
|
12
|
+
# Receive OSC messages and do something with them
|
13
|
+
module OSC
|
14
|
+
|
15
|
+
# Key that will be used by Patch to identify the module
|
16
|
+
KEY = :osc
|
17
|
+
extend self
|
18
|
+
::Patch::IO::Module.add(self)
|
19
|
+
|
20
|
+
# Instantiate an OSC server and/or client using the given config
|
21
|
+
# @param [Hash] config
|
22
|
+
# @param [Hash] options
|
23
|
+
# @option options [Action::Container] :actions
|
24
|
+
# @option options [Log] :log
|
25
|
+
# @return [::Patch::IO::OSC::Server]
|
26
|
+
def new_from_config(config, options = {})
|
27
|
+
instance_options = {
|
28
|
+
:log => options[:log]
|
29
|
+
}
|
30
|
+
if config[:server].nil?
|
31
|
+
unless config[:client].nil?
|
32
|
+
instance_options[:id] = config[:id]
|
33
|
+
Client.new(config[:client][:host], config[:client][:port], instance_options)
|
34
|
+
end
|
35
|
+
else
|
36
|
+
instance_options[:echo] = config[:client]
|
37
|
+
Server.new(config[:id], config[:server][:port], instance_options)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|