gamelan 0.3

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.
@@ -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
+