live_api 0.9.0
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/Manifest +3 -0
- data/Rakefile +13 -0
- data/lib/live_api.rb +251 -0
- data/live_api.gemspec +34 -0
- metadata +68 -0
data/Manifest
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake'
|
4
|
+
require 'echoe'
|
5
|
+
|
6
|
+
Echoe.new('live_api', '0.9.0') do |p|
|
7
|
+
p.description = 'This module allows you to create a connection to Ableton Live 8.1.x through a Max4Live plugin. You can then manipulate objects in the LOM (Live Object Model) as though they were Ruby objects.'
|
8
|
+
p.url = 'http://irkenkitties.com'
|
9
|
+
p.author = 'Saf Allen'
|
10
|
+
p.email = 'irkenkitties @nospam@ gmail.com'
|
11
|
+
p.ignore_pattern = ['build.sh']
|
12
|
+
p.development_dependencies = ['rosc']
|
13
|
+
end
|
data/lib/live_api.rb
ADDED
@@ -0,0 +1,251 @@
|
|
1
|
+
###################################################################################
|
2
|
+
# This module allows you to create a connection to Ableton Live 8.1.x through a
|
3
|
+
# Max4Live plugin. You can then manipulate objects in the LOM (Live Object Model)
|
4
|
+
# as though they were Ruby objects.
|
5
|
+
#
|
6
|
+
# Author: Saf Allen
|
7
|
+
###################################################################################
|
8
|
+
require 'rubygems'
|
9
|
+
require 'osc'
|
10
|
+
|
11
|
+
|
12
|
+
### Extend the StringIO to have a skip method
|
13
|
+
class StringIO
|
14
|
+
|
15
|
+
### Skip n bytes
|
16
|
+
def skip(n)
|
17
|
+
self.seek(n, IO::SEEK_CUR)
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
### Skip the OSC 4 byte alinged padding
|
22
|
+
def skip_padding
|
23
|
+
self.skip((4-pos)%4)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
module LiveAPI
|
29
|
+
|
30
|
+
### A class to manage a UDP Open Sound Control connection
|
31
|
+
### to Ableton Live
|
32
|
+
class LiveConnection
|
33
|
+
|
34
|
+
TypeTags = {'i' => :decode_int32, 'f' => :decode_float32, 'b' => :decode_blob, 's' => :decode_string}
|
35
|
+
ClassMap = {Fixnum => 'i', Float => 'f', String => 's'}
|
36
|
+
|
37
|
+
public
|
38
|
+
### Initialize
|
39
|
+
def initialize(host, port)
|
40
|
+
@host = host
|
41
|
+
@server_port = port.succ
|
42
|
+
@client_port = port
|
43
|
+
@client = OSC::UDPSocket.new
|
44
|
+
@server = OSC::UDPServer.new
|
45
|
+
@server.bind(nil, @server_port)
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
### Send an OSC message
|
50
|
+
def request(path, *cmds)
|
51
|
+
type_tags = cmds.map{|cmd| ClassMap[cmd.class]}.join
|
52
|
+
message = OSC::Message.new(path, type_tags, *cmds)
|
53
|
+
@client.send(message, 0, 'localhost', 9000)
|
54
|
+
cmds.last == 'getinfo' ? receive_multi : receive unless(cmds.first == 'set')
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
private
|
59
|
+
### Send a message
|
60
|
+
def send_message(message)
|
61
|
+
@client.send(message, 0, @host, @client_port)
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
### Receive a single message
|
66
|
+
def receive
|
67
|
+
response, sender = @server.recvfrom(32_767)
|
68
|
+
[decode_osc(response)]
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
### Receive a message with multiple parts
|
73
|
+
def receive_multi
|
74
|
+
response_ary = []
|
75
|
+
while 1
|
76
|
+
response, sender = @server.recvfrom(32_767)
|
77
|
+
response = decode_osc(response)
|
78
|
+
response_ary << response
|
79
|
+
break if response[1] == 'done'
|
80
|
+
end
|
81
|
+
return response_ary
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
### Decode and OSC packet
|
86
|
+
def decode_osc(message)
|
87
|
+
parsed = []
|
88
|
+
io = StringIO.new(message)
|
89
|
+
parsed << decode_string(io)
|
90
|
+
type_tags = decode_string(io)
|
91
|
+
type_tags = type_tags.split(//)
|
92
|
+
type_tags.shift
|
93
|
+
type_tags.each do |type_tag|
|
94
|
+
parsed << self.send(TypeTags[type_tag], io) rescue nil
|
95
|
+
end
|
96
|
+
parsed
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
### Decode a 32 bit int
|
101
|
+
def decode_int32(io)
|
102
|
+
i = io.read(4).unpack('N')[0]
|
103
|
+
i = 2**32 - i if i > (2**31-1) # two's complement
|
104
|
+
i
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
### Decode a 32 bit float
|
109
|
+
def decode_float32(io)
|
110
|
+
f = io.read(4).unpack('g')[0]
|
111
|
+
f
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
### Decode a string
|
116
|
+
def decode_string(io)
|
117
|
+
s = io.gets("\0").chomp("\0")
|
118
|
+
io.skip_padding
|
119
|
+
s
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
### Decode a blob of data
|
124
|
+
def decode_blob(io)
|
125
|
+
l = io.read(4).unpack('N')[0]
|
126
|
+
b = io.read(l)
|
127
|
+
io.skip_padding
|
128
|
+
b
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
### Decode a time tag
|
133
|
+
def decode_timetag(io)
|
134
|
+
t1 = io.read(4).unpack('N')[0]
|
135
|
+
t2 = io.read(4).unpack('N')[0]
|
136
|
+
[t1,t2]
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
### LiveObject is a base class for any type of object in Ableton Live
|
142
|
+
class LiveObject
|
143
|
+
|
144
|
+
attr_reader :properties, :functions, :id, :live_path, :type, :children, :live_connection
|
145
|
+
|
146
|
+
### Thanks _why
|
147
|
+
def metaclass; class << self; self; end; end
|
148
|
+
def meta_eval &blk; metaclass.instance_eval &blk; end
|
149
|
+
|
150
|
+
|
151
|
+
### A nicer inspect method
|
152
|
+
def inspect
|
153
|
+
"<#{@type} id: #{@id} #{name.first if self.respond_to?(:name)}>"
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
### Initialize
|
158
|
+
def initialize(live_path, live_connection)
|
159
|
+
@live_path = live_path
|
160
|
+
@live_connection = live_connection
|
161
|
+
setup_object(live_connection.request(live_path, 'getinfo'))
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
### Parse Live's getinfo data to set up this object
|
166
|
+
def setup_object(info_list)
|
167
|
+
@properties = []
|
168
|
+
@functions = []
|
169
|
+
@children = []
|
170
|
+
info_list.each do |info|
|
171
|
+
info.shift
|
172
|
+
case info.first
|
173
|
+
when 'id'
|
174
|
+
@id = info.last
|
175
|
+
when 'type'
|
176
|
+
@type = info.last
|
177
|
+
when 'description'
|
178
|
+
info.shift
|
179
|
+
@description = info.join(' ').gsub('\\', ',')
|
180
|
+
when 'children'
|
181
|
+
info.shift
|
182
|
+
@children << info[0]
|
183
|
+
metaclass.class_eval do
|
184
|
+
define_method(info[0].to_sym) do
|
185
|
+
children = []
|
186
|
+
child_count = getcount(info[0].to_sym)
|
187
|
+
0.upto(child_count - 1) do |i|
|
188
|
+
child = LiveObject.new("#{@live_path}/#{info.first}/#{i}", @live_connection)
|
189
|
+
children << child
|
190
|
+
end
|
191
|
+
return children
|
192
|
+
end
|
193
|
+
end
|
194
|
+
when 'child'
|
195
|
+
info.shift
|
196
|
+
@children << info[0]
|
197
|
+
metaclass.class_eval do
|
198
|
+
define_method(info[0].to_sym) do
|
199
|
+
return LiveObject.new("#{@live_path}/#{info.first}", @live_connection)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
when 'property'
|
203
|
+
@properties << info[1]
|
204
|
+
### Setup property get
|
205
|
+
metaclass.class_eval do
|
206
|
+
define_method(info[1].to_sym) do
|
207
|
+
response = @live_connection.request(@live_path, 'get', info[1]).first
|
208
|
+
# This may be wrong if a request ever returns more than a 2 element array [key,val]
|
209
|
+
return response.last
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
### Setup property set
|
214
|
+
metaclass.class_eval do
|
215
|
+
define_method((info[1] + '=').to_sym) do |arg|
|
216
|
+
@live_connection.request(@live_path, 'set', info[1], arg)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
when 'function'
|
220
|
+
@functions << info[1]
|
221
|
+
metaclass.class_eval do
|
222
|
+
define_method(info[1].to_sym) do
|
223
|
+
@live_connection.request(@live_path, 'call', info[1])
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
### Add method for counting children
|
230
|
+
metaclass.class_eval do
|
231
|
+
define_method(:getcount) do |child|
|
232
|
+
child = child.to_s
|
233
|
+
return 0 unless @children.include?(child) and child.match(/s$/)
|
234
|
+
count = @live_connection.request(@live_path, 'getcount', child)
|
235
|
+
count.last.last
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
### Quickly make a connection on the default port
|
244
|
+
### and return the live_set root object
|
245
|
+
def connect_default
|
246
|
+
lc = LiveConnection.new('localhost', 9000)
|
247
|
+
LiveObject.new('/live_set', lc)
|
248
|
+
end
|
249
|
+
module_function :connect_default
|
250
|
+
|
251
|
+
end
|
data/live_api.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{live_api}
|
5
|
+
s.version = "0.9.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Saf Allen"]
|
9
|
+
s.date = %q{2010-10-14}
|
10
|
+
s.description = %q{This module allows you to create a connection to Ableton Live 8.1.x through a Max4Live plugin. You can then manipulate objects in the LOM (Live Object Model) as though they were Ruby objects.}
|
11
|
+
s.email = %q{irkenkitties @nospam@ gmail.com}
|
12
|
+
s.extra_rdoc_files = ["lib/live_api.rb"]
|
13
|
+
s.files = ["Rakefile", "lib/live_api.rb", "Manifest", "live_api.gemspec"]
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.homepage = %q{http://irkenkitties.com}
|
16
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Live_api"]
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.rubyforge_project = %q{live_api}
|
19
|
+
s.rubygems_version = %q{1.3.1}
|
20
|
+
s.summary = %q{This module allows you to create a connection to Ableton Live 8.1.x through a Max4Live plugin. You can then manipulate objects in the LOM (Live Object Model) as though they were Ruby objects.}
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 2
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
27
|
+
s.add_development_dependency(%q<rosc>, [">= 0"])
|
28
|
+
else
|
29
|
+
s.add_dependency(%q<rosc>, [">= 0"])
|
30
|
+
end
|
31
|
+
else
|
32
|
+
s.add_dependency(%q<rosc>, [">= 0"])
|
33
|
+
end
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: live_api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Saf Allen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-10-14 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rosc
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: This module allows you to create a connection to Ableton Live 8.1.x through a Max4Live plugin. You can then manipulate objects in the LOM (Live Object Model) as though they were Ruby objects.
|
26
|
+
email: irkenkitties @nospam@ gmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- lib/live_api.rb
|
33
|
+
files:
|
34
|
+
- Rakefile
|
35
|
+
- lib/live_api.rb
|
36
|
+
- Manifest
|
37
|
+
- live_api.gemspec
|
38
|
+
has_rdoc: true
|
39
|
+
homepage: http://irkenkitties.com
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options:
|
42
|
+
- --line-numbers
|
43
|
+
- --inline-source
|
44
|
+
- --title
|
45
|
+
- Live_api
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "1.2"
|
59
|
+
version:
|
60
|
+
requirements: []
|
61
|
+
|
62
|
+
rubyforge_project: live_api
|
63
|
+
rubygems_version: 1.3.1
|
64
|
+
signing_key:
|
65
|
+
specification_version: 2
|
66
|
+
summary: This module allows you to create a connection to Ableton Live 8.1.x through a Max4Live plugin. You can then manipulate objects in the LOM (Live Object Model) as though they were Ruby objects.
|
67
|
+
test_files: []
|
68
|
+
|