live_api 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/Manifest +3 -0
  2. data/Rakefile +13 -0
  3. data/lib/live_api.rb +251 -0
  4. data/live_api.gemspec +34 -0
  5. metadata +68 -0
@@ -0,0 +1,3 @@
1
+ Rakefile
2
+ lib/live_api.rb
3
+ Manifest
@@ -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
@@ -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
@@ -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
+