ruck 0.1.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/.gitignore +2 -0
- data/README +85 -0
- data/Rakefile +25 -0
- data/VERSION +1 -0
- data/bin/ruck_glapp +77 -0
- data/bin/ruck_midi +143 -0
- data/bin/ruck_ugen +53 -0
- data/examples/glapp/ex01.rb +13 -0
- data/examples/midi/ex01.rb +24 -0
- data/examples/ugen/ex01.rb +24 -0
- data/examples/ugen/ex02.rb +2 -0
- data/examples/ugen/ex03.rb +8 -0
- data/examples/ugen/ex04.rb +14 -0
- data/examples/ugen/ex05.rb +9 -0
- data/examples/ugen/ex06.rb +10 -0
- data/examples/ugen/ex07.rb +35 -0
- data/examples/ugen/ex08.rb +28 -0
- data/examples/ugen/ex09.rb +26 -0
- data/examples/ugen/ex10.rb +15 -0
- data/examples/ugen/ex11.rb +10 -0
- data/examples/ugen/ex12.rb +9 -0
- data/lib/ruck.rb +13 -0
- data/lib/ruck/bench.rb +44 -0
- data/lib/ruck/misc/linkage.rb +22 -0
- data/lib/ruck/misc/metaid.rb +18 -0
- data/lib/ruck/misc/pcm_time_helpers.rb +29 -0
- data/lib/ruck/misc/riff.rb +71 -0
- data/lib/ruck/misc/wavparse.rb +35 -0
- data/lib/ruck/shreduling.rb +147 -0
- data/lib/ruck/ugen/general.rb +408 -0
- data/lib/ruck/ugen/oscillators.rb +106 -0
- data/lib/ruck/ugen/wav.rb +185 -0
- data/ruck.gemspec +94 -0
- metadata +102 -0
@@ -0,0 +1,408 @@
|
|
1
|
+
|
2
|
+
module Ruck
|
3
|
+
|
4
|
+
module UGen
|
5
|
+
|
6
|
+
def to_s
|
7
|
+
"<#{self.class}" +
|
8
|
+
(name ? "(#{name})" : "") +
|
9
|
+
" #{attr_names.map { |a| "#{a}:#{send a}" }.join " "}>"
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_accessor :name
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def require_attrs(attrs, names)
|
17
|
+
names.each do |name|
|
18
|
+
unless attrs.has_key? name
|
19
|
+
raise "#{self} requires attribute #{name}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_attrs(attrs)
|
25
|
+
attrs.each do |attr, value|
|
26
|
+
send("#{attr}=", value)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def pop_attrs(attrs, names)
|
31
|
+
names.map { |name| attrs.delete(name) }
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
module Target
|
37
|
+
def add_source(ugen)
|
38
|
+
if ugen.is_a? Array
|
39
|
+
ugen.each { |u| add_source u }
|
40
|
+
else
|
41
|
+
@ins << ugen
|
42
|
+
end
|
43
|
+
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def remove_source(ugen)
|
48
|
+
if ugen.is_a? Array
|
49
|
+
ugen.each { |u| remove_source u }
|
50
|
+
else
|
51
|
+
@ins.delete(ugen)
|
52
|
+
end
|
53
|
+
|
54
|
+
self
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module MultiChannelTarget
|
59
|
+
def add_source(ugen)
|
60
|
+
if ugen.is_a? Array
|
61
|
+
ugen.each { |u| add_source u }
|
62
|
+
return self
|
63
|
+
end
|
64
|
+
|
65
|
+
if ugen.out_channels.length == 1
|
66
|
+
@in_channels.each { |chan| chan.add_source ugen.out(0) }
|
67
|
+
else
|
68
|
+
1.upto([ugen.out_channels.length, @in_channels.length].min) do |i|
|
69
|
+
@in_channels[i-1].add_source ugen.out(i-1)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
def remove_source(ugen)
|
77
|
+
if ugen.is_a? Array
|
78
|
+
ugen.each { |u| remove_source u }
|
79
|
+
return
|
80
|
+
end
|
81
|
+
|
82
|
+
# remove all outputs of ugen from all inputs of self
|
83
|
+
@in_channels.each do |in_chan|
|
84
|
+
ugen.out_channels.each do |out_chan|
|
85
|
+
in_chan.remove_source out_chan
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
self
|
90
|
+
end
|
91
|
+
|
92
|
+
def in_channels
|
93
|
+
@in_channels
|
94
|
+
end
|
95
|
+
|
96
|
+
def in(chan)
|
97
|
+
@in_channels[chan]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
module Source
|
102
|
+
def >>(ugen)
|
103
|
+
ugen.add_source self
|
104
|
+
end
|
105
|
+
|
106
|
+
def <<(ugen)
|
107
|
+
ugen.remove_source self
|
108
|
+
end
|
109
|
+
|
110
|
+
def out_channels
|
111
|
+
[self]
|
112
|
+
end
|
113
|
+
|
114
|
+
def out(chan)
|
115
|
+
self if chan == 0
|
116
|
+
end
|
117
|
+
|
118
|
+
def next(now); @last; end
|
119
|
+
def last; @last; end
|
120
|
+
end
|
121
|
+
|
122
|
+
module MultiChannelSource
|
123
|
+
def >>(ugen)
|
124
|
+
ugen.add_source self
|
125
|
+
end
|
126
|
+
|
127
|
+
def <<(ugen)
|
128
|
+
ugen.remove_source self
|
129
|
+
end
|
130
|
+
|
131
|
+
def out_channels
|
132
|
+
@out_channels
|
133
|
+
end
|
134
|
+
|
135
|
+
def out(chan)
|
136
|
+
@out_channels[chan]
|
137
|
+
end
|
138
|
+
|
139
|
+
def next(now, chan = 0); @last[chan]; end
|
140
|
+
def last(chan = 0); @last[chan]; end
|
141
|
+
end
|
142
|
+
|
143
|
+
class InChannel
|
144
|
+
include UGen
|
145
|
+
include Target
|
146
|
+
|
147
|
+
def initialize(attrs = {})
|
148
|
+
parse_attrs attrs
|
149
|
+
@ins = []
|
150
|
+
@last = 0.0
|
151
|
+
end
|
152
|
+
|
153
|
+
def next(now)
|
154
|
+
return @last if @now == now
|
155
|
+
@now = now
|
156
|
+
@last = @ins.inject(0) { |samp, ugen| samp += ugen.next(now) }
|
157
|
+
end
|
158
|
+
|
159
|
+
def attr_names
|
160
|
+
[]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class OutChannel
|
165
|
+
include Source
|
166
|
+
|
167
|
+
def initialize(parent, channel_number)
|
168
|
+
@parent = parent
|
169
|
+
@channel_number = channel_number
|
170
|
+
end
|
171
|
+
|
172
|
+
def next(now)
|
173
|
+
return @last if @now == now
|
174
|
+
@last = @parent.next(now, @channel_number)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
module Generators
|
179
|
+
|
180
|
+
class Gain
|
181
|
+
include UGen
|
182
|
+
include Source
|
183
|
+
include Target
|
184
|
+
|
185
|
+
linkable_attr :gain
|
186
|
+
|
187
|
+
def initialize(attrs = {})
|
188
|
+
parse_attrs({ :gain => 1.0 }.merge(attrs))
|
189
|
+
@ins = []
|
190
|
+
@last = 0.0
|
191
|
+
end
|
192
|
+
|
193
|
+
def next(now)
|
194
|
+
return @last if @now == now
|
195
|
+
@now = now
|
196
|
+
@last = @ins.inject(0) { |samp, ugen| samp += ugen.next(now) } * gain
|
197
|
+
end
|
198
|
+
|
199
|
+
def attr_names
|
200
|
+
[:gain]
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
class Step
|
205
|
+
include UGen
|
206
|
+
include Source
|
207
|
+
|
208
|
+
linkable_attr :value
|
209
|
+
|
210
|
+
def initialize(attrs = {})
|
211
|
+
parse_attrs({ :value => 0.0 }.merge(attrs))
|
212
|
+
@last = value
|
213
|
+
end
|
214
|
+
|
215
|
+
def next(now)
|
216
|
+
return @last if @now == now
|
217
|
+
@now = now
|
218
|
+
@last = value
|
219
|
+
end
|
220
|
+
|
221
|
+
def attr_names
|
222
|
+
[:value]
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
class Delay
|
227
|
+
include UGen
|
228
|
+
include Target
|
229
|
+
include Source
|
230
|
+
|
231
|
+
linkable_attr :gain
|
232
|
+
|
233
|
+
def initialize(attrs = {})
|
234
|
+
require_attrs attrs, [:time]
|
235
|
+
samples = attrs.delete(:time)
|
236
|
+
parse_attrs attrs
|
237
|
+
@ins = []
|
238
|
+
@last = 0.0
|
239
|
+
@queue = [0.0] * samples
|
240
|
+
end
|
241
|
+
|
242
|
+
def next(now)
|
243
|
+
return @last if @now == now
|
244
|
+
@now = now
|
245
|
+
|
246
|
+
@queue << @ins.inject(0) { |samp, ugen| samp += ugen.next(now) } * gain
|
247
|
+
@last = @queue.shift
|
248
|
+
end
|
249
|
+
|
250
|
+
def attr_names
|
251
|
+
[:time]
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
class Noise
|
256
|
+
include UGen
|
257
|
+
include Source
|
258
|
+
|
259
|
+
linkable_attr :gain
|
260
|
+
|
261
|
+
def initialize(attrs = {})
|
262
|
+
parse_attrs({ :gain => 1.0 }.merge(attrs))
|
263
|
+
@last = 0.0
|
264
|
+
end
|
265
|
+
|
266
|
+
def next(now)
|
267
|
+
return @last if @now == now
|
268
|
+
@now = now
|
269
|
+
@last = rand * gain
|
270
|
+
end
|
271
|
+
|
272
|
+
def attr_names
|
273
|
+
[:gain]
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
class Ramp
|
278
|
+
include UGen
|
279
|
+
include Source
|
280
|
+
|
281
|
+
linkable_attr :from
|
282
|
+
linkable_attr :to
|
283
|
+
linkable_attr :duration
|
284
|
+
linkable_attr :progress
|
285
|
+
linkable_attr :paused
|
286
|
+
|
287
|
+
def initialize(attrs = {})
|
288
|
+
parse_attrs({ :from => 0.0,
|
289
|
+
:to => 1.0,
|
290
|
+
:duration => 1.second }.merge(attrs))
|
291
|
+
@progress = 0.0
|
292
|
+
@paused = false
|
293
|
+
@last = 0.0
|
294
|
+
end
|
295
|
+
|
296
|
+
def next(now)
|
297
|
+
return @last if @now == now
|
298
|
+
@now = now
|
299
|
+
@last = progress * (to - from) + from
|
300
|
+
inc_progress
|
301
|
+
@last
|
302
|
+
end
|
303
|
+
|
304
|
+
def reverse
|
305
|
+
@from, @to = @to, @from
|
306
|
+
end
|
307
|
+
|
308
|
+
def reset
|
309
|
+
@progress = 0.0
|
310
|
+
end
|
311
|
+
|
312
|
+
def finished?
|
313
|
+
progress == 1.0
|
314
|
+
end
|
315
|
+
|
316
|
+
def attr_names
|
317
|
+
[:from, :to, :duration, :progress, :paused]
|
318
|
+
end
|
319
|
+
|
320
|
+
protected
|
321
|
+
|
322
|
+
def inc_progress
|
323
|
+
return if @paused
|
324
|
+
@progress += 1.0 / duration
|
325
|
+
@progress = 1.0 if @progress > 1.0
|
326
|
+
end
|
327
|
+
|
328
|
+
end
|
329
|
+
|
330
|
+
class ADSR
|
331
|
+
include UGen
|
332
|
+
include Target
|
333
|
+
include Source
|
334
|
+
|
335
|
+
attr_accessor :attack_time
|
336
|
+
attr_accessor :attack_gain
|
337
|
+
attr_accessor :decay_time
|
338
|
+
attr_accessor :sustain_gain
|
339
|
+
attr_accessor :release_time
|
340
|
+
|
341
|
+
def initialize(attrs = {})
|
342
|
+
parse_attrs({ :attack_time => 50.ms,
|
343
|
+
:attack_gain => 1.0,
|
344
|
+
:decay_time => 50.ms,
|
345
|
+
:sustain_gain => 0.5,
|
346
|
+
:release_time => 500.ms }.merge(attrs))
|
347
|
+
|
348
|
+
@ramp = Ramp.new
|
349
|
+
|
350
|
+
@ins = []
|
351
|
+
@last = 0.0
|
352
|
+
@gain = 0.0
|
353
|
+
@state = :idle
|
354
|
+
end
|
355
|
+
|
356
|
+
def next(now)
|
357
|
+
return @last if @now == now
|
358
|
+
@now = now
|
359
|
+
@gain = case @state
|
360
|
+
when :idle
|
361
|
+
0
|
362
|
+
when :attack
|
363
|
+
if @ramp.finished?
|
364
|
+
@ramp.reset
|
365
|
+
@ramp.from, @ramp.to = @ramp.last, @sustain_gain
|
366
|
+
@ramp.duration = @decay_time
|
367
|
+
@state = :decay
|
368
|
+
end
|
369
|
+
@ramp.next(now)
|
370
|
+
when :decay
|
371
|
+
@state = :sustain if @ramp.finished?
|
372
|
+
@ramp.next(now)
|
373
|
+
when :sustain
|
374
|
+
@sustain_gain
|
375
|
+
when :release
|
376
|
+
@state = :idle if @ramp.finished?
|
377
|
+
@ramp.next(now)
|
378
|
+
end
|
379
|
+
@last = @ins.inject(0) { |samp, ugen| samp += ugen.next(now) } * @gain
|
380
|
+
end
|
381
|
+
|
382
|
+
def on
|
383
|
+
@ramp.reset
|
384
|
+
@ramp.from, @ramp.to = @gain, @attack_gain
|
385
|
+
@ramp.duration = @attack_time
|
386
|
+
@state = :attack
|
387
|
+
end
|
388
|
+
|
389
|
+
def off
|
390
|
+
@ramp.reset
|
391
|
+
@ramp.from, @ramp.to = @gain, 0
|
392
|
+
@ramp.duration = @release_time
|
393
|
+
@state = :release
|
394
|
+
end
|
395
|
+
|
396
|
+
def attr_names
|
397
|
+
[:attack_time, :attack_gain, :decay_time, :sustain_gain, :release_time]
|
398
|
+
end
|
399
|
+
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
end
|
404
|
+
|
405
|
+
# Allow chucking all elements of an array to
|
406
|
+
class Array
|
407
|
+
include Ruck::Source
|
408
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
|
2
|
+
module Ruck
|
3
|
+
|
4
|
+
module Oscillator
|
5
|
+
include UGen
|
6
|
+
TWO_PI = 2 * Math::PI
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
base.instance_eval do
|
10
|
+
linkable_attr :freq
|
11
|
+
linkable_attr :phase
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def phase_forward
|
16
|
+
@phase = (@phase + freq.to_f / SAMPLE_RATE.to_f) % 1.0
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module Generators
|
21
|
+
|
22
|
+
class SinOsc
|
23
|
+
include Source
|
24
|
+
include Oscillator
|
25
|
+
|
26
|
+
linkable_attr :freq
|
27
|
+
linkable_attr :gain
|
28
|
+
|
29
|
+
def initialize(attrs = {})
|
30
|
+
parse_attrs({ :freq => 440.0,
|
31
|
+
:gain => 1.0 }.merge(attrs))
|
32
|
+
@phase = 0.0
|
33
|
+
@last = 0.0
|
34
|
+
end
|
35
|
+
|
36
|
+
def next(now)
|
37
|
+
return @last if @now == now
|
38
|
+
@now = now
|
39
|
+
@last = gain * Math.sin(phase * TWO_PI)
|
40
|
+
phase_forward
|
41
|
+
@last
|
42
|
+
end
|
43
|
+
|
44
|
+
def attr_names
|
45
|
+
[:freq, :gain, :phase]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class SawOsc
|
50
|
+
include Source
|
51
|
+
include Oscillator
|
52
|
+
|
53
|
+
linkable_attr :gain
|
54
|
+
|
55
|
+
def initialize(attrs = {})
|
56
|
+
parse_attrs({ :freq => 440.0,
|
57
|
+
:gain => 1.0 }.merge(attrs))
|
58
|
+
@phase = 0.0
|
59
|
+
@last = 0.0
|
60
|
+
end
|
61
|
+
|
62
|
+
def next(now)
|
63
|
+
return @last if @now == now
|
64
|
+
@now = now
|
65
|
+
@last = ((phase * 2.0) - 1.0) * gain
|
66
|
+
phase_forward
|
67
|
+
@last
|
68
|
+
end
|
69
|
+
|
70
|
+
def attr_names
|
71
|
+
[:freq, :gain, :phase]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class TriOsc
|
76
|
+
include Source
|
77
|
+
include Oscillator
|
78
|
+
|
79
|
+
linkable_attr :gain
|
80
|
+
|
81
|
+
def initialize(attrs = {})
|
82
|
+
parse_attrs({ :freq => 440.0,
|
83
|
+
:gain => 1.0 }.merge(attrs))
|
84
|
+
@phase = 0.0
|
85
|
+
@last = 0.0
|
86
|
+
end
|
87
|
+
|
88
|
+
def next(now)
|
89
|
+
return @last if @now == now
|
90
|
+
@now = now
|
91
|
+
@last = if phase < 0.5
|
92
|
+
phase * 4.0 - 1.0
|
93
|
+
else
|
94
|
+
1.0 - ((phase - 0.5) * 4.0)
|
95
|
+
end * gain
|
96
|
+
phase_forward
|
97
|
+
@last
|
98
|
+
end
|
99
|
+
|
100
|
+
def attr_names
|
101
|
+
[:freq, :gain, :phase]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|