ruck 0.2.0 → 0.3.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2010 Tom Lieber
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,146 @@
1
+ # ruck: a port of ChucK's strong timing to Ruby!
2
+
3
+ ruck lets you create virtual timelines on which you can
4
+ precisely time the execution of events.
5
+
6
+ This is accomplished using Shred, Clock, and Shreduler:
7
+
8
+ - Shred: a resumable Proc
9
+ - Clock: manages occurrences on a timeline
10
+ - Shreduler: executes shreds on time
11
+
12
+ Here's an example of how to use Shred:
13
+
14
+ shred = Ruck::Shred.new do
15
+ puts "A"
16
+ Ruck::Shred.current.pause
17
+ puts "B"
18
+ Ruck::Shred.current.pause
19
+ puts "C"
20
+ end
21
+
22
+ shred.call
23
+ shred.call
24
+ shred.call
25
+
26
+ # prints:
27
+ # A
28
+ # B
29
+ # C
30
+
31
+ Here's how Clock works:
32
+
33
+ clock = Ruck::Clock.new
34
+
35
+ clock.schedule("C", 3)
36
+ clock.schedule("B", 2)
37
+ clock.schedule("A", 1)
38
+
39
+ 3.times do
40
+ letter, time = clock.unschedule_next
41
+ puts "#{letter} @ #{time}"
42
+ end
43
+
44
+ # prints:
45
+ # A @ 1.0
46
+ # B @ 2.0
47
+ # C @ 3.0
48
+
49
+ Here's how these two are combined with Shreduler:
50
+
51
+ @shreduler = Ruck::Shreduler.new
52
+
53
+ @shreduler.shredule(Ruck::Shred.new do
54
+ %w{ A B C D E }.each do |letter|
55
+ puts "#{letter}"
56
+ @shreduler.shredule(Ruck::Shred.current, @shreduler.now + 1)
57
+ Ruck::Shred.current.pause
58
+ end
59
+ end)
60
+
61
+ @shreduler.shredule(Ruck::Shred.new do
62
+ %w{ 1 2 3 4 5 }.each do |number|
63
+ puts "#{number}"
64
+ @shreduler.shredule(Ruck::Shred.current, @shreduler.now + 1)
65
+ Ruck::Shred.current.pause
66
+ end
67
+ end)
68
+
69
+ @shreduler.run
70
+
71
+ # prints
72
+ # A
73
+ # 1
74
+ # B
75
+ # 2
76
+ # C
77
+ # 3
78
+ # D
79
+ # 4
80
+ # E
81
+ # 5
82
+
83
+ Though this is somewhat inconvenient to use, so when you're
84
+ using just one global Shreduler, you can call
85
+ Shreduler#make_convenient, which adds useful methods to Object
86
+ and Shred so that you can write the above example more
87
+ concisely:
88
+
89
+ @shreduler = Ruck::Shreduler.new
90
+ @shreduler.make_convenient
91
+
92
+ spork do
93
+ %w{ A B C D E }.each do |letter|
94
+ puts "#{letter}"
95
+ Ruck::Shred.yield(1)
96
+ end
97
+ end
98
+
99
+ spork do
100
+ %w{ 1 2 3 4 5 }.each do |number|
101
+ puts "#{number}"
102
+ Ruck::Shred.yield(1)
103
+ end
104
+ end
105
+
106
+ @shreduler.run
107
+
108
+ ## Shredulers and time
109
+
110
+ ruck doesn't specify any behavior for when time passes,
111
+ so by default all shreds are executed as fast as possible
112
+ as they're drained from the queue. In other words, there's
113
+ no mapping from virtual time to anything else, so Shreduler
114
+ only really cares about order.
115
+
116
+ You change this by sub-classing Shreduler and overriding its
117
+ methods. For example, an easy modification is to map the
118
+ time units to seconds:
119
+
120
+ class RealTimeShreduler < Ruck::Shreduler
121
+ def fast_forward(dt)
122
+ super
123
+ sleep(dt)
124
+ end
125
+ end
126
+
127
+ ## Useful Shredulers
128
+
129
+ These gems provide shredulers with other interesting mappings,
130
+ as well as defining convenient DSLs to make shreduling less
131
+ verbose:
132
+
133
+ <dl>
134
+ <dt>ruck-realtime</dt>
135
+ <dd>the above example</dd>
136
+
137
+ <dt>ruck-midi</dt>
138
+ <dd>maps to quarter notes in a MIDI file, quarter
139
+ notes in real-time, or both simultaneously (playing back,
140
+ then saving to disk)</dd>
141
+
142
+ <dt>ruck-ugen</dt>
143
+ <dd>maps to samples in an audio stream, providing a
144
+ simple unit generator framework for reading and writing WAV
145
+ files with effects</dd>
146
+ </dl>
data/Rakefile CHANGED
@@ -15,11 +15,22 @@ begin
15
15
  to its virtual clock. Schedulers can map virtual time to samples
16
16
  in a WAV file, real time, time in a MIDI file, or anything else
17
17
  by overriding "sim_to" in the Shreduler class.
