midi-topaz 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/topaz/external_midi_tempo.rb +9 -15
- data/lib/topaz/internal_tempo.rb +19 -13
- data/lib/topaz/midi_sync_output.rb +3 -3
- data/lib/topaz/tempo.rb +41 -19
- data/lib/topaz/tempo_source.rb +20 -0
- data/lib/topaz.rb +2 -1
- metadata +3 -2
@@ -3,12 +3,13 @@ module Topaz
|
|
3
3
|
|
4
4
|
# trigger an event based on received midi clock messages
|
5
5
|
class ExternalMIDITempo
|
6
|
+
|
7
|
+
include TempoSource
|
6
8
|
|
7
|
-
attr_accessor :action
|
8
9
|
attr_reader :clock
|
9
10
|
|
10
|
-
def initialize(input, options = {})
|
11
|
-
@
|
11
|
+
def initialize(actions, input, options = {})
|
12
|
+
@actions = actions
|
12
13
|
self.interval = options[:interval] || 4
|
13
14
|
@tempo_calculator = TempoCalculator.new
|
14
15
|
@clock = MIDIEye::Listener.new(input)
|
@@ -21,14 +22,12 @@ module Topaz
|
|
21
22
|
@tempo_calculator.find_tempo
|
22
23
|
end
|
23
24
|
|
24
|
-
def start(*a)
|
25
|
-
@action[:on_start].call unless @action[:on_start].nil?
|
25
|
+
def start(*a)
|
26
26
|
@clock.start(*a)
|
27
27
|
self
|
28
28
|
end
|
29
29
|
|
30
30
|
def stop(*a)
|
31
|
-
@action[:on_stop].call unless @action[:on_stop].nil?
|
32
31
|
@clock.start(*a)
|
33
32
|
self
|
34
33
|
end
|
@@ -61,17 +60,12 @@ module Topaz
|
|
61
60
|
|
62
61
|
def initialize_clock
|
63
62
|
@counter = 0
|
64
|
-
@clock.listen_for(:name => "Clock") do |msg|
|
65
|
-
if
|
66
|
-
|
67
|
-
return
|
68
|
-
end
|
69
|
-
@action[:destinations].each do |output|
|
70
|
-
output.on_tick
|
71
|
-
end
|
63
|
+
@clock.listen_for(:name => "Clock") do |msg|
|
64
|
+
(stop and return) if stop?
|
65
|
+
do_midi_clock
|
72
66
|
@tempo_calculator.timestamps << msg[:timestamp]
|
73
67
|
if @counter.eql?(@per_tick)
|
74
|
-
|
68
|
+
do_tick
|
75
69
|
@counter = 0
|
76
70
|
else
|
77
71
|
@counter += 1
|
data/lib/topaz/internal_tempo.rb
CHANGED
@@ -3,12 +3,13 @@ module Topaz
|
|
3
3
|
|
4
4
|
class InternalTempo < Gamelan::Timer
|
5
5
|
|
6
|
+
include TempoSource
|
7
|
+
|
6
8
|
attr_accessor :action
|
7
9
|
|
8
|
-
def initialize(tempo, options = {})
|
9
|
-
@
|
10
|
+
def initialize(actions, tempo, options = {})
|
11
|
+
@actions = actions
|
10
12
|
self.interval = options[:interval] || 4
|
11
|
-
@destinations = options[:destinations]
|
12
13
|
@last = 0
|
13
14
|
@last_sync = 0
|
14
15
|
super({:tempo => tempo})
|
@@ -17,7 +18,6 @@ module Topaz
|
|
17
18
|
# start the internal timer
|
18
19
|
# pass :background => true to keep the timer in a background thread
|
19
20
|
def start(options = {})
|
20
|
-
@action[:on_start].call unless @action[:on_start].nil?
|
21
21
|
run
|
22
22
|
join unless options[:background]
|
23
23
|
self
|
@@ -34,7 +34,6 @@ module Topaz
|
|
34
34
|
|
35
35
|
# stop the timer
|
36
36
|
def stop(*a)
|
37
|
-
@action[:on_stop].call unless @action[:on_stop].nil?
|
38
37
|
super()
|
39
38
|
self
|
40
39
|
end
|
@@ -49,21 +48,28 @@ module Topaz
|
|
49
48
|
# Run all ready tasks.
|
50
49
|
def dispatch
|
51
50
|
# stuff to do on every tick
|
52
|
-
|
51
|
+
if time_for_midi_clock?
|
53
52
|
# look for stop
|
54
|
-
if
|
55
|
-
|
56
|
-
return
|
57
|
-
end
|
58
|
-
@action[:destinations].each { |dest| dest.on_tick }
|
53
|
+
(stop and return) if stop?
|
54
|
+
do_midi_clock
|
59
55
|
@last_sync = (@phase * 24).to_i
|
60
56
|
end
|
61
57
|
# stuff to do on @interval
|
62
|
-
|
63
|
-
|
58
|
+
if time_for_tick?
|
59
|
+
do_tick
|
64
60
|
@last = (@phase * @interval).to_i
|
65
61
|
end
|
66
62
|
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def time_for_midi_clock?
|
67
|
+
!@last_sync.eql?((@phase * 24).to_i)
|
68
|
+
end
|
69
|
+
|
70
|
+
def time_for_tick?
|
71
|
+
!@last.eql?((@phase * @interval).to_i)
|
72
|
+
end
|
67
73
|
|
68
74
|
end
|
69
75
|
|
@@ -9,17 +9,17 @@ module Topaz
|
|
9
9
|
end
|
10
10
|
|
11
11
|
# send a start message
|
12
|
-
def
|
12
|
+
def start(*a)
|
13
13
|
@output.puts(MIDIMessage::SystemRealtime["Start"].new.to_a)
|
14
14
|
end
|
15
15
|
|
16
16
|
# send a stop message
|
17
|
-
def
|
17
|
+
def stop(*a)
|
18
18
|
@output.puts(MIDIMessage::SystemRealtime["Stop"].new.to_a)
|
19
19
|
end
|
20
20
|
|
21
21
|
# send a clock message
|
22
|
-
def
|
22
|
+
def midi_clock(*a)
|
23
23
|
@output.puts(MIDIMessage::SystemRealtime["Clock"].new.to_a)
|
24
24
|
end
|
25
25
|
|
data/lib/topaz/tempo.rb
CHANGED
@@ -6,27 +6,30 @@ module Topaz
|
|
6
6
|
|
7
7
|
extend Forwardable
|
8
8
|
|
9
|
-
attr_reader :source
|
9
|
+
attr_reader :source, :destinations
|
10
10
|
|
11
11
|
def_delegators :source, :tempo, :interval, :interval=, :join
|
12
12
|
|
13
13
|
def initialize(*args, &event)
|
14
|
-
@destinations = []
|
14
|
+
@destinations = []
|
15
|
+
@actions = {
|
16
|
+
:start => nil,
|
17
|
+
:stop => nil,
|
18
|
+
:tick => nil,
|
19
|
+
:midi_clock => Proc.new { @destinations.each { |d| d.send(:midi_clock) if d.respond_to?(:midi_clock) } },
|
20
|
+
:stop_when => nil
|
21
|
+
}
|
22
|
+
|
23
|
+
on_tick(&event)
|
15
24
|
|
16
25
|
if args.first.kind_of?(Numeric)
|
17
|
-
@source = InternalTempo.new(args.shift)
|
26
|
+
@source = InternalTempo.new(@actions, args.shift)
|
18
27
|
end
|
19
28
|
options = args.first
|
20
29
|
|
21
30
|
initialize_midi_io(options)
|
22
31
|
raise "You must specify an internal tempo rate or an external tempo source" if @source.nil?
|
23
|
-
|
24
|
-
:on_start => nil,
|
25
|
-
:on_stop => nil,
|
26
|
-
:on_tick => event,
|
27
|
-
:destinations => @destinations,
|
28
|
-
:stop_when => nil
|
29
|
-
}
|
32
|
+
|
30
33
|
@source.interval = options[:interval] unless options.nil? || options[:interval].nil?
|
31
34
|
end
|
32
35
|
|
@@ -45,22 +48,26 @@ module Topaz
|
|
45
48
|
|
46
49
|
# pass in a callback that is called when start is called
|
47
50
|
def on_start(&block)
|
48
|
-
@
|
51
|
+
@actions[:start] = block
|
49
52
|
end
|
50
53
|
|
51
54
|
# pass in a callback that is called when stop is called
|
52
55
|
def on_stop(&block)
|
53
|
-
@
|
56
|
+
@actions[:stop] = block
|
54
57
|
end
|
55
58
|
|
56
59
|
# pass in a callback which will
|
57
60
|
def stop_when(&block)
|
58
|
-
@
|
61
|
+
@actions[:stop_when] = block
|
59
62
|
end
|
60
63
|
|
61
64
|
# pass in a callback which will be fired on each tick
|
62
65
|
def on_tick(&block)
|
63
|
-
|
66
|
+
proc = Proc.new do |dests|
|
67
|
+
@destinations.each { |d| d.send(:tick) if d.respond_to?(:tick) }
|
68
|
+
yield
|
69
|
+
end
|
70
|
+
@actions[:tick] = proc
|
64
71
|
end
|
65
72
|
|
66
73
|
# this will start the generator
|
@@ -70,14 +77,16 @@ module Topaz
|
|
70
77
|
#
|
71
78
|
def start(options = {})
|
72
79
|
@start_time = Time.now
|
73
|
-
@destinations.each { |dest| dest.
|
74
|
-
@source.start(options)
|
80
|
+
@destinations.each { |dest| dest.start(:parent => self) }
|
81
|
+
@source.start(options) if options[:parent].nil?
|
82
|
+
@actions[:start].call unless @actions[:start].nil?
|
75
83
|
end
|
76
84
|
|
77
85
|
# this will stop tempo
|
78
86
|
def stop(options = {})
|
79
|
-
@destinations.each { |dest| dest.
|
80
|
-
@source.stop(options)
|
87
|
+
@destinations.each { |dest| dest.stop(:parent => self) }
|
88
|
+
@source.stop(options) if options[:parent].nil?
|
89
|
+
@actions[:stop].call unless @actions[:stop].nil?
|
81
90
|
@start_time = nil
|
82
91
|
end
|
83
92
|
|
@@ -87,6 +96,19 @@ module Topaz
|
|
87
96
|
end
|
88
97
|
alias_method :time_since_start, :time
|
89
98
|
|
99
|
+
# add a destination
|
100
|
+
# accepts MIDISyncOutput or another Tempo object
|
101
|
+
def add_destination(tempo)
|
102
|
+
@destinations << tempo
|
103
|
+
end
|
104
|
+
alias_method :<<, :add_destination
|
105
|
+
|
106
|
+
protected
|
107
|
+
|
108
|
+
def tick
|
109
|
+
@actions[:tick].call
|
110
|
+
end
|
111
|
+
|
90
112
|
private
|
91
113
|
|
92
114
|
def initialize_midi_io(args)
|
@@ -95,7 +117,7 @@ module Topaz
|
|
95
117
|
if ports.kind_of?(Array)
|
96
118
|
ports.each { |port| initialize_midi_io(port) }
|
97
119
|
elsif ports.type.eql?(:input) && @source.nil?
|
98
|
-
@source = ExternalMIDITempo.new(ports)
|
120
|
+
@source = ExternalMIDITempo.new(@actions, ports)
|
99
121
|
elsif ports.type.eql?(:output)
|
100
122
|
@destinations << MIDISyncOutput.new(ports)
|
101
123
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
module Topaz
|
3
|
+
|
4
|
+
module TempoSource
|
5
|
+
|
6
|
+
def do_midi_clock
|
7
|
+
@actions[:midi_clock].call
|
8
|
+
end
|
9
|
+
|
10
|
+
def do_tick
|
11
|
+
@actions[:tick].call
|
12
|
+
end
|
13
|
+
|
14
|
+
def stop?
|
15
|
+
!@actions[:stop_when].nil? && @actions[:stop_when].call
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
data/lib/topaz.rb
CHANGED
@@ -9,6 +9,7 @@ require 'gamelan'
|
|
9
9
|
require 'midi-eye'
|
10
10
|
require 'midi-message'
|
11
11
|
|
12
|
+
require 'topaz/tempo_source'
|
12
13
|
require 'topaz/external_midi_tempo'
|
13
14
|
require 'topaz/internal_tempo'
|
14
15
|
require 'topaz/midi_sync_output'
|
@@ -17,6 +18,6 @@ require 'topaz/tempo'
|
|
17
18
|
|
18
19
|
module Topaz
|
19
20
|
|
20
|
-
VERSION = "0.0.
|
21
|
+
VERSION = "0.0.5"
|
21
22
|
|
22
23
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: midi-topaz
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.5
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Ari Russo
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-06-
|
13
|
+
date: 2011-06-25 00:00:00 -04:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -71,6 +71,7 @@ files:
|
|
71
71
|
- lib/topaz/midi_sync_output.rb
|
72
72
|
- lib/topaz/internal_tempo.rb
|
73
73
|
- lib/topaz/tempo_calculator.rb
|
74
|
+
- lib/topaz/tempo_source.rb
|
74
75
|
- lib/topaz/external_midi_tempo.rb
|
75
76
|
- lib/topaz/tempo.rb
|
76
77
|
- test/helper.rb
|