ruck 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -41,7 +41,6 @@ UGenShreduler:
41
41
  [sin >> wav, sin2] >> blackhole
42
42
  play 3.seconds
43
43
 
44
-
45
44
  RealTimeShreduler
46
45
 
47
46
  This shreduler attempts to keep virtual time in line with
@@ -54,32 +53,28 @@ MIDIShreduler
54
53
  MIDI playback and saving MIDI to disk. An example runner
55
54
  is provided in midi_runner.rb which you invoke like this:
56
55
 
57
- $ ruby midilib_runner.rb MIDI_FILENAME NUM_TRACKS LIVE SCRIPT_FILENAME [...]
56
+ $ ruck_midi MIDI_FILENAME NUM_TRACKS LIVE SCRIPT_FILENAME [...]
58
57
 
59
58
  where LIVE is "no" to only save the MIDI output, or "yes"
60
59
  to also play in real-time.
61
60
 
61
+ GLAppShreduler
62
+
63
+ You don't want to know. (But the scripts are included so
64
+ you can find them if you're really curious.)
65
+
62
66
  USAGE
63
67
  =====
64
68
 
65
- ugen_runner.rb is an (example) program that lets you run scripts
66
- as you do with ChucK, with a shreduler that pulls samples from
67
- the unit generator graph (through the blackhole):
69
+ This project has tons of examples because even though the library
70
+ is tiny, there's a lot to test, and a lot of ways to use it.
68
71
 
69
- $ ruby ugen_runner.rb examples/ex01.rb
72
+ For examples of how to implement your own Shreduler, see the bottom
73
+ of lib/ruck/shreduling.rb and all the scripts in bin/.
70
74
 
71
- It uses a UGenShreduler with a hard-coded sample rate. You can use it
72
- as an example for how to embed your own shreduler. It boils down
73
- to this:
75
+ For example of how to use the provided Shredulers, check the
76
+ examples/ directory. Those scripts contain no boilerplate includes
77
+ because they are written to be invoked on the command line by
78
+ one of the bin/ scripts. For example:
74
79
 
75
- require "ruck"
76
-
77
- @shreduler = Ruck::Shreduler.new # replace with your subclass
78
-
79
- # repeat this part as necessary to seed @shreduler with shreds
80
- @shreduler.spork("main") do
81
- ...
82
- end
83
-
84
- # this returns when no shreds remain
85
- @shreduler.run
80
+ $ ruck_ugen examples/ex01.rb
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.2
data/bin/ruck_glapp CHANGED
@@ -4,6 +4,8 @@ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
4
  # requires glapp gem
5
5
  # sudo gem install alltom-glapp
6
6
  # see http://alltom.com/pages/glapp for more details
7
+ # UPDATE: ugh, github stopped producing gems.
8
+ # find a way to get it on your own 'til I fix that.
7
9
 
8
10
  require "ruck"
9
11
 
@@ -12,32 +14,52 @@ require "glapp"
12
14
 
13
15
  class GLAppShreduler < Ruck::Shreduler
14
16
  def initialize
15
- @shreds_waiting_for_next_frame = []
17
+ @shred_queues = {}
18
+ [:frame,
19
+ :key_down, :key_up,
20
+ :mouse_down, :mouse_up, :mouse_move].each do |q|
21
+ @shred_queues[q] = []
22
+ end
16
23
  super
17
24
  end
18
25
 
26
+ def starting
27
+ @start_time = Time.now
28
+ end
29
+
19
30
  def actual_now
20
31
  Time.now - @start_time
21
32
  end
22
33
 
23
- def enqueue_for_next_frame(shred)
24
- @shreds_waiting_for_next_frame << shred
34
+ def sleep_current_til(queue)
35
+ shred = current_shred
25
36
  @shreds.delete(shred)
37
+ @shred_queues[queue] << shred
38
+ shred.yield(0)
39
+
40
+ @ev
26
41
  end
27
42
 
28
- def next_frame_has_arrived
29
- @shreds += @shreds_waiting_for_next_frame
43
+ def raise_event(ev, queue)
44
+ # should use actual_now,
45
+ # but we don't want to put it into the future
46
+ new_now = actual_now
47
+ new_now = [new_now, next_shred.now].min if next_shred
48
+ @now = new_now
30
49
 
