ruck-ugen 0.2.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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/README ADDED
@@ -0,0 +1,31 @@
1
+ ruck-ugen's shreduler calculates an audio unit generator
2
+ graph's output in virtual time. The graph can be modified by
3
+ shreds all the while.
4
+
5
+ The graph is written in Ruby for flexibility, so it's
6
+ too slow (on my computer) for real time, so there is no
7
+ real-time playback. You can use WavOut, though.
8
+ (See examples/ex01.rb)
9
+
10
+ Check out the library of built-in unit generators in
11
+ lib/ruck/ugen/ugen/ and make your own.
12
+
13
+ Unit Generator Usage
14
+ ====================
15
+
16
+ Play a sine wave (examples/ex02.rb):
17
+
18
+ s = SinOsc.new(:freq => 440)
19
+ w = WavOut.new(:filename => "ex02.wav")
20
+ s >> w >> blackhole
21
+ play 3.seconds
22
+
23
+ Attach lambdas to unit generator attributes
24
+ (examples/ex03.rb):
25
+
26
+ wav = WavOut.new(:filename => "ex03.wav")
27
+ sin2 = SinOsc.new(:freq => 3)
28
+ sin = SinOsc.new(:freq => L{ sin2.last * 220 + 660 },
29
+ :gain => L{ 0.5 + sin2.last * 0.5 })
30
+ [sin >> wav, sin2] >> blackhole
31
+ play 3.seconds
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ require "rubygems"
2
+ require "rake"
3
+
4
+ begin
5
+ require "jeweler"
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "ruck-ugen"
8
+ gem.email = "tom@alltom.com"
9
+ gem.homepage = "http://github.com/alltom/ruck-ugen"
10
+ gem.authors = ["Tom Lieber"]
11
+ gem.summary = %Q{ruck shreduler + mini audio unit generator framework inspired by ChucK}
12
+ gem.description = <<-EOF
13
+ A ruck shreduler and mini audio unit generator framework inspired by ChucK.
14
+ Includes library for reading and writing multi-channel WAV files, and some
15
+ basic audio filters.
16
+ EOF
17
+ gem.has_rdoc = false
18
+ gem.add_dependency "ruck", ">= 0"
19
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
20
+ end
21
+ Jeweler::GemcutterTasks.new
22
+ rescue LoadError
23
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
24
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
data/bin/ruck_ugen ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems"
4
+
5
+ # for testing inside gem dir
6
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
7
+
8
+ require "ruck"
9
+ require "ruck/ugen"
10
+
11
+ SAMPLE_RATE = 22050
12
+ SHREDULER = Ruck::UGen::UGenShreduler.new
13
+ BLACKHOLE = Ruck::UGen::InChannel.new
14
+
15
+ filenames = ARGV
16
+ filenames.each do |filename|
17
+ unless File.readable?(filename)
18
+ LOG.fatal "Cannot read file #{filename}"
19
+ exit
20
+ end
21
+ end
22
+
23
+ filenames.each do |filename|
24
+ SHREDULER.spork(filename) do
25
+ include Ruck::UGen::ShredLocal
26
+ include Ruck::UGen::Generators
27
+ require filename
28
+ end
29
+ end
30
+ SHREDULER.run
data/examples/ex01.rb ADDED
@@ -0,0 +1,24 @@
1
+ def beep(wav, chan)
2
+ (s = SawOsc.new(:freq => 440, :gain => 0.25)) >> wav.in(chan)
3
+ 10.times do
4
+ play 0.1.seconds
5
+ s.freq *= 1.2
6
+ end
7
+ s << wav
8
+ end
9
+
10
+ wav = WavOut.new(:filename => "ex01.wav", :num_channels => 2)
11
+ SinOsc.new(:freq => 440, :gain => 0.25) >> wav
12
+ SinOsc.new(:freq => 880, :gain => 0.25) >> wav
13
+
14
+ wav >> blackhole
15
+
16
+ chan = 0
17
+
18
+ 10.times do
19
+ play 0.7.seconds
20
+ chan = (chan + 1) % 2
21
+ spork("beep") { beep(wav, chan) }
22
+ end
23
+
24
+ play 2.seconds
data/examples/ex02.rb ADDED
@@ -0,0 +1,2 @@
1
+ SinOsc.new(:freq => 440) >> WavOut.new(:filename => "ex02.wav") >> blackhole
2
+ play 3.seconds
data/examples/ex03.rb ADDED
@@ -0,0 +1,8 @@
1
+ wav = WavOut.new(:filename => "ex03.wav")
2
+ sin2 = SinOsc.new(:freq => 3)
3
+ sin = SinOsc.new(:freq => L{ sin2.last * 220 + 660 },
4
+ :gain => L{ 0.5 + sin2.last * 0.5 })
5
+
6
+ [sin >> wav, sin2] >> blackhole
7
+
8
+ play 3.seconds
data/examples/ex04.rb ADDED
@@ -0,0 +1,14 @@
1
+ (wav = WavOut.new(:filename => "ex04.wav")) >> blackhole
2
+ s = SawOsc.new(:freq => 440, :gain => 0.5)
3
+ adsr = ADSR.new(:attack_time => 50.ms,
4
+ :attack_gain => 1.0,
5
+ :decay_time => 50.ms,
6
+ :sustain_gain => 0.5,
7
+ :release_time => 1.second)
8
+ s >> adsr >> wav
9
+
10
+ play 1.second
11
+ adsr.on
12
+ play 2.seconds
13
+ adsr.off
14
+ play 2.seconds
data/examples/ex05.rb ADDED
@@ -0,0 +1,9 @@
1
+ wav = WavIn.new(:filename => "ex01.wav")
2
+ wav >> WavOut.new(:filename => "ex05.wav") >> blackhole
3
+
4
+ wav.play
5
+ play 1.second
6
+
7
+ (r = Ramp.new(:from => 1.0, :to => 2.0, :duration => 3.seconds)) >> blackhole
8
+ wav.rate = L{ r.last }
9
+ play 3.seconds
data/examples/ex06.rb ADDED
@@ -0,0 +1,10 @@
1
+ # run ex01.rb first
2
+
3
+ spoken = WavIn.new(:filename => "ex01.wav")
4
+ wav = WavOut.new(:filename => "ex06.wav")
5
+ spoken.out(0) >> wav >> blackhole
6
+
7
+ (sin = SinOsc.new(:freq => 3, :gain => 0.1)) >> blackhole
8
+ spoken.rate = L{ 1.0 + sin.last }
9
+
10
+ play spoken.duration
data/examples/ex07.rb ADDED
@@ -0,0 +1,35 @@
1
+ @bpm = 130.0
2
+ @one_beat = (1.0 / @bpm).minutes
3
+
4
+ (@wav = WavOut.new(:filename => "ex07.wav")) >> blackhole
5
+
6
+ def smash(len = @one_beat)
7
+ (n = Noise.new(:gain => 0.4)) >> (a = ADSR.new) >> @wav
8
+ a.release_time = len
9
+ a.on; play @one_beat / 1.5
10
+ a.off; play len
11
+ a << @wav
12
+ end
13
+
14
+ def beat
15
+ (thump = SawOsc.new(:freq => 220, :gain => 0.7)) >> (a = ADSR.new) >> @wav
16
+ a.on; play @one_beat / 2.0
17
+ a.off; play a.release_time
18
+ a << @wav
19
+ end
20
+
21
+ 4.times do
22
+ spork("beat 1") { beat }; spork("smash") { smash }
23
+ play @one_beat
24
+
25
+ spork("beat 2") { beat }
26
+ play @one_beat
27
+
28
+ spork("beat 3") { beat }
29
+ play @one_beat
30
+
31
+ spork("beat 4") { beat }
32
+ play @one_beat
33
+ end
34
+
35
+ smash(5.seconds)
data/examples/ex08.rb ADDED
@@ -0,0 +1,28 @@
1
+ # An experiment with formants
2
+ # http://en.wikipedia.org/wiki/Formant
3
+
4
+ wav = WavOut.new(:filename => "ex08.wav")
5
+ ramps = (1..4).map { Ramp.new(:duration => 50.ms) }
6
+ oscillators = (1..4).map { SinOsc.new }
7
+ [oscillators >> wav, ramps] >> blackhole
8
+
9
+ (0..3).each { |i| oscillators[i].freq = L{ ramps[i].last } }
10
+
11
+ #vowel_ah = [1000, 1400]
12
+ #vowel_eh = [500, 2300]
13
+ #vowel_oh = [500, 1000]
14
+ vowel_ee = [[320, 1.0], [2500, 1.0], [3200, 1.0], [4600, 0.6]]
15
+ vowel_oo = [[320, 1.0], [800, 0.3], [2500, 0.1], [3300, 0.1]]
16
+
17
+ 5.times do
18
+ [vowel_ee, vowel_oo].each do |vowel|
19
+ puts "doing #{vowel.inspect}"
20
+ (0..3).each do |i|
21
+ ramps[i].reset
22
+ ramps[i].from = ramps[i].to
23
+ ramps[i].to = vowel[i].first
24
+ oscillators[i].gain = vowel[i].last / 4.0
25
+ end
26
+ play 1.second
27
+ end
28
+ end
data/examples/ex09.rb ADDED
@@ -0,0 +1,26 @@
1
+ # run ex01.rb first (or, preferably, use way better input than ex01.wav)
2
+
3
+ =begin
4
+ https://lists.cs.princeton.edu/pipermail/chuck-users/2008-May/002983.html
5
+
6
+ > One way of subjectively "widening" a stereo image is to do the following:
7
+ > feed the left channel back to the right with a short delay, inverted;
8
+ > feed the right channel back to the left with a short delay, inverted;
9
+ =end
10
+
11
+ mix = 0.5
12
+
13
+ wavin = WavIn.new :filename => "ex01.wav", :gain => (1.0 - mix)
14
+ wavout = WavOut.new :filename => "ex09.wav", :num_channels => 2
15
+
16
+ wavin.out(0) >> (delayed_left = Delay.new :time => 10.ms, :gain => mix)
17
+ wavin.out(1) >> (delayed_right = Delay.new :time => 10.ms, :gain => mix)
18
+ inverted_left = Step.new :value => L{ -delayed_left.next(now) }
19
+ inverted_right = Step.new :value => L{ -delayed_right.next(now) }
20
+
21
+ wavout >> blackhole
22
+ [wavin.out(0), inverted_right] >> wavout.in(0)
23
+ [wavin.out(1), inverted_left ] >> wavout.in(1)
24
+
25
+ play wavin.duration
26
+ puts "processed #{wavin.duration/SAMPLE_RATE} seconds"
data/examples/ex10.rb ADDED
@@ -0,0 +1,10 @@
1
+ # multi-channel WavOut
2
+
3
+ s1 = SinOsc.new :freq => 440
4
+ s2 = SinOsc.new :freq => 440 * 2
5
+ wav = WavOut.new :filename => "ex10.wav", :num_channels => 2
6
+ s1 >> wav.in(0)
7
+ s2 >> wav.in(1)
8
+ wav >> blackhole
9
+
10
+ play 3.seconds
data/examples/ex11.rb ADDED
@@ -0,0 +1,9 @@
1
+ # adds 60 Hz hum to a stereo wav file
2
+
3
+ wavin = WavIn.new :filename => "ex10.wav", :gain => 0.5
4
+ wavout = WavOut.new :filename => "ex11.wav", :num_channels => 2
5
+ wavin >> wavout
6
+ SinOsc.new(:freq => 60, :gain => 0.5) >> wavout
7
+ wavout >> blackhole
8
+
9
+ play 3.seconds
@@ -0,0 +1,52 @@
1
+
2
+ # linkable_attr generates accessor methods for
3
+ # instance variables that behaves almost like
4
+ # attr_accessor.
5
+
6
+ # The difference is that if an object is assigned
7
+ # which responds to :call, accessing the method
8
+ # returns the result of invoking :call instead of
9
+ # the Proc/lambda/block itself.
10
+
11
+ # relies on _why's metaid, included below.
12
+
13
+ class Object
14
+ def linkable_attr(attr_sym)
15
+ attr_reader attr_sym
16
+ define_method("#{attr_sym}=") do |val|
17
+ instance_variable_set("@#{attr_sym}", val)
18
+ if val.respond_to? :call
19
+ meta_def attr_sym do
20
+ val.call
21
+ end
22
+ else
23
+ meta_def attr_sym do
24
+ val
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ def L(&block)
31
+ block
32
+ end
33
+ end
34
+
35
+ # stolen from metaid.rb:
36
+ # http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html
37
+ # (that site does not exist, but a mirror can be found)
38
+ class Object
39
+ # The hidden singleton lurks behind everyone
40
+ def metaclass; class << self; self; end; end
41
+ def meta_eval &blk; metaclass.instance_eval &blk; end
42
+
43
+ # Adds methods to a metaclass
44
+ def meta_def name, &blk
45
+ meta_eval { define_method name, &blk }
46
+ end
47
+
48
+ # Defines an instance method within a class
49
+ def class_def name, &blk
50
+ class_eval { define_method name, &blk }
51
+ end
52
+ end
@@ -0,0 +1,101 @@
1
+
2
+ # module for parsing RIFF files (the binary protocol
3
+ # used for packets in a WAV file, for example)
4
+
5
+ # get an array of all the RIFF chunks in a file:
6
+ # RiffReader.new("file.wav").chunks
7
+
8
+ # this library avoids loading the entire file into
9
+ # memory by LEAVING THE FILE OPEN FOR READING FOREVER.
10
+
11
+ module Riff
12
+
13
+ class RiffReaderChunk
14
+ attr_reader :data_start
15
+ attr_accessor :data_skip
16
+
17
+ def initialize(fn, start)
18
+ @fn, @start = fn, start
19
+ @size_start = @start + 4
20
+ @data_start = @start + 8
21
+ @data_skip = 0
22
+ end
23
+
24
+ def type
25
+ return @type if @type
26
+ return nil if @fn.closed?
27
+
28
+ @fn.seek @start
29
+ @type = @fn.read(4)
30
+ end
31
+
32
+ def size
33
+ return @size - @data_skip if @size
34
+ return nil if @fn.closed?
35
+
36
+ @fn.seek @size_start
37
+ @size = @fn.read(4).unpack("L")[0]
38
+ @size - @data_skip
39
+ end
40
+
41
+ # pass a Range of bytes, or start and length
42
+ def [](*args)
43
+ return if @fn.closed?
44
+
45
+ first, last = case args.length
46
+ when 1; [args.first.begin, args.first.end]
47
+ when 2; [args[0], args[0] + args[1]]
48
+ end
49
+ @fn.seek @data_start + @data_skip + first
50
+ @fn.read(last - first + 1)
51
+ end
52
+
53
+ def chunks
54
+ return @chunks if @chunks
55
+ return [] if @fn.closed?
56
+
57
+ offset = @data_start + @data_skip
58
+ @chunks = []
59
+ while offset + @data_skip - @data_start < size
60
+ chunk = RiffReaderChunk.new(@fn, offset)
61
+ @chunks << chunk
62
+ offset += @data_start + chunk.size
63
+ end
64
+
65
+ @chunks
66
+ end
67
+
68
+ def to_s
69
+ "<RiffHeader type:#{type} size:#{size}>"
70
+ end
71
+
72
+ end
73
+
74
+ class RiffReader
75
+ def initialize(filename)
76
+ @fn = File.open(filename, "rb")
77
+ end
78
+
79
+ def chunks
80
+ return @chunks if @chunks
81
+ return [] if @fn.closed?
82
+
83
+ offset = 0
84
+ @chunks = []
85
+ until @fn.eof?
86
+ chunk = RiffReaderChunk.new(@fn, offset)
87
+ @chunks << chunk
88
+ offset += 8 + chunk.size
89
+ @fn.seek offset + 8
90
+ end
91
+
92
+ @chunks
93
+ end
94
+
95
+ def close
96
+ @fn.close unless @fn.closed?
97
+ end
98
+
99
+ end
100
+
101
+ end