midi-topaz 0.0.4 → 0.0.5
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.
- 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
|