31
- @shreds_waiting_for_next_frame.each do |shred|
32
- shred.now = now
33
- end
50
+ @ev = ev # returned by sleep_current_til
51
+
52
+ shreds = @shred_queues[queue]
53
+ @shred_queues[queue] = []
34
54
 
35
- @shreds_waiting_for_next_frame = []
55
+ shreds.each do |shred|
56
+ shred.now = new_now
57
+ @shreds << shred
58
+ invoke_shred shred
59
+ end
36
60
  end
37
61
 
38
62
  def catch_up
39
- @start_time ||= Time.now
40
-
41
63
  while shreds.length > 0 && next_shred.now < actual_now
42
64
  run_one
43
65
  end
@@ -45,33 +67,96 @@ class GLAppShreduler < Ruck::Shreduler
45
67
  end
46
68
 
47
69
  module ShredLocal
48
- def wait_for_frame
49
- SHREDULER.enqueue_for_next_frame(SHREDULER.current_shred)
50
- SHREDULER.current_shred.yield(0)
70
+ def spork(name = "unnamed", &shred)
71
+ SHREDULER.spork(name, &shred)
51
72
  end
52
73
 
53
74
  def wait(seconds)
54
75
  SHREDULER.current_shred.yield(seconds)
55
76
  end
77
+
78
+ def wait_for_frame
79
+ SHREDULER.sleep_current_til(:frame)
80
+ end
81
+
82
+ def wait_for_key_down
83
+ SHREDULER.sleep_current_til(:key_down)
84
+ end
85
+
86
+ def wait_for_key_up
87
+ SHREDULER.sleep_current_til(:key_down)
88
+ end
89
+
90
+ def wait_for_mouse_down
91
+ SHREDULER.sleep_current_til(:mouse_down)
92
+ end
93
+
94
+ def wait_for_mouse_up
95
+ SHREDULER.sleep_current_til(:mouse_up)
96
+ end
97
+
98
+ def wait_for_mouse_move
99
+ SHREDULER.sleep_current_til(:mouse_move)
100
+ end
56
101
  end
57
102
 
58
103
  class MySketch
104
+ include GLApp
105
+
59
106
  def setup
60
107
  ARGV.each do |filename|
61
108
  SHREDULER.spork(filename) do
62
109
  require filename
63
110
  end
64
111
  end
112
+ @events = []
113
+
114
+ SHREDULER.starting
65
115
  end
66
116
 
67
117
  def draw
68
- SHREDULER.catch_up
69
- SHREDULER.next_frame_has_arrived
118
+ @events.each do |pair|
119
+ ev, queue = pair
120
+ SHREDULER.raise_event ev, queue
121
+ end
122
+ @events = []
123
+
124
+ SHREDULER.catch_up # execute shreds whose time came while drawing previous frame
125
+
126
+ SHREDULER.raise_event Object.new, :frame
127
+ end
128
+
129
+ def keyboard_down(key, modifiers)
130
+ @events << [Struct.new(:key).new(key), :key_down]
131
+ end
132
+
133
+ def keyboard_up(key, modifiers)
134
+ @events << [Struct.new(:key).new(key), :key_up]
135
+ end
136
+
137
+ def special_keyboard_down(key, modifiers)
138
+ @events << [Struct.new(:key).new(key), :key_down]
139
+ end
140
+
141
+ def special_keyboard_up(key, modifiers)
142
+ @events << [Struct.new(:key).new(key), :key_up]
143
+ end
144
+
145
+ def mouse_click(button, state, x, y)
146
+ if state == 0
147
+ @events << [Struct.new(:button, :x, :y).new(button, x, y), :mouse_down]
148
+ else
149
+ @events << [Struct.new(:button, :x, :y).new(button, x, y), :mouse_up]
150
+ end
151
+ end
152
+
153
+ def mouse_motion(x, y)
154
+ @events << [Struct.new(:x, :y).new(x, y), :mouse_move]
70
155
  end
71
156
  end
72
157
 
73
- include GLApp
74
- include ShredLocal
75
158
  SHREDULER = GLAppShreduler.new
76
159
 
160
+ include GLApp::Helpers
161
+ include ShredLocal
77
162
  MySketch.new.show 800, 600, "My Sketch"
data/bin/ruck_midi CHANGED
@@ -124,6 +124,12 @@ module ShredLocal
124
124
 
125
125
  end
