ruck-ugen 0.2.0

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