gamelan 0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,27 @@
1
+ = Gamelan
2
+
3
+ Gamelan is a good-enough soft real-time event scheduler especially for music
4
+ applications. It exposes a simple API for executing Ruby code at a required
5
+ time. Uses include sending MIDI or OSC messages to external applications or
6
+ hardware.
7
+
8
+ Gamelan also makes life easier by supporting logical time. Logical time is
9
+ reflected in the scheduler's phase. The unit in logical time is the beat, and
10
+ the Scheduler's phase will increment by 1.0 with every beat.
11
+
12
+ Logical time varies with real time according to the tempo, which is specified
13
+ in bpm. For example, the Scheduler's phase will increment by 2.0 for every
14
+ second that elapses when using the default tempo of 120bpm. Applications are
15
+ free to alter the tempo at any time, including from within tasks.
16
+
17
+ = Notes
18
+
19
+ The author admits that Ruby is not at all friendly to realtime applications.
20
+ No guarantees are made about the scheduler's performance. It will not drift
21
+ (it will always stay in sync with the system clock), but jitter is inevitable.
22
+ This is minimized by using a hybrid spinlock implementation to wait between
23
+ dispatches, and by using a reasonably efficient priority queue to store Tasks.
24
+
25
+ The design is an elaboration of Topher Cyll's Timer implementation from his
26
+ book, <em>Practical Ruby Projects</em>, and the Priority Queue implementation
27
+ comes from Brian Amberg.
@@ -0,0 +1,15 @@
1
+ # Gamelan is a good-enough soft real-time event scheduler,
2
+ # written in Ruby, especially for music applications.
3
+ #
4
+ # Copyright (c) 2008 Jeremy Voorhis <jvoorhis@gmail.com>
5
+ #
6
+ # This code released under the terms of the MIT license.
7
+
8
+ def jruby?
9
+ defined?(JRUBY_VERSION)
10
+ end
11
+
12
+ require 'rubygems'
13
+ require 'gamelan/scheduler'
14
+ require 'gamelan/task'
15
+
@@ -0,0 +1,56 @@
1
+ module Gamelan
2
+
3
+ if jruby?
4
+
5
+ class Queue
6
+ include Java
7
+ include_package 'java.util.concurrent'
8
+
9
+ def initialize(sched)
10
+ @scheduler = sched
11
+ comparator = lambda { |a,b| a.delay <=> b.delay }
12
+ @queue = PriorityBlockingQueue.new(10000, &comparator)
13
+ @lock = Mutex.new
14
+ end
15
+
16
+ def push(task)
17
+ @lock.synchronize { @queue.add(task) }
18
+ end
19
+ alias << push
20
+
21
+ def pop
22
+ @lock.synchronize { @queue.remove }
23
+ end
24
+
25
+ def ready?
26
+ @queue.peek && @queue.peek.delay < @scheduler.phase
27
+ end
28
+ end
29
+
30
+ else
31
+
32
+ require 'priority_queue/c_priority_queue'
33
+ require 'priority_queue'
34
+
35
+ class Queue
36
+ def initialize(sched)
37
+ @scheduler = sched
38
+ @queue = ::PriorityQueue.new
39
+ end
40
+
41
+ def push(task)
42
+ @queue.push(task, task.delay)
43
+ end
44
+ alias << push
45
+
46
+ def pop
47
+ @queue.delete_min[0]
48
+ end
49
+
50
+ def ready?
51
+ @queue.min && @queue.min[1] < @scheduler.phase
52
+ end
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,28 @@
1
+ require 'gamelan/timer'
2
+ require 'gamelan/queue'
3
+
4
+ module Gamelan
5
+ # The scheduler allows the user to schedule tasks, represented by +Gamelan::Task+.
6
+ class Scheduler < Timer
7
+
8
+ # Construct a new scheduler. +Scheduler#run+ must be called explicitly once a Scheduler is created. Accepts two options, +:tempo+ and +:rate+.
9
+ # [+:tempo+] The tempo's scheduler, in bpm. For example, at +:tempo => 120+, the scheduler's logical +phase+ will advance by 2.0 every 60 seconds.
10
+ # [+:rate+] Frequency in Hz at which the scheduler will attempt to run ready tasks. For example, The scheduler will poll for tasks 100 times in one
11
+ # second when +:rate+ is 100.
12
+ def initialize(options = {})
13
+ super
14
+ @queue = Gamelan::Queue.new(self)
15
+ end
16
+
17
+ # Schedule a task to be performed at +delay+ beats.
18
+ def at(delay, *params, &task)
19
+ @queue << Task.new(self, delay.to_f, *params, &task)
20
+ end
21
+
22
+ protected
23
+ # Run all ready tasks.
24
+ def dispatch
25
+ @queue.pop.run while @queue.ready?
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,25 @@
1
+ require 'forwardable'
2
+
3
+ module Gamelan
4
+
5
+ # Tasks run by the Scheduler. A task is a combination of a block to be
6
+ # run, a delay, in beats, that specifies when to run the Task, and an
7
+ # optional list of args. A reference to the Scheduler is also stored, so it
8
+ # can be manipulatd by tasks.
9
+ class Task
10
+ extend Forwardable
11
+ def_delegators :@scheduler, :at, :phase, :rate, :time
12
+ attr_reader :delay, :args, :scheduler
13
+
14
+ # Construct a Task with a Scheduler reference, a delay in beats, an
15
+ # optional list of args, and a block.
16
+ def initialize(sched, delay, *args, &block)
17
+ @scheduler, @delay, @proc, @args = sched, delay, block, args
18
+ end
19
+
20
+ # The scheduler will invoke Task#run is called with the Task's +delay+ at
21
+ # the scheduled time. Any optional +args+, if given, will follow.
22
+ # are yielded to the block.
23
+ def run; @proc[@delay, *@args] end
24
+ end
25
+ end
@@ -0,0 +1,62 @@
1
+ module Gamelan
2
+ # The Timer is responsible for executing code at a fixed rate. Users must
3
+ # subclass +Gamelan::Timer+. See +Gamelan::Scheduler+ for an example.
4
+ class Timer
5
+ attr_reader :phase, :rate, :time
6
+
7
+ # Construct a new timer. +Timer#run+ must be called explicitly once a Timer
8
+ # is created. Accepts two options, +:tempo+ and +:rate+.
9
+ # [+:tempo+] The timer's tempo, in bpm. For example, at +:tempo => 120+,
10
+ # the timer's logical +phase+ will advance by 2.0 every 60 seconds.
11
+ # [+:rate+] Frequency in Hz at which the scheduler will dispatch.
12
+ def initialize(options = {})
13
+ self.tempo = options.fetch(:tempo, 120)
14
+ @rate = 1.0 / options.fetch(:rate, 1000)
15
+ @sleep_for = rate / 10.0
16
+ end
17
+
18
+ # Initialize the scheduler's clock, and begin executing tasks.
19
+ def run
20
+ return if @running
21
+ @running = true
22
+ @thread = Thread.new do
23
+ @phase = 0.0
24
+ @origin = @time = Time.now.to_f
25
+ loop { dispatch; advance }
26
+ end
27
+ end
28
+
29
+ # Halt the scheduler. Note that the scheduler may be restarted, but is
30
+ # not resumable.
31
+ def stop
32
+ @running = false
33
+ @thread.kill
34
+ end
35
+
36
+ # Current tempo, in bpm.
37
+ def tempo
38
+ @tempo * 60.0
39
+ end
40
+
41
+ # Set the tempo in bpm.
42
+ def tempo=(bpm)
43
+ @tempo = bpm / 60.0
44
+ end
45
+
46
+ def join; @thread.join end
47
+
48
+ private
49
+ # Advances the internal clock time and spins until it is reached.
50
+ def advance
51
+ @time += @rate
52
+ @phase += (@time - @origin) * @tempo
53
+ @origin = @time
54
+ sleep(@sleep_for) until Time.now.to_f >= @time
55
+ end
56
+
57
+ # Run all ready tasks.
58
+ def dispatch
59
+ raise NotImplementedError, "subclass responsibility"
60
+ end
61
+ end
62
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gamelan
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.3"
5
+ platform: ruby
6
+ authors:
7
+ - Jeremy Voorhis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-01-21 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: PriorityQueue
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.1.2
24
+ version:
25
+ description:
26
+ email: jvoorhis@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README.rdoc
33
+ files:
34
+ - lib/gamelan/queue.rb
35
+ - lib/gamelan/scheduler.rb
36
+ - lib/gamelan/task.rb
37
+ - lib/gamelan/timer.rb
38
+ - lib/gamelan.rb
39
+ - README.rdoc
40
+ has_rdoc: true
41
+ homepage: http://github.com/jvoorhis/gamelan
42
+ post_install_message:
43
+ rdoc_options:
44
+ - --title
45
+ - Gamelan
46
+ - --main
47
+ - README.rdoc
48
+ - --line-numbers
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ requirements: []
64
+
65
+ rubyforge_project: gamelan
66
+ rubygems_version: 1.2.0
67
+ signing_key:
68
+ specification_version: 2
69
+ summary: Gamelan is a good-enough soft real-time event scheduler, written in Ruby, especially for music applications.
70
+ test_files: []
71
+