126
126
 
127
+ def save
128
+ return if @saved
129
+ @saved = true
130
+ File.open(MIDI_FILENAME, "wb") { |file| SEQUENCE.write(file) }
131
+ end
132
+
127
133
  FILENAMES.each do |filename|
128
134
  unless File.readable?(filename)
129
135
  LOG.fatal "Cannot read file #{filename}"
@@ -138,6 +144,8 @@ FILENAMES.each do |filename|
138
144
  end
139
145
  end
140
146
 
147
+ at_exit { save }
148
+
141
149
  SHREDULER.run
142
150
 
143
- File.open(MIDI_FILENAME, "wb") { |file| SEQUENCE.write(file) }
151
+ save
data/bin/ruck_sleep ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+
4
+ require "ruck"
5
+
6
+ # stuff accessible in a shred
7
+ module ShredLocal
8
+
9
+ def now
10
+ SHREDULER.now
11
+ end
12
+
13
+ def spork(name = "unnamed", &shred)
14
+ SHREDULER.spork(name, &shred)
15
+ end
16
+
17
+ def wait(seconds)
18
+ SHREDULER.current_shred.yield(seconds)
19
+ end
20
+
21
+ def finish
22
+ shred = SHREDULER.current_shred
23
+ SHREDULER.remove_shred shred
24
+ shred.finish
25
+ end
26
+
27
+ end
28
+
29
+
30
+ SAMPLE_RATE = 1
31
+ SHREDULER = Ruck::RealTimeShreduler.new
32
+
33
+ filenames = ARGV
34
+ filenames.each do |filename|
35
+ unless File.readable?(filename)
36
+ LOG.fatal "Cannot read file #{filename}"
37
+ exit
38
+ end
39
+ end
40
+
41
+ filenames.each do |filename|
42
+ SHREDULER.spork(filename) do
43
+ include ShredLocal
44
+ require filename
45
+ end
46
+ end
47
+ SHREDULER.run
@@ -1,9 +1,24 @@
1
1
 
2
2
  clear
3
3
 
4
+ spork("framerate watcher") do
5
+ start_time = Time.now
6
+ frames = 0
7
+
8
+ loop do
9
+ wait_for_frame
10
+ frames += 1
11
+ if frames % 100 == 0
12
+ puts "#{frames.to_f / (Time.now - start_time)}"
13
+ end
14
+ end
15
+
16
+ end
17
+
4
18
  loop do
5
19
  wait 2
6
20
 
21
+ glColor 1, 0, 0
7
22
  glTranslate(0, 0, -5)
8
23
  glutSolidCube(2)
9
24
 
