ruck 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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