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.
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
+