@@ -0,0 +1,57 @@
1
+
2
+ require "rubygems"
3
+ require "midiator"
4
+
5
+ $midi = MIDIator::Interface.new
6
+ $midi.use :dls_synth
7
+ $midi.instruct_user!
8
+
9
+ def note_on(note, velocity = 127, channel = 0)
10
+ $midi.driver.note_on(note, channel, velocity)
11
+ end
12
+
13
+ def note_off(note, channel = 0)
14
+ $midi.driver.note_on(note, channel, 0)
15
+ end
16
+
17
+ def star_shifter(key)
18
+ wait 3
19
+ @stars.shift
20
+ note_off(key)
21
+ end
22
+
23
+ spork do
24
+ while ev = wait_for_key_down
25
+ note_on(ev.key)
26
+
27
+ @stars << Star.new
28
+ spork { star_shifter ev.key }
29
+ end
30
+ end
31
+
32
+ spork do
33
+ while ev = wait_for_key_up
34
+ end
35
+ end
36
+
37
+ class Star
38
+ def initialize
39
+ @x = rand * 2 - 1
40
+ @y = rand * 2 - 1
41
+ end
42
+
43
+ def draw
44
+ glPushMatrix
45
+ glTranslate @x, @y, -5
46
+ glutSolidCube(0.1)
47
+ glPopMatrix
48
+ end
49
+ end
50
+
51
+ clear
52
+
53
+ @stars = []
54
+ while wait_for_frame
55
+ clear
56
+ @stars.each { |star| star.draw }
57
+ end
@@ -0,0 +1,9 @@
1
+
2
+ spork do
3
+ loop do
4
+ ev = wait_for_key_down
5
+ puts "you pressed #{ev.key}"
6
+ end
7
+ end
8
+
9
+ clear
@@ -0,0 +1,72 @@
1
+
2
+ def maybe
3
+ rand >= 0.5
4
+ end
5
+
6
+ @base = 43
7
+ @scale = 0
8
+ @chord = 0
9
+
10
+ major_scale = [0, 2, 4, 5, 7, 9, 11]
11
+ minor_scale = [0, 2, 3, 5, 7, 8, 11]
12
+ @scales = [major_scale, minor_scale]
13
+
14
+ def scale(note, scale)
15
+ len = scale.length
16
+ oct = note / len
17
+ off = note % len
18
+ while off < 0
19
+ off += len
20
+ oct -= 1
21
+ end
22
+ oct * 12 + scale[off]
23
+ end
24
+
25
+ def play(note, dur = 1.beat)
26
+ midi_note = @base + scale(note, @scales[@scale])
27
+ note_on midi_note, 100
28
+ wait dur
29
+ note_off midi_note
30
+ end
31
+
32
+ def change_chords
33
+ loop do
34
+ wait 4.beats
35
+ @chord = rand(7)
36
+ if rand <= 0.1
37
+ @scale = (@scale + 1) % @scales.length
38
+ end
39
+ end
40
+ end
41
+
42
+ def play_chord(dur)
43
+ spork { play(@chord, dur) }
44
+ spork { play(@chord + 2, dur) }
45
+ spork { play(@chord + 4, dur) }
46
+ end
47
+
48
+ def play_chords
49
+ loop do
50
+ if maybe && maybe
51
+ play_chord 2.beats
52
+ wait 2.beats
53
+ play_chord 2.beats
54
+ wait 2.beats
55
+ else
56
+ play_chord 4.beats
57
+ wait 4.beats
58
+ end
59
+ end
60
+ end
61
+
62
+ def play_melody
63
+ loop do
64
+ len = ((rand(2) + 1) / 2.0).beats
65
+ play(@chord + 7 + 2 * rand(4), len)
66
+ wait len
67
+ end
68
+ end
69
+
70
+ spork { change_chords }
71
+ spork { play_chords }
72
+ spork { play_melody }
@@ -2,14 +2,14 @@
2
2
 
3
3
  spork("a") do
4
4
  loop do
5
- play 1.second
5
+ wait 1
6
6
  puts "second"
7
7
  end
8
8
  end
9
9
 
10
10
  spork("b") do
11
11
  loop do
12
- play 0.5.second
12
+ wait 0.5
13
13
  puts " half-second"
14
14
  end
15
15
  end
@@ -2,7 +2,7 @@
2
2
  module Ruck
3
3
 
4
4
  class Shred
5
- attr_reader :now
5
+ attr_accessor :now
6
6
  attr_accessor :finished
7
7
 
8
8
  def initialize(shreduler, now, name, &block)
@@ -15,17 +15,23 @@ module Ruck
15
15
 
16
16
  def go(resume)
17
17
  @resume = resume
18
+
19
+ # TODO
20
+ # I don't think this is the right place to catch errors.
21
+ # I've read the strangest memory errors after an exception
22
+ # is caught here; I have a feeling exceptions ought to be
23
+ # caught within the continuation itself.
18
24
  begin
19
25
  @block.call self
20
26
  rescue => e
21
27
  LOG.error "#{self} exited uncleanly:\n#{e}\n#{e.backtrace}"
22
28
  end
29
+
23
30
  @finished = true
24
31
  end
25
32
 
26
33
  def yield(samples)
27
34
  LOG.debug "shred #{self} yielding #{samples}"
28
- samples = samples
29
35
  @now += samples
30
36
  callcc do |cont|
31
37
  @block = cont # save where we are
@@ -81,20 +87,25 @@ module Ruck
81
87
  @now = new_now
82
88
  end
83
89
 
84
- # invokes the next shred, simulates to the new VM time, then returns
85
- def run_one
86
- @current_shred = next_shred
87
-
88
- sim_to(@current_shred.now)
89
-
90
+ def invoke_shred(shred)
90
91
  # execute shred, saving this as the resume point
91
92
  LOG.debug "resuming shred #{@current_shred} at #{now}"
93
+ @current_shred = shred
92
94
  callcc { |cont| @current_shred.go(cont) }
