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