mattly-schuschein-client 0.1

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,31 @@
1
+ # Schuschein Client
2
+
3
+ by Matthew Lyon <matt@flowerpowered.com>
4
+
5
+ ## DESCRIPTION:
6
+
7
+ Schuschein is a protocol for synchronizing events across multiple processes and possibly multiple computers on a very rapid timeline. It is intended to be used in music sequencing.
8
+
9
+ The name comes from the "Glass Clock of Bad Schuschein" in Terry Pratchett's [Thief of Time](http://wiki.lspace.org/wiki/Glass_Clock) novel, after a clock that ticks at the speed of the universe, thereby destroying time.
10
+
11
+ ## FEATURES
12
+
13
+ - Stores blocks in a queue to be called at a specified tick time in the future
14
+ - Receives Time Packets from Schuschein Server (not yet released), pulls scheduled processes out and calls them.
15
+
16
+ # SYNOPSIS
17
+
18
+ scheduler = Schuschein.new 5000
19
+ process = lambda {|tick, sch| puts "the time is now: #{tick}" }
20
+ [480, 960, 1440, 1920].each {|time| scheduler.queue.push time, process}
21
+ a.listen
22
+ sleep 30
23
+ # start the Schuschein server at position 0
24
+ # => the time is now: 480
25
+ # => the time is now: 960
26
+ # => the time is now: 1440
27
+ # => the time is now: 1920
28
+
29
+ ## REQUIREMENTS
30
+
31
+ - [Datagrammer](http://github.com/mattly/datagrammer)
@@ -0,0 +1,5 @@
1
+ require "rake"
2
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
3
+
4
+ desc "Run the specs."
5
+ task :default => :spec
@@ -0,0 +1,39 @@
1
+ require 'datagrammer'
2
+
3
+ alias :L :lambda
4
+
5
+ $:.unshift File.dirname(__FILE__)
6
+ require 'schuschein/schedule'
7
+ class Schuschein
8
+
9
+ def initialize(port=33333, address="0.0.0.0")
10
+ @queue = Schuschein::Schedule.new
11
+ @listener = Datagrammer.new(port, :address => address)
12
+ end
13
+
14
+ attr_accessor :position, :now, :queue, :listener
15
+
16
+ def listen
17
+ @listener.listen do |dg, msg|
18
+ send(msg.shift.sub(/^\//,''), *msg)
19
+ end
20
+ end
21
+
22
+ def tick(bar, beat, pulse, absolute)
23
+ @position = bar, beat, pulse
24
+ @now = absolute
25
+ @queue.shift_to(@now).each {|time, proc| proc.call(time, self) }
26
+ end
27
+
28
+ def tempo(rate=nil)
29
+ return @tempo unless rate
30
+ self.tempo = rate
31
+ end
32
+
33
+ def tempo=(rate=nil)
34
+ return unless rate.kind_of?(Numeric)
35
+ @tempo = rate
36
+ @listener.speak('/tempo', rate)
37
+ end
38
+
39
+ end
@@ -0,0 +1,30 @@
1
+ class Schuschein
2
+ class Schedule
3
+
4
+ def initialize
5
+ @contents = []
6
+ @time = 0
7
+ end
8
+
9
+ attr_accessor :contents, :time
10
+
11
+ def at(position, &block)
12
+ @contents.push [position.to_i, block]
13
+ end
14
+
15
+ def in(delta, &block)
16
+ at @time + delta.to_i.abs, &block
17
+ end
18
+
19
+ def next(multiple, &block)
20
+ at @time + multiple - @time % multiple, &block
21
+ end
22
+
23
+ def shift_to(position)
24
+ @time = position
25
+ ready, @contents = @contents.partition {|pos, proc| pos <= position }
26
+ ready
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,76 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe Schuschein::Schedule do
4
+
5
+ before do
6
+ @schedule = Schuschein::Schedule.new
7
+ end
8
+
9
+ it "has an empty queue" do
10
+ @schedule.contents.should be_empty
11
+ end
12
+
13
+ it "has a zero time value" do
14
+ @schedule.time.should be_zero
15
+ end
16
+
17
+ describe "queueing" do
18
+ before do
19
+ @event = L{ puts "hi" }
20
+ end
21
+
22
+ it "queues things to a specific time with at" do
23
+ @schedule.at(100, &@event)
24
+ @schedule.contents.should include([100, @event])
25
+ end
26
+
27
+ it "queues things to an arbitrary future time with in" do
28
+ @schedule.time = 100
29
+ @schedule.in(100, &@event)
30
+ @schedule.contents.should include([200, @event])
31
+ end
32
+
33
+ it "queues things to the next multiple of time with next" do
34
+ @schedule.time = 500
35
+ @schedule.next(480, &@event)
36
+ @schedule.contents.should include([960, @event])
37
+ end
38
+
39
+ end
40
+
41
+ describe "dequeueing" do
42
+ before do
43
+ @time = 100
44
+ @event = L{ puts "hi"}
45
+ @schedule.at @time, &@event
46
+ end
47
+
48
+ it "doesn't return events after the given time" do
49
+ @schedule.shift_to(99).should be_empty
50
+ end
51
+
52
+ it "doesn't remove items after the given time" do
53
+ @schedule.shift_to(99)
54
+ @schedule.contents.should == [[@time, @event]]
55
+ end
56
+
57
+ it "returns events for the specified time" do
58
+ @schedule.shift_to(100).should == [[@time, @event]]
59
+ end
60
+
61
+ it "returns events before the specified time" do
62
+ @schedule.shift_to(101).should == [[@time, @event]]
63
+ end
64
+
65
+ it "removes events when returning them" do
66
+ @schedule.shift_to(100)
67
+ @schedule.contents.should be_empty
68
+ end
69
+
70
+ it "sets the internal time for reference" do
71
+ @schedule.shift_to(100)
72
+ @schedule.time.should == 100
73
+ end
74
+ end
75
+
76
+ end
@@ -0,0 +1,68 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe Schuschein do
4
+
5
+ before do
6
+ @s = Schuschein.new
7
+ end
8
+
9
+ after do
10
+ @s.listener.socket.close
11
+ end
12
+
13
+ it "should have a schedule object for its queue" do
14
+ @s.queue.should be_kind_of(Schuschein::Schedule)
15
+ end
16
+
17
+ it "should have a datagrammer for its listener" do
18
+ @s.listener.should be_kind_of(Datagrammer)
19
+ end
20
+
21
+ describe "responding to tick messages" do
22
+
23
+ def tick
24
+ @s.tick(4,1,1,7680)
25
+ end
26
+
27
+ it "sets the position" do
28
+ tick
29
+ @s.position.should == [4,1,1]
30
+ end
31
+
32
+ it "shifts the queue to the absolute time" do
33
+ @s.queue.should_receive(:shift_to).with(7680).and_return([[7679, proc { "" }]])
34
+ tick
35
+ end
36
+
37
+ it "calls the procs returned from the queue" do
38
+ var = 0
39
+ @s.queue.stub!(:shift_to).and_return([[7679, proc {|t,s| var = t }]])
40
+ tick
41
+ var.should == 7679
42
+ end
43
+
44
+ end
45
+
46
+ describe "tempo" do
47
+ it "returns the tempo on #tempo without arguments" do
48
+ @s.instance_variable_set(:@tempo, 120)
49
+ @s.tempo.should == 120
50
+ end
51
+
52
+ it "stores the tempo value on #tempo(arg)" do
53
+ @s.tempo(96)
54
+ @s.tempo.should == 96
55
+ end
56
+
57
+ it "responds by calling #tempo= on #tempo(arg)" do
58
+ @s.should_receive(:tempo=).with(96)
59
+ @s.tempo(96)
60
+ end
61
+
62
+ it "tells the listener to speak the new tempo on #tempo=" do
63
+ @s.listener.should_receive(:speak).with("/tempo", 96)
64
+ @s.tempo = 96
65
+ end
66
+ end
67
+
68
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+
3
+ begin
4
+ require 'spec'
5
+ rescue LoadError
6
+ gem 'rspec'
7
+ require 'spec'
8
+ end
9
+
10
+ gem 'ruby-debug'
11
+ require 'ruby-debug'
12
+
13
+ require "#{File.dirname(__FILE__)}/../lib/schuschein"
14
+
15
+ Debugger.start
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mattly-schuschein-client
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - Matthew Lyon
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-10-25 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: mattly-datagrammer
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.1.1
23
+ version:
24
+ description: client for the schuschein distributed scheduler
25
+ email: matt@flowerpowered.com
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files: []
31
+
32
+ files:
33
+ - README.mkdn
34
+ - Rakefile
35
+ - spec/schedule_spec.rb
36
+ - spec/schuschein_spec.rb
37
+ - spec/spec_helper.rb
38
+ - lib/schuschein
39
+ - lib/schuschein/schedule.rb
40
+ - lib/schuschein.rb
41
+ has_rdoc: false
42
+ homepage: http://github.com/mattly/schuschein-client
43
+ post_install_message:
44
+ rdoc_options: []
45
+
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 1.8.6
53
+ version:
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.2.0
64
+ signing_key:
65
+ specification_version: 2
66
+ summary: client for the schuschein distributed scheduler
67
+ test_files: []
68
+