93
95
  LOG.debug "back to run loop"
96
+ end
97
+
98
+ # invokes the next shred, simulates to the new VM time, then returns
99
+ def run_one
100
+ shred = next_shred
101
+
102
+ sim_to(shred.now)
103
+
104
+ invoke_shred shred
94
105
 
95
106
  if @current_shred.finished
96
- LOG.debug "#{@current_shred} finished"
97
- @shreds.delete(@current_shred)
107
+ LOG.debug "#{shred} finished"
108
+ @shreds.delete(shred)
98
109
  end
99
110
  end
100
111
 
data/ruck.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{ruck}
8
- s.version = "0.1.0"
8
+ s.version = "0.1.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Tom Lieber"]
12
- s.date = %q{2009-11-16}
12
+ s.date = %q{2009-11-21}
13
13
  s.description = %q{ Ruck uses continuations and a simple scheduler to ensure "shreds"
14
14
  (threads in Ruck) are woken at precisely the right time according
15
15
  to its virtual clock. Schedulers can map virtual time to samples
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
20
20
  are provided. See the README or the web page for details.
21
21
  }
22
22
  s.email = %q{tom@alltom.com}
23
- s.executables = ["ruck_glapp", "ruck_midi", "ruck_ugen"]
23
+ s.executables = ["ruck_glapp", "ruck_midi", "ruck_sleep", "ruck_ugen"]
24
24
  s.extra_rdoc_files = [
25
25
  "README"
26
26
  ]
@@ -31,9 +31,13 @@ Gem::Specification.new do |s|
31
31
  "VERSION",
32
32
  "bin/ruck_glapp",
33
33
  "bin/ruck_midi",
34
+ "bin/ruck_sleep",
34
35
  "bin/ruck_ugen",
35
36
  "examples/glapp/ex01.rb",
37
+ "examples/glapp/ex02.rb",
38
+ "examples/glapp/ex03.rb",
36
39
  "examples/midi/ex01.rb",
40
+ "examples/midi/ex02.rb",
37
41
  "examples/ugen/ex01.rb",
38
42
  "examples/ugen/ex02.rb",
39
43
  "examples/ugen/ex03.rb",
@@ -66,7 +70,10 @@ Gem::Specification.new do |s|
66
70
  s.summary = %q{strong timing for Ruby: cooperative threads on a virtual clock}
67
71
  s.test_files = [
68
72
  "examples/glapp/ex01.rb",
73
+ "examples/glapp/ex02.rb",
74
+ "examples/glapp/ex03.rb",
69
75
  "examples/midi/ex01.rb",
76
+ "examples/midi/ex02.rb",
70
77
  "examples/ugen/ex01.rb",
71
78
  "examples/ugen/ex02.rb",
72
79
  "examples/ugen/ex03.rb",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruck
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Lieber
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-11-16 00:00:00 -08:00
12
+ date: 2009-11-21 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -18,6 +18,7 @@ email: tom@alltom.com
18
18
  executables:
19
19
  - ruck_glapp
20
20
  - ruck_midi
21
+ - ruck_sleep
21
22
  - ruck_ugen
22
23
  extensions: []
23
24
 
@@ -30,9 +31,13 @@ files:
30
31
  - VERSION
31
32
  - bin/ruck_glapp
32
33
  - bin/ruck_midi
34
+ - bin/ruck_sleep
33
35
  - bin/ruck_ugen
34
36
  - examples/glapp/ex01.rb
37
+ - examples/glapp/ex02.rb
38
+ - examples/glapp/ex03.rb
35
39
  - examples/midi/ex01.rb
40
+ - examples/midi/ex02.rb
36
41
  - examples/ugen/ex01.rb
37
42
  - examples/ugen/ex02.rb
38
43
  - examples/ugen/ex03.rb
@@ -87,7 +92,10 @@ specification_version: 3
87
92
  summary: "strong timing for Ruby: cooperative threads on a virtual clock"
88
93
  test_files:
89
94
  - examples/glapp/ex01.rb
95
+ - examples/glapp/ex02.rb
96
+ - examples/glapp/ex03.rb
90
97
  - examples/midi/ex01.rb
98
+ - examples/midi/ex02.rb
91
99
  - examples/ugen/ex01.rb
92
100
  - examples/ugen/ex02.rb
93
101
  - examples/ugen/ex03.rb