basic-sequencer 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 69fcf47b967d669ca28420c35c71dd4a642229da
4
+ data.tar.gz: 96e296ed53e0f27a539dcfb8f32a9ebf461fc519
5
+ SHA512:
6
+ metadata.gz: 79b566c5927160eead43d655a1a06c08eea322f63b6ec12bb1bcbf5ed64cfd48ad75aaa9210107829ef39fac244a42f6350278cd9fe3cc96d4084de9bc03d303
7
+ data.tar.gz: dae58578dc14438b6133954e076679ad6bad8da0606ece4b222cf3488a80b7b934997ca93ffda0693425a8983f0e9286d0267ba5adb47da185e590e8c2286512
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2011-2014 Ari Russo
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,25 @@
1
+ #Sequencer
2
+
3
+ Perform a sequence of events at tempo
4
+
5
+ The tempo clock is MIDI syncable by way of [topaz](http://github.com/arirusso/topaz).
6
+
7
+ This is used by [diamond](http://github.com/arirusso/diamond)
8
+
9
+ ## Installation
10
+
11
+ `gem install basic-sequencer`
12
+
13
+ or with Bundler, add this to your Gemfile
14
+
15
+ `gem "basic-sequencer"`
16
+
17
+ ## Documentation
18
+
19
+ * [rdoc](http://rubydoc.info/github/arirusso/sequencer)
20
+
21
+ ## License
22
+
23
+ Licensed under Apache 2.0, See the file LICENSE
24
+
25
+ Copyright © 2011-2014 Ari Russo
data/lib/sequencer.rb ADDED
@@ -0,0 +1,24 @@
1
+ #
2
+ # Sequencer
3
+ # Perform a sequence of events at tempo
4
+ #
5
+ # (c)2011-2014 Ari Russo
6
+ # Licensed under Apache 2.0
7
+ #
8
+
9
+ # libs
10
+ require "forwardable"
11
+ require "topaz"
12
+
13
+ # classes
14
+ require "sequencer/clock"
15
+ require "sequencer/core"
16
+ require "sequencer/event"
17
+ require "sequencer/event_trigger"
18
+ require "sequencer/loop"
19
+
20
+ module Sequencer
21
+
22
+ VERSION = "0.0.4"
23
+
24
+ end
@@ -0,0 +1,75 @@
1
+ module Sequencer
2
+
3
+ # A light wrapper for Topaz::Tempo that adds some event handling
4
+ class Clock
5
+
6
+ extend Forwardable
7
+
8
+ attr_reader :event
9
+ def_delegators :@clock, :pause, :stop, :unpause
10
+
11
+ # @param [Fixnum, UniMIDI::Input] tempo_or_input
12
+ # @param [Hash] options
13
+ # @option options [Array<UniMIDI::Output>, UniMIDI::Output] :outputs MIDI output device(s)
14
+ def initialize(tempo_or_input, options = {})
15
+ @event = Event.new
16
+ initialize_clock(tempo_or_input, options.fetch(:resolution, 128), :outputs => options[:outputs])
17
+ end
18
+
19
+ # Start the clock
20
+ # @param [Hash] options
21
+ # @option options [Boolean] :blocking Whether to run in the foreground (also :focus, :foreground)
22
+ # @option options [Boolean] :suppress_clock Whether this clock is a sync-slave
23
+ # @return [Boolean]
24
+ def start(options = {})
25
+ clock_options = {}
26
+ clock_options[:background] = ![:blocking, :focus, :foreground].any? { |key| !!options[key] }
27
+ @clock.start(clock_options) unless !!options[:suppress_clock]
28
+ Thread.abort_on_exception = true
29
+ end
30
+
31
+ private
32
+
33
+ # Action taken by the clock on a tick. Fires the tick event
34
+ def on_tick
35
+ @event.do_tick
36
+ end
37
+
38
+ # Instantiate the underlying clock object
39
+ # @param [Fixnum, UniMIDI::Input] tempo_or_input
40
+ # @param [Fixnum] resolution
41
+ # @param [Hash] options
42
+ # @option options [Array<UniMIDI::Output>, UniMIDI::Output] :outputs MIDI output device(s)
43
+ def initialize_clock(tempo_or_input, resolution, options = {})
44
+ @clock = Topaz::Tempo.new(tempo_or_input, :midi => options[:outputs])
45
+ @clock.interval = @clock.interval * (resolution / @clock.interval)
46
+ @clock.on_tick { on_tick }
47
+ end
48
+
49
+ # Clock event callbacks
50
+ class Event
51
+
52
+ def initialize
53
+ @tick = []
54
+ end
55
+
56
+ # Access the tick event callback
57
+ # @param [Proc] block
58
+ # @return [Proc]
59
+ def tick(&block)
60
+ if block_given?
61
+ @tick.clear
62
+ @tick << block
63
+ end
64
+ @tick
65
+ end
66
+
67
+ # Fire the tick event callback
68
+ # @return [Boolean]
69
+ def do_tick
70
+ !@tick.empty? && @tick.map(&:call)
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,77 @@
1
+ module Sequencer
2
+
3
+ # The core sequencer
4
+ class Core
5
+
6
+ attr_reader :event, :loop, :trigger
7
+ attr_accessor :pointer
8
+
9
+ def initialize
10
+ @event = Event.new
11
+ @loop = Loop.new
12
+ @pointer = 0
13
+ @trigger = EventTrigger.new
14
+ end
15
+
16
+ # Execute a single cycle of sequencing (perform and step)
17
+ # @param [Array] sequence The sequence to execute a single cycle of
18
+ # @return [Boolean] Whether perform and step both finished
19
+ def exec(sequence)
20
+ perform(sequence) && step(sequence)
21
+ end
22
+
23
+ # Step the sequencer and fire the step event
24
+ # @param [Array] sequence
25
+ # @return [Boolean]
26
+ def step(sequence)
27
+ if reset_pointer?(:length => sequence.length)
28
+ reset_pointer
29
+ else
30
+ @pointer += 1
31
+ end
32
+ @event.do_step
33
+ true
34
+ end
35
+
36
+ # If a stop is triggered, stop. Otherwise if reset is triggered, reset. Finally,
37
+ # fire the perform event on the current step of the sequence
38
+ # @param [Array] sequence
39
+ # @return [Boolean] True if perform event was fired
40
+ def perform(sequence)
41
+ data = sequence.at(@pointer)
42
+ @event.do_next(@pointer, data) if @event.next?(@pointer)
43
+ if @trigger.stop?(@pointer, data)
44
+ @event.do_stop
45
+ false
46
+ else
47
+ reset_pointer if @trigger.reset?(@pointer, data)
48
+ @event.do_perform(data)
49
+ true
50
+ end
51
+ end
52
+
53
+ # Set the pointer to the loop start point
54
+ # @return [Fixnum]
55
+ def reset_pointer
56
+ @pointer = @loop.next
57
+ end
58
+
59
+ private
60
+
61
+ # Is the pointer at the point where it needs to be reset?
62
+ # @param [Hash] options
63
+ # @option options [Fixnum] :length The length of the sequence (used when the default loop is active)
64
+ # @return [Boolean]
65
+ def reset_pointer?(options = {})
66
+ !@loop.disabled? && !@loop.in_bounds?(@pointer + 1, :length => options[:length])
67
+ end
68
+
69
+ end
70
+
71
+ # Shortcut to the Core constructor
72
+ # @return [Core]
73
+ def self.new
74
+ Core.new
75
+ end
76
+
77
+ end
@@ -0,0 +1,86 @@
1
+ module Sequencer
2
+
3
+ # Events that are fired by the sequencer
4
+ class Event
5
+
6
+ def initialize
7
+ @next = {}
8
+ @perform = []
9
+ @step = []
10
+ @stop = []
11
+ end
12
+
13
+ def next(pointer = nil, &block)
14
+ if block_given?
15
+ @next[pointer] ||= []
16
+ @next[pointer] << block
17
+ end
18
+ hash
19
+ end
20
+
21
+ def next?(pointer = nil)
22
+ !@next[pointer].nil?
23
+ end
24
+
25
+ def do_next(pointer, data)
26
+ keys = [pointer, nil]
27
+ callbacks = keys.map { |key| @next.delete(key) }.flatten.compact
28
+ callbacks.each(&:call)
29
+ true
30
+ end
31
+
32
+ # Set the step event
33
+ # @param [Proc] block
34
+ # @return [Proc]
35
+ def step(&block)
36
+ if block_given?
37
+ @step.clear
38
+ @step << block
39
+ end
40
+ @step
41
+ end
42
+
43
+ # Fire the step events
44
+ # @return [Boolean]
45
+ def do_step
46
+ !@step.empty? && @step.map(:call)
47
+ end
48
+
49
+ # Access the stop events
50
+ # @param [Proc] block
51
+ # @return [Proc]
52
+ def stop(&block)
53
+ if block_given?
54
+ @stop.clear
55
+ @stop << block
56
+ end
57
+ @stop
58
+ end
59
+
60
+ # Fire the stop event
61
+ # @return [Boolean]
62
+ def do_stop
63
+ @stop.map(&:call)
64
+ end
65
+
66
+ # Set the perform event
67
+ # @param [Proc] block
68
+ # @return [Proc]
69
+ def perform(&block)
70
+ if block_given?
71
+ @perform.clear
72
+ @perform << block
73
+ end
74
+ @perform
75
+ end
76
+
77
+ # Fire the perform event
78
+ # @param [Object] data Data for the current sequence step
79
+ # @return [Boolean]
80
+ def do_perform(data)
81
+ @perform.map { |callback| callback.call(data) }
82
+ end
83
+
84
+ end
85
+
86
+ end
@@ -0,0 +1,78 @@
1
+ module Sequencer
2
+
3
+ # Callbacks that when evaluate to true, will trigger the corresponding sequencer event
4
+ class EventTrigger
5
+
6
+ # Set the reset trigger. When true, the sequence will go back to step 0
7
+ # @param [Proc] block
8
+ # @return [Proc]
9
+ def reset(&block)
10
+ @reset = block
11
+ end
12
+
13
+ # Set the rest trigger. When true, no messages will be outputted during that step
14
+ # @param [Proc] block
15
+ # @return [Proc]
16
+ def rest(&block)
17
+ @rest = block
18
+ end
19
+
20
+ # Whether the reset event should fire
21
+ # @param [Fixnum] pointer The sequencer pointer
22
+ # @param [Object] data Data for the current sequence step
23
+ # @return [Boolean]
24
+ def reset?(pointer, data)
25
+ !@reset.nil? && @reset.call(pointer, data)
26
+ end
27
+
28
+ # Whether the rest event should fire
29
+ # @param [Fixnum] pointer The sequencer pointer
30
+ # @param [Object] data Data for the current sequence step
31
+ # @return [Boolean]
32
+ def rest?(pointer, data)
33
+ !@rest.nil? && @rest.call(pointer, data)
34
+ end
35
+
36
+ # Set the stop trigger. When true, the sequencer will stop
37
+ # @param [Proc] block
38
+ # @return [Proc]
39
+ def stop(&block)
40
+ @stop = block
41
+ end
42
+
43
+ # Whether to fire the stop event
44
+ # @param [Fixnum] pointer The sequencer pointer
45
+ # @param [Object] data Data for the current sequence step
46
+ # @return [Boolean]
47
+ def stop?(pointer, data)
48
+ !@stop.nil? && @stop.call(pointer, data)
49
+ end
50
+
51
+ # Shortcut to trigger a rest event on a given interval of ticks
52
+ # @param [Fixnum, nil] num The number of ticks or nil to cancel existing triggers
53
+ # @return [Fixnum, nil]
54
+ def rest_every(num)
55
+ if num.nil?
56
+ @rest = nil
57
+ else
58
+ rest { |pointer| pointer % num == 0 }
59
+ num
60
+ end
61
+ end
62
+
63
+ # Shortcut to trigger a reset even on a given interval of ticks
64
+ # @param [Fixnum, nil] num The number of ticks or nil to cancel existing triggers
65
+ # @return [Fixnum, nil]
66
+ def reset_every(num)
67
+ if num.nil?
68
+ @reset = nil
69
+ else
70
+ reset { |pointer| pointer % num == 0 }
71
+ num
72
+ end
73
+ end
74
+
75
+ end
76
+
77
+ end
78
+
@@ -0,0 +1,60 @@
1
+ module Sequencer
2
+
3
+ class Loop
4
+
5
+ attr_reader :count, :range
6
+
7
+ def initialize
8
+ @count = 0
9
+ @range = nil
10
+ @is_disabled = false
11
+ end
12
+
13
+ # Set the loop range
14
+ # @param [Array<Fixnum>, FalseClass, Fixnum, Range] value
15
+ # @return [FalseClass, Range]
16
+ def range=(value)
17
+ @range = to_range(value)
18
+ end
19
+
20
+ def start
21
+ default? ? 0 : @range.begin
22
+ end
23
+
24
+ def next
25
+ @count += 1
26
+ start
27
+ end
28
+
29
+ def default?
30
+ @range.nil?
31
+ end
32
+
33
+ def disabled?
34
+ @is_disabled
35
+ end
36
+
37
+ def disable
38
+ @is_disabled = true
39
+ end
40
+
41
+ def in_bounds?(num, options = {})
42
+ length = options[:length]
43
+ range = default? ? 0..(length-1) : @range
44
+ range.include?(num)
45
+ end
46
+
47
+ private
48
+
49
+ # @param [Array<Fixnum>, Fixnum, Range] value
50
+ # @return [FalseClass, Range]
51
+ def to_range(value)
52
+ case value
53
+ when Array then (value[0]..value[1])
54
+ when Fixnum then (0..value)
55
+ when Range then value
56
+ end
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,51 @@
1
+ require "helper"
2
+
3
+ class Sequencer::ClockTest < Test::Unit::TestCase
4
+
5
+ context "Clock" do
6
+
7
+ setup do
8
+ @clock = Sequencer::Clock.new(120)
9
+ end
10
+
11
+ context "Clock::Event#tick" do
12
+
13
+ setup do
14
+ @flag = false
15
+ @flag2 = false
16
+ end
17
+
18
+ should "assign single callback" do
19
+ @clock.event.tick { @flag = true }
20
+ refute @flag
21
+ @clock.event.do_tick
22
+ assert @flag
23
+ end
24
+
25
+ should "reassign single callback" do
26
+ @clock.event.tick { @flag = true }
27
+ @clock.event.tick { @flag2 = true }
28
+ refute @flag
29
+ refute @flag2
30
+ @clock.event.do_tick
31
+ refute @flag
32
+ assert @flag2
33
+ end
34
+
35
+ should "allow multiple callbacks" do
36
+ @clock.event.tick { @flag = true }
37
+ @clock.event.tick << proc { @flag2 = true }
38
+ refute @flag
39
+ refute @flag2
40
+ @clock.event.do_tick
41
+ assert @flag
42
+ assert @flag2
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+
data/test/core_test.rb ADDED
@@ -0,0 +1,133 @@
1
+ require "helper"
2
+
3
+ class Sequencer::CoreTest < Test::Unit::TestCase
4
+
5
+ context "Core" do
6
+
7
+ setup do
8
+ @sequencer = Sequencer::Core.new
9
+ end
10
+
11
+ context "#exec" do
12
+
13
+ setup do
14
+ @sequence = [1,2,3,4]
15
+ end
16
+
17
+ should "move to next" do
18
+ assert_equal 0, @sequencer.pointer
19
+ @sequencer.exec(@sequence)
20
+ assert_equal 1, @sequencer.pointer
21
+ end
22
+
23
+ should "do repeat" do
24
+ @sequencer.pointer = @sequence.length - 1
25
+ @sequencer.exec(@sequence)
26
+ assert_equal 0, @sequencer.pointer
27
+ end
28
+
29
+ should "stop" do
30
+ @sequencer.trigger.expects(:stop?).once.returns(true)
31
+ @sequencer.trigger.expects(:reset?).never
32
+ @sequencer.event.expects(:do_perform).never
33
+ @sequencer.exec(@sequence)
34
+ end
35
+
36
+ should "reset and fire event" do
37
+ @sequencer.pointer = 3
38
+ @sequencer.trigger.expects(:stop?).once.returns(false)
39
+ @sequencer.trigger.expects(:reset?).once.returns(true)
40
+ @sequencer.event.expects(:do_perform).once
41
+ @sequencer.exec(@sequence)
42
+ assert_equal 1, @sequencer.pointer
43
+ end
44
+
45
+ should "fire event" do
46
+ @sequencer.pointer = 2
47
+ @sequencer.trigger.expects(:stop?).once.returns(false)
48
+ @sequencer.trigger.expects(:reset?).once.returns(false)
49
+ @sequencer.event.expects(:do_perform).once
50
+ @sequencer.exec(@sequence)
51
+ assert_equal 3, @sequencer.pointer
52
+ end
53
+
54
+ end
55
+
56
+ context "#step" do
57
+
58
+ setup do
59
+ @sequence = [1,2,3,4]
60
+ @sequencer.event.expects(:do_step).once
61
+ end
62
+
63
+ should "move to next" do
64
+ assert_equal 0, @sequencer.pointer
65
+ @sequencer.step(@sequence)
66
+ assert_equal 1, @sequencer.pointer
67
+ end
68
+
69
+ should "do repeat" do
70
+ @sequencer.pointer = @sequence.length - 1
71
+ @sequencer.step(@sequence)
72
+ assert_equal 0, @sequencer.pointer
73
+ end
74
+
75
+ end
76
+
77
+ context "#perform" do
78
+
79
+ setup do
80
+ @sequence = [1,2,3,4]
81
+ end
82
+
83
+ should "stop" do
84
+ @sequencer.trigger.expects(:stop?).once.returns(true)
85
+ @sequencer.trigger.expects(:reset?).never
86
+ @sequencer.event.expects(:do_perform).never
87
+ @sequencer.perform(@sequence)
88
+ end
89
+
90
+ should "reset and fire event" do
91
+ @sequencer.pointer = 3
92
+ @sequencer.trigger.expects(:stop?).once.returns(false)
93
+ @sequencer.trigger.expects(:reset?).once.returns(true)
94
+ @sequencer.event.expects(:do_perform).once
95
+ @sequencer.perform(@sequence)
96
+ assert_equal 0, @sequencer.pointer
97
+ end
98
+
99
+ should "fire event" do
100
+ @sequencer.pointer = 3
101
+ @sequencer.trigger.expects(:stop?).once.returns(false)
102
+ @sequencer.trigger.expects(:reset?).once.returns(false)
103
+ @sequencer.event.expects(:do_perform).once
104
+ @sequencer.perform(@sequence)
105
+ assert_equal 3, @sequencer.pointer
106
+ end
107
+
108
+ end
109
+
110
+ context "Functional" do
111
+
112
+ setup do
113
+ @clock = Sequencer::Clock.new(120)
114
+ @clock.event.tick { @sequencer.exec(@sequence) }
115
+ @sequencer.event.stop { @clock.stop }
116
+ @sequence = [1,2,3,4]
117
+ @cache = []
118
+ @sequencer.event.perform { | data| @cache << data }
119
+ @sequencer.trigger.stop { @sequencer.loop.count == 1 }
120
+ @clock.start(:focus => true)
121
+ end
122
+
123
+ should "create cache of sequence" do
124
+ assert_equal @sequence, @cache
125
+ end
126
+
127
+ end
128
+
129
+
130
+ end
131
+
132
+ end
133
+
@@ -0,0 +1,89 @@
1
+ require "helper"
2
+
3
+ class Sequencer::EventTriggerTest < Test::Unit::TestCase
4
+
5
+ context "EventTrigger" do
6
+
7
+ setup do
8
+ @trigger = Sequencer::EventTrigger.new
9
+ end
10
+
11
+ context "#reset?" do
12
+
13
+ setup do
14
+ @has_run = false
15
+ @trigger.reset { |p, d| @has_run = d }
16
+ end
17
+
18
+ should "fire stop trigger" do
19
+ assert @trigger.reset?(0, true)
20
+ assert @has_run = true
21
+ end
22
+
23
+ end
24
+
25
+ context "#reset_every" do
26
+
27
+ setup do
28
+ @trigger.reset_every(3)
29
+ end
30
+
31
+ should "reset every three ticks" do
32
+ 3.times do |i|
33
+ assert @trigger.reset?((i * 3) + 0, false)
34
+ refute @trigger.reset?((i * 3) + 1, false)
35
+ refute @trigger.reset?((i * 3) + 2, false)
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ context "#rest_every" do
42
+
43
+ setup do
44
+ @trigger.rest_every(3)
45
+ end
46
+
47
+ should "reset every three ticks" do
48
+ 3.times do |i|
49
+ assert @trigger.rest?((i * 3) + 0, false)
50
+ refute @trigger.rest?((i * 3) + 1, false)
51
+ refute @trigger.rest?((i * 3) + 2, false)
52
+ end
53
+ end
54
+
55
+ end
56
+
57
+ context "#rest?" do
58
+
59
+ setup do
60
+ @has_run = false
61
+ @trigger.rest { |p, d| @has_run = d }
62
+ end
63
+
64
+ should "fire stop trigger" do
65
+ assert @trigger.rest?(0, true)
66
+ assert @has_run = true
67
+ end
68
+
69
+ end
70
+
71
+ context "#stop?" do
72
+
73
+ setup do
74
+ @has_run = false
75
+ @trigger.stop { |p, d| @has_run = d }
76
+ end
77
+
78
+ should "fire stop trigger" do
79
+ assert @trigger.stop?(0, true)
80
+ assert @has_run = true
81
+ end
82
+
83
+ end
84
+
85
+ end
86
+
87
+ end
88
+
89
+
data/test/helper.rb ADDED
@@ -0,0 +1,15 @@
1
+ dir = File.dirname(File.expand_path(__FILE__))
2
+ $LOAD_PATH.unshift dir + "/../lib"
3
+
4
+ require "test/unit"
5
+ require "mocha/test_unit"
6
+ require "shoulda-context"
7
+ require "sequencer"
8
+
9
+ module TestHelper
10
+
11
+ def self.select_midi_output
12
+ $midi_output = UniMIDI::Output.gets
13
+ end
14
+
15
+ end
data/test/loop_test.rb ADDED
@@ -0,0 +1,89 @@
1
+ require "helper"
2
+
3
+ class Sequencer::LoopTest < Test::Unit::TestCase
4
+
5
+ context "Loop" do
6
+
7
+ setup do
8
+ @loop = Sequencer::Loop.new
9
+ end
10
+
11
+ context "#to_range" do
12
+
13
+ should "convert array" do
14
+ assert_equal 1..6, @loop.send(:to_range, [1, 6])
15
+ end
16
+
17
+ should "convert number" do
18
+ assert_equal 0..9, @loop.send(:to_range, 9)
19
+ end
20
+
21
+ should "pass range" do
22
+ assert_equal 5..8, @loop.send(:to_range, 5..8)
23
+ end
24
+
25
+ end
26
+
27
+ context "#start" do
28
+
29
+ context "default loop" do
30
+
31
+ should "always have 0 as start" do
32
+ assert @loop.default?
33
+ assert_equal 0, @loop.start
34
+ end
35
+
36
+ end
37
+
38
+ context "custom loop" do
39
+
40
+ setup do
41
+ @loop.range = 3..6
42
+ end
43
+
44
+ should "have custom start point" do
45
+ refute @loop.default?
46
+ assert_equal 3, @loop.start
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+
53
+ context "#in_bounds?" do
54
+
55
+ context "default loop" do
56
+
57
+ should "include number in range" do
58
+ assert @loop.in_bounds?(3, :length => 10)
59
+ end
60
+
61
+ should "not include number out of range" do
62
+ refute @loop.in_bounds?(10, :length => 8)
63
+ end
64
+
65
+ end
66
+
67
+ context "custom loop" do
68
+
69
+ setup do
70
+ @loop.range = 0..6
71
+ end
72
+
73
+ should "include number in range" do
74
+ assert @loop.in_bounds?(5)
75
+ end
76
+
77
+ should "not include number out of range" do
78
+ refute @loop.in_bounds?(7)
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+
85
+ end
86
+
87
+ end
88
+
89
+
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: basic-sequencer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Ari Russo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: midi-topaz
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.0.15
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.0.15
33
+ - !ruby/object:Gem::Dependency
34
+ name: unimidi
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 0.2.5
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 0.2.5
53
+ description: Perform a sequence of events at tempo in Ruby
54
+ email:
55
+ - ari.russo@gmail.com
56
+ executables: []
57
+ extensions: []
58
+ extra_rdoc_files: []
59
+ files:
60
+ - LICENSE
61
+ - README.md
62
+ - lib/sequencer.rb
63
+ - lib/sequencer/clock.rb
64
+ - lib/sequencer/core.rb
65
+ - lib/sequencer/event.rb
66
+ - lib/sequencer/event_trigger.rb
67
+ - lib/sequencer/loop.rb
68
+ - test/clock_test.rb
69
+ - test/core_test.rb
70
+ - test/event_trigger_test.rb
71
+ - test/helper.rb
72
+ - test/loop_test.rb
73
+ homepage: http://github.com/arirusso/sequencer
74
+ licenses:
75
+ - Apache 2.0
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 1.3.6
91
+ requirements: []
92
+ rubyforge_project: basic-sequencer
93
+ rubygems_version: 2.2.2
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: Perform a sequence of events at tempo
97
+ test_files: []