18
+
19
+ A small library of useful unit generators and plenty of examples
20
+ are provided. See the README or the web page for details.
18
21
  EOF
19
22
  gem.has_rdoc = false
23
+ gem.add_dependency "PriorityQueue", ">= 0"
20
24
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
21
25
  end
22
26
  Jeweler::GemcutterTasks.new
23
27
  rescue LoadError
24
28
  puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
25
29
  end
30
+
31
+ if require "spec/rake/spectask"
32
+ desc "Run all specs"
33
+ Spec::Rake::SpecTask.new("spec") do |t|
34
+ t.spec_files = FileList["spec/**/*.rb"]
35
+ end
36
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
@@ -0,0 +1,27 @@
1
+
2
+ require "ruck"
3
+
4
+ include Ruck
5
+
6
+ shred1 = Shred.new do |shred|
7
+ puts "A"
8
+ Shred.current.pause
9
+ puts "B"
10
+ Shred.current.pause
11
+ puts "C"
12
+ end
13
+
14
+ shred2 = Shred.new do |shred|
15
+ puts "1"
16
+ Shred.current.pause
17
+ puts "2"
18
+ Shred.current.pause
19
+ puts "3"
20
+ end
21
+
22
+ shred1.call
23
+ shred2.call
24
+ shred1.call
25
+ shred2.call
26
+ shred1.call
27
+ shred2.call
@@ -0,0 +1,31 @@
1
+
2
+ require "ruck"
3
+
4
+ include Ruck
5
+
6
+ class RealTimeShreduler < Shreduler
7
+ def fast_forward(dt)
8
+ super
9
+ sleep(dt)
10
+ end
11
+ end
12
+
13
+ @shreduler = RealTimeShreduler.new
14
+
15
+ @shreduler.shredule(Shred.new do
16
+ %w{ A B C D E }.each do |letter|
17
+ puts "#{letter}"
18
+ @shreduler.shredule(Shred.current, @shreduler.now + 1)
19
+ Shred.current.pause
20
+ end
21
+ end)
22
+
23
+ @shreduler.shredule(Shred.new do
24
+ %w{ 1 2 3 4 5 }.each do |number|
25
+ puts "#{number}"
26
+ @shreduler.shredule(Shred.current, @shreduler.now + 1)
27
+ Shred.current.pause
28
+ end
29
+ end)
30
+
31
+ @shreduler.run
@@ -0,0 +1,75 @@
1
+
2
+ require "rubygems"
3
+ require "midiator"
4
+ require "ruck"
5
+
6
+ include Ruck
7
+
8
+ def setup_midi
9
+ midi = MIDIator::Interface.new
10
+ # midi.autodetect_driver
11
+ midi.use :dls_synth # OS X built-in synth
12
+
13
+ midi.control_change 32, 10, 1 # TR-808 is Program 26 in LSB bank 1
14
+ midi.program_change 10, 26
15
+
16
+ include MIDIator::Drums
17
+
18
+ midi
19
+ end
20
+
21
+ class RealTimeShreduler < Shreduler
22
+ def fast_forward(dt)
23
+ super
24
+ sleep(dt)
25
+ end
26
+ end
27
+
28
+
29
+
30
+ @midi = setup_midi
31
+ @shreduler = RealTimeShreduler.new
32
+
33
+ @warped_clock = @shreduler.clock.add_child_clock(Clock.new)
34
+
35
+
36
+ # warped bass drum player
37
+ @shreduler.shredule(Shred.new do
38
+ loop do
39
+ @midi.note_on(BassDrum1, 10, 127)
40
+ @midi.note_on(BassDrum1, 10, 127)
41
+
42
+ @shreduler.shredule(Shred.current, @warped_clock.now + 1, @warped_clock)
43
+ Shred.current.pause
44
+ end
45
+ end, nil, @warped_clock)
46
+
47
+
48
+ # normal crash cymbal player
49
+ @shreduler.shredule(Shred.new do
50
+ loop do
51
+ @midi.note_on(CrashCymbal1, 10, 127)
52
+ @midi.note_on(CrashCymbal1, 10, 127)
53
+
54
+ @shreduler.shredule(Shred.current, @shreduler.now + 1)
55
+ Shred.current.pause
56
+ end
57
+ end)
58
+
59
+
60
+ # time warper
61
+ @shreduler.shredule(Shred.new do |shred|
62
+ p = 0.0
63
+
64
+ loop do
65
+ bpm = Math.sin(p += 0.1) * 270.0 + 300.0
66
+ @warped_clock.relative_rate = bpm / 60.0
67
+ puts "#{bpm} bpm"
68
+
69
+ @shreduler.shredule(Shred.current, @shreduler.now + 0.1)
70
+ Shred.current.pause
71
+ end
72
+ end)
73
+
74
+
75
+ @shreduler.run
@@ -0,0 +1,33 @@
1
+
2
+ # this example demonstrates Shreduler#make_convenient,
3
+ # which adds Object#spork and Shred#yield
4
+
5
+ require "ruck"
6
+
7
+ include Ruck
8
+
9
+ class RealTimeShreduler < Shreduler
10
+ def fast_forward(dt)
11
+ super
12
+ sleep(dt)
13
+ end
14
+ end
15
+
16
+ @shreduler = RealTimeShreduler.new
17
+ @shreduler.make_convenient
18
+
19
+ spork do
20
+ %w{ A B C D E }.each do |letter|
21
+ puts "#{letter}"
22
+ Shred.yield(1)
23
+ end
24
+ end
25
+
26
+ spork do
27
+ %w{ 1 2 3 4 5 }.each do |number|
28
+ puts "#{number}"
29
+ Shred.yield(1)
30
+ end
31
+ end
32
+
33
+ @shreduler.run
@@ -0,0 +1,33 @@
1
+
2
+ require "ruck"
3
+
4
+ include Ruck
5
+
6
+ class RealTimeShreduler < Shreduler
7
+ def fast_forward(dt)
8
+ super
9
+ sleep(dt)
10
+ end
11
+ end
12
+
13
+ @shreduler = RealTimeShreduler.new
14
+ @shreduler.make_convenient
15
+
16
+ spork_loop(:gogogo) do
17
+ puts "YEE HAW!"
18
+ puts
19
+ end
20
+
21
+ spork do
22
+ loop do
23
+ Shred.yield(1)
24
+ puts "not yet"
25
+ Shred.yield(1)
26
+ puts "still not yet"
27
+ Shred.yield(1)
28
+ puts "okay now!"
29
+ raise_event(:gogogo)
30
+ end
31
+ end
32
+
33
+ @shreduler.run
@@ -0,0 +1,110 @@
1
+
2
+ require "rubygems"
3
+ require "midiator"
4
+ require "ruck"
5
+
6
+ include Ruck
7
+
8
+ def setup_midi
9
+ midi = MIDIator::Interface.new
10
+ # midi.autodetect_driver
11
+ midi.use :dls_synth # OS X built-in synth
12
+
13
+ include MIDIator::Drums
14
+
15
+ midi
16
+ end
17
+
18
+ class RealTimeShreduler < Ruck::Shreduler
19
+ def run
20
+ @start_time = Time.now
21
+ @simulated_now = 0
22
+ super
23
+ end
24
+
25
+ def fast_forward(dt)
26
+ super
27
+
28
+ actual_now = Time.now - @start_time
29
+ @simulated_now += dt
30
+ if @simulated_now > actual_now
31
+ sleep(@simulated_now - actual_now)
32
+ end
33
+ end
34
+ end
35
+
36
+
37
+ @midi = setup_midi
38
+ @shreduler = RealTimeShreduler.new
39
+ @shreduler.make_convenient
40
+
41
+ @scale = [0, 2, 4, 5, 7, 9, 11]
42
+ @base = 30
43
+ def to_scale(note)
44
+ @base + @scale[note % @scale.length] + 12 * (note / @scale.length).to_i
45
+ end
46
+
47
+ spork_loop(0.2) do
48
+ @midi.play([to_scale((rand * 21).to_i), to_scale((rand * 21).to_i)])
49
+ end
50
+
51
+ spork_loop do
52
+ puts "major"
53
+ # @base = 30
54
+ @scale = [0, 2, 4, 5, 7, 9, 11]
55
+ Shred.yield 5
56
+
57
+ puts "minor"
58
+ # @base = 32
59
+ @scale = [0, 2, 3, 5, 7, 8, 11]
60
+ Shred.yield 5
61
+ end
62
+
63
+ @shreduler.run
64
+
65
+ exit
66
+
67
+
68
+ @warped_clock = @shreduler.clock.add_child_clock(Clock.new)
69
+
70
+
71
+ # warped bass drum player
72
+ @shreduler.shredule(Shred.new do
73
+ loop do
74
+ @midi.note_on(BassDrum1, 10, 127)
75
+ @midi.note_on(BassDrum1, 10, 127)
76
+
77
+ @shreduler.shredule(Shred.current, @warped_clock.now + 1, @warped_clock)
78
+ Shred.current.pause
79
+ end
80
+ end, nil, @warped_clock)
81
+
82
+
83
+ # normal crash cymbal player
84
+ @shreduler.shredule(Shred.new do
85
+ loop do
86
+ @midi.note_on(CrashCymbal1, 10, 127)
87
+ @midi.note_on(CrashCymbal1, 10, 127)
88
+
89
+ @shreduler.shredule(Shred.current, @shreduler.now + 1)
90
+ Shred.current.pause
91
+ end
92
+ end)
93
+
94
+
95
+ # time warper
96
+ @shreduler.shredule(Shred.new do |shred|
97
+ p = 0.0
98
+
99
+ loop do
100
+ bpm = Math.sin(p += 0.1) * 270.0 + 300.0
101
+ @warped_clock.relative_rate = bpm / 60.0
102
+ puts "#{bpm} bpm"
103
+
104
+ @shreduler.shredule(Shred.current, @shreduler.now + 0.1)
105
+ Shred.current.pause
106
+ end
107
+ end)
108
+
109
+
110
+ @shreduler.run