tef-animation 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/tef/Animation/Animatable.rb +28 -0
- data/lib/tef/Animation/Animation_Handler.rb +60 -25
- data/lib/tef/Animation/Eyes.rb +24 -5
- data/lib/tef/Animation/String.rb +29 -0
- data/lib/tef/Sequencing/AudacityLabelReader.rb +52 -0
- data/lib/tef/Sequencing/BaseSequence.rb +41 -18
- data/lib/tef/Sequencing/EventCollector.rb +7 -3
- data/lib/tef/Sequencing/SequencePlayer.rb +9 -1
- data/lib/tef/Sequencing/Sheet.rb +16 -2
- data/lib/tef/Sequencing/SheetSequence.rb +68 -34
- data/lib/tef/animation.rb +3 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1db58af80b8a46f264d810c4119617e776dcb4036374041ce2051753003442d7
|
4
|
+
data.tar.gz: 823d5ada4c84f7e5e83e04943a7005516deb343a140c7e0352d3bd9570b06fbf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f95e1a8b015fe89f8df92a51b7bb04683d69fc9f0e4bd5016350abc2f208379643371e2e25111491b0520a7acbfb43de17e2392bdb712eecca2c95d628833d8a
|
7
|
+
data.tar.gz: a5dff21053dee87aae002e2213c76b831fad03b7c48ee08b72e654474696a363f52adb9609a200d12dc96ad9c0fc46c0154cf00726f57d153a1f661fea0438d0
|
@@ -149,6 +149,8 @@ module TEF
|
|
149
149
|
@animatable_colors = {}
|
150
150
|
@animatable_coordinates = {}
|
151
151
|
|
152
|
+
@animatable_pending_strings = [];
|
153
|
+
|
152
154
|
self.class.get_attr_list.each do |key, val|
|
153
155
|
@animatable_attributes[key] = Value.new(val)
|
154
156
|
end
|
@@ -206,6 +208,11 @@ module TEF
|
|
206
208
|
self.death_time = Time.at(0)
|
207
209
|
end
|
208
210
|
|
211
|
+
def is_dead?
|
212
|
+
return false if @death_time.nil?
|
213
|
+
return @death_time < Time.now();
|
214
|
+
end
|
215
|
+
|
209
216
|
# Quickly configure this object.
|
210
217
|
#
|
211
218
|
# This is a convenience function to very quickly and easily
|
@@ -225,6 +232,11 @@ module TEF
|
|
225
232
|
raise ArgumentError, 'Config must be a hash!' unless h.is_a? Hash
|
226
233
|
|
227
234
|
h.each do |key, data|
|
235
|
+
if(key == :die_after)
|
236
|
+
die_in(data.to_f);
|
237
|
+
next;
|
238
|
+
end
|
239
|
+
|
228
240
|
value = @animatable_attributes[key] ||
|
229
241
|
@animatable_colors[key] ||
|
230
242
|
@animatable_coordinates[key]
|
@@ -245,6 +257,10 @@ module TEF
|
|
245
257
|
''
|
246
258
|
end
|
247
259
|
|
260
|
+
def send_string(str)
|
261
|
+
@animatable_pending_strings << str;
|
262
|
+
end
|
263
|
+
|
248
264
|
private def all_animatable_attributes
|
249
265
|
out = @animatable_attributes.values
|
250
266
|
out += @animatable_coordinates.values.map(&:animatable_attributes)
|
@@ -337,6 +353,18 @@ module TEF
|
|
337
353
|
|
338
354
|
out_elements
|
339
355
|
end
|
356
|
+
|
357
|
+
def get_setss_strings()
|
358
|
+
return [] unless @module_id
|
359
|
+
|
360
|
+
out = @animatable_pending_strings.map do |str|
|
361
|
+
"#{@module_id} #{str}"
|
362
|
+
end
|
363
|
+
|
364
|
+
@animatable_pending_strings.clear
|
365
|
+
|
366
|
+
return out;
|
367
|
+
end
|
340
368
|
end
|
341
369
|
|
342
370
|
class Box < Animatable
|
@@ -62,6 +62,26 @@ module TEF
|
|
62
62
|
key
|
63
63
|
end
|
64
64
|
|
65
|
+
private def internal_set_object(key, new_obj)
|
66
|
+
key = clean_key key
|
67
|
+
|
68
|
+
@active_animations[key]&.module_id = nil;
|
69
|
+
|
70
|
+
if new_obj.nil?
|
71
|
+
@pending_deletions[key] = true
|
72
|
+
@pending_creations.delete key
|
73
|
+
|
74
|
+
elsif new_obj.is_a? Animatable
|
75
|
+
new_obj.module_id = key
|
76
|
+
|
77
|
+
@active_animations[key] = new_obj
|
78
|
+
@pending_creations[key] = new_obj.creation_string
|
79
|
+
@pending_deletions.delete key
|
80
|
+
else
|
81
|
+
raise ArgumentError, 'New animation object is of invalid type'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
65
85
|
# @return [Animatable] Returns the Animatable with matching key.
|
66
86
|
def [](key)
|
67
87
|
@active_animations[clean_key key]
|
@@ -77,25 +97,28 @@ module TEF
|
|
77
97
|
# either replace it with the given {Animatable}, or, if nil was
|
78
98
|
# given, will delete the animation entry.
|
79
99
|
def []=(key, new_obj)
|
80
|
-
@animation_mutex.synchronize
|
81
|
-
key
|
82
|
-
|
83
|
-
|
100
|
+
@animation_mutex.synchronize do
|
101
|
+
internal_set_object(key, new_obj)
|
102
|
+
end
|
103
|
+
end
|
84
104
|
|
85
|
-
|
86
|
-
|
87
|
-
|
105
|
+
def append_to_set(new_obj, set_no = 200)
|
106
|
+
@animation_mutex.synchronize do
|
107
|
+
start_no = { S: set_no, M: 0 };
|
88
108
|
|
89
|
-
|
90
|
-
|
109
|
+
until(@active_animations[clean_key start_no].nil? ||
|
110
|
+
@active_animations[clean_key start_no].is_dead?) do
|
111
|
+
start_no[:M] += 1
|
112
|
+
end
|
91
113
|
|
92
|
-
|
93
|
-
|
94
|
-
@pending_deletions.delete key
|
95
|
-
else
|
96
|
-
raise ArgumentError, 'New animation object is of invalid type'
|
114
|
+
if(start_no[:M] >= 255)
|
115
|
+
raise ArgumentError, 'No more space for new animations!'
|
97
116
|
end
|
98
|
-
|
117
|
+
|
118
|
+
internal_set_object(start_no, new_obj)
|
119
|
+
end
|
120
|
+
|
121
|
+
new_obj
|
99
122
|
end
|
100
123
|
|
101
124
|
# Internal function to join an Array of strings and send it onto
|
@@ -125,10 +148,11 @@ module TEF
|
|
125
148
|
# using the main synch time, rather than using relative time.
|
126
149
|
private def update_deaths
|
127
150
|
death_reconfigs = [];
|
151
|
+
deletions = []
|
128
152
|
|
129
|
-
@animation_mutex.synchronize
|
153
|
+
@animation_mutex.synchronize do
|
130
154
|
@active_animations.each do |key, animation|
|
131
|
-
if
|
155
|
+
if animation.is_dead?
|
132
156
|
@pending_deletions[key] = :silent
|
133
157
|
end
|
134
158
|
|
@@ -137,14 +161,14 @@ module TEF
|
|
137
161
|
|
138
162
|
death_reconfigs << new_death
|
139
163
|
end
|
140
|
-
}
|
141
164
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
165
|
+
@pending_deletions.each do |key, val|
|
166
|
+
deletions << "#{key};" unless val == :silent
|
167
|
+
@active_animations.delete key
|
168
|
+
end
|
169
|
+
@pending_deletions = {}
|
170
|
+
|
146
171
|
end
|
147
|
-
@pending_deletions = {}
|
148
172
|
|
149
173
|
join_and_send('DTIME', death_reconfigs)
|
150
174
|
join_and_send('DELETE', deletions)
|
@@ -222,11 +246,11 @@ module TEF
|
|
222
246
|
private def update_colors
|
223
247
|
pending_changes = []
|
224
248
|
|
225
|
-
@animation_mutex.synchronize
|
249
|
+
@animation_mutex.synchronize do
|
226
250
|
@active_animations.each do |key, anim|
|
227
251
|
pending_changes += anim.get_setc_strings
|
228
252
|
end
|
229
|
-
|
253
|
+
end
|
230
254
|
|
231
255
|
return if pending_changes.empty?
|
232
256
|
x_logd "Pending changes are #{pending_changes}"
|
@@ -234,6 +258,16 @@ module TEF
|
|
234
258
|
join_and_send 'CSET', pending_changes
|
235
259
|
end
|
236
260
|
|
261
|
+
private def update_strings
|
262
|
+
@animation_mutex.synchronize do
|
263
|
+
@active_animations.values.each do |anim|
|
264
|
+
anim.get_setss_strings().each do |str|
|
265
|
+
@furcoms.send_message 'SSET', str
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
237
271
|
# Update tick.
|
238
272
|
#
|
239
273
|
# Calling this function will send all updates and changes over the
|
@@ -252,6 +286,7 @@ module TEF
|
|
252
286
|
|
253
287
|
update_values
|
254
288
|
update_colors
|
289
|
+
update_strings
|
255
290
|
end
|
256
291
|
end
|
257
292
|
end
|
data/lib/tef/Animation/Eyes.rb
CHANGED
@@ -8,15 +8,34 @@ module TEF
|
|
8
8
|
class Eye < Animatable
|
9
9
|
animatable_color :outer_color, 0
|
10
10
|
animatable_color :inner_color, 1
|
11
|
+
animatable_color :blush, 2
|
11
12
|
|
12
|
-
animatable_attr :iris_x,
|
13
|
-
|
14
|
-
animatable_attr :
|
15
|
-
animatable_attr :
|
16
|
-
animatable_attr :
|
13
|
+
animatable_attr :iris_x, 0x0
|
14
|
+
|
15
|
+
animatable_attr :angry, 0x100
|
16
|
+
animatable_attr :happy, 0x101
|
17
|
+
animatable_attr :heart, 0x102
|
18
|
+
animatable_attr :surprised, 0x103
|
19
|
+
animatable_attr :shy, 0x104
|
17
20
|
|
18
21
|
def initialize()
|
19
22
|
super();
|
23
|
+
|
24
|
+
@last_mood = :relaxed;
|
25
|
+
@animatable_colors[:blush].configure({ target: 0xFF000000, delay_a: 1 });
|
26
|
+
end
|
27
|
+
|
28
|
+
def set_mood(mood, amount: 1)
|
29
|
+
if @last_mood != :relaxed
|
30
|
+
@animatable_attributes[@last_mood].add = 0;
|
31
|
+
end
|
32
|
+
|
33
|
+
return if mood.nil?
|
34
|
+
@last_mood = mood.to_sym;
|
35
|
+
|
36
|
+
return if @last_mood == :relaxed
|
37
|
+
|
38
|
+
self.configure({ @last_mood.to_sym => { add: amount, dampen: 0.1 }});
|
20
39
|
end
|
21
40
|
end
|
22
41
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
require_relative 'Animatable.rb'
|
3
|
+
|
4
|
+
module TEF
|
5
|
+
module Animation
|
6
|
+
class StringDisplay < Animatable
|
7
|
+
animatable_color :color, 0
|
8
|
+
|
9
|
+
animatable_attr :x, 1
|
10
|
+
animatable_attr :y, 2
|
11
|
+
animatable_attr :alignment, 3
|
12
|
+
|
13
|
+
attr_reader :string
|
14
|
+
|
15
|
+
def initialize()
|
16
|
+
super();
|
17
|
+
|
18
|
+
self.string = "";
|
19
|
+
end
|
20
|
+
|
21
|
+
def string=(new_string)
|
22
|
+
return if new_string == @string
|
23
|
+
@string = new_string
|
24
|
+
|
25
|
+
@animatable_pending_strings = [new_string]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
module TEF
|
3
|
+
module Sequencing
|
4
|
+
|
5
|
+
# Audacity label reader helper.
|
6
|
+
#
|
7
|
+
# This class is meant to read in an exported list of Labels, as
|
8
|
+
# generated by audacity. It provides proper data conversion and a few
|
9
|
+
# convenience functions.
|
10
|
+
class AudacityReader
|
11
|
+
# Initialize a new reader.
|
12
|
+
#
|
13
|
+
# This will read in the given file and process it.
|
14
|
+
# Note that since Audacity does not export the name of different
|
15
|
+
# label tracks, it simply exports all labels, each label track should
|
16
|
+
# begin with a label called "TRACK: Name".
|
17
|
+
# This serves as a means to separate label tracks.
|
18
|
+
#
|
19
|
+
# @param [String] file The file to read.
|
20
|
+
# Should be a tab-separated list of start and end time and label name.
|
21
|
+
def initialize(file)
|
22
|
+
@tracks = Hash.new({});
|
23
|
+
|
24
|
+
File.open(file, 'r') do |f|
|
25
|
+
current_track = 'default';
|
26
|
+
@tracks['default'] = [];
|
27
|
+
|
28
|
+
f.each do |line|
|
29
|
+
m = /^(?<start>[\.\d]+)\s+(?<stop>[\d\.]+)\s+(?<text>\S.+)/.match line
|
30
|
+
next unless m;
|
31
|
+
|
32
|
+
if(m[:text] =~ /TRACK:\s*(\S.+)/)
|
33
|
+
current_track = $1;
|
34
|
+
@tracks[current_track] = [];
|
35
|
+
else
|
36
|
+
@tracks[current_track] << { start: m[:start].to_f, stop: m[:stop].to_f, text: m[:text] }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [Array<{start: Numeric, stop: Numeric, text: String}] The
|
43
|
+
# corresponding list of labels for the given track, or an empty
|
44
|
+
# array if the track did not exist.
|
45
|
+
def [](name)
|
46
|
+
return [] if @tracks[name].nil?
|
47
|
+
|
48
|
+
return @tracks[name]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -33,7 +33,7 @@ module TEF
|
|
33
33
|
# purposes. Can be:
|
34
34
|
# - :uninitialized (right after construction)
|
35
35
|
# - :running (after having called setup())
|
36
|
-
# - :
|
36
|
+
# - :idle (after teardown() was called)
|
37
37
|
attr_reader :state
|
38
38
|
|
39
39
|
# Initialize a BaseSequence.
|
@@ -50,7 +50,10 @@ module TEF
|
|
50
50
|
@end_time ||= options[:end_time];
|
51
51
|
|
52
52
|
@offset = offset;
|
53
|
-
@slope = slope;
|
53
|
+
@slope = slope.round(6);
|
54
|
+
# Explanation: For some bizarre reason, rounding is necessary
|
55
|
+
# to avoid a glitch in the timing math. 6 digits of precision
|
56
|
+
# should be precise enough anyways.
|
54
57
|
|
55
58
|
@state = :uninitialized
|
56
59
|
|
@@ -60,17 +63,29 @@ module TEF
|
|
60
63
|
def parent_start_time
|
61
64
|
@offset + @start_time / @slope
|
62
65
|
end
|
66
|
+
def parent_end_time
|
67
|
+
return nil if @end_time.nil?
|
68
|
+
|
69
|
+
@offset + @end_time / @slope
|
70
|
+
end
|
71
|
+
|
72
|
+
def parent_end_time=(new_time)
|
73
|
+
if(new_time.nil?)
|
74
|
+
@end_time = nil;
|
75
|
+
return;
|
76
|
+
end
|
77
|
+
|
78
|
+
@end_time = (new_time - @offset) * @slope;
|
79
|
+
end
|
63
80
|
|
64
81
|
def setup()
|
65
|
-
|
82
|
+
return unless @state == :idle
|
66
83
|
@state = :running
|
67
84
|
end
|
68
85
|
|
69
86
|
def teardown()
|
70
87
|
return unless @state == :running
|
71
|
-
@state = :
|
72
|
-
|
73
|
-
@opts_hash = nil;
|
88
|
+
@state = :idle
|
74
89
|
end
|
75
90
|
|
76
91
|
# Look for the next possible event that this sequence wants to
|
@@ -83,32 +98,40 @@ module TEF
|
|
83
98
|
# @note When using BaseSequence as base class, the user
|
84
99
|
# shall overload {#overload_append_events} rather than this function!
|
85
100
|
def append_events(collector)
|
101
|
+
return if @state == :uninitialized
|
102
|
+
|
86
103
|
local_collector = collector.offset_collector(@offset, @slope);
|
87
104
|
|
105
|
+
# Return if the collector has events before our start time
|
88
106
|
return if local_collector.has_events? &&
|
89
107
|
local_collector.event_time < @start_time
|
90
|
-
return if @state == :torn_down
|
91
108
|
|
92
|
-
if
|
109
|
+
if !@end_time.nil?
|
110
|
+
if @state == :running
|
111
|
+
local_collector.add_event({
|
112
|
+
time: [@end_time, local_collector.start_time + 0.01].max,
|
113
|
+
code: proc { self.teardown() }
|
114
|
+
})
|
115
|
+
end
|
116
|
+
|
117
|
+
return if local_collector.start_time >= @end_time
|
118
|
+
end
|
119
|
+
|
120
|
+
if @state == :idle
|
93
121
|
local_collector.add_event({
|
94
122
|
time: [@start_time, local_collector.start_time + 0.01].max,
|
95
123
|
code: proc { self.setup() }
|
96
124
|
});
|
97
125
|
end
|
98
126
|
|
99
|
-
|
100
|
-
overload_append_events(local_collector)
|
101
|
-
end
|
102
|
-
|
103
|
-
if !@end_time.nil?
|
104
|
-
local_collector.add_event({
|
105
|
-
time: @end_time,
|
106
|
-
code: proc { self.teardown() }
|
107
|
-
})
|
108
|
-
end
|
127
|
+
overload_append_events(local_collector)
|
109
128
|
end
|
110
129
|
|
111
130
|
def overload_append_events(_collector) end
|
131
|
+
|
132
|
+
def destroy!()
|
133
|
+
teardown() if @state == :running
|
134
|
+
end
|
112
135
|
end
|
113
136
|
end
|
114
137
|
end
|
@@ -41,8 +41,9 @@ module TEF
|
|
41
41
|
# This should only be done via {EventCollector#offset_collector}!
|
42
42
|
def initialize(parent, total_offset, total_slope)
|
43
43
|
@parent = parent
|
44
|
+
|
44
45
|
@total_offset = total_offset
|
45
|
-
@total_slope = total_slope
|
46
|
+
@total_slope = total_slope.to_f
|
46
47
|
end
|
47
48
|
|
48
49
|
# @param [Time, nil] global_time Time to convert
|
@@ -50,7 +51,7 @@ module TEF
|
|
50
51
|
def convert_to_local(global_time)
|
51
52
|
return nil if global_time.nil?
|
52
53
|
|
53
|
-
(global_time - @total_offset) * @total_slope
|
54
|
+
((global_time - @total_offset) * @total_slope).round(3)
|
54
55
|
end
|
55
56
|
|
56
57
|
# @param [Numeric, nil] local_time Time (abstract) to convert back
|
@@ -59,7 +60,7 @@ module TEF
|
|
59
60
|
def convert_to_global(local_time)
|
60
61
|
return nil if local_time.nil?
|
61
62
|
|
62
|
-
@total_offset + (local_time.to_f / @total_slope)
|
63
|
+
@total_offset + (local_time.to_f.round(3) / @total_slope)
|
63
64
|
end
|
64
65
|
|
65
66
|
# (see EventCollector#start_time)
|
@@ -133,8 +134,11 @@ module TEF
|
|
133
134
|
# and set the event list to [event], else append the event
|
134
135
|
# to the event list.
|
135
136
|
def add_event(event)
|
137
|
+
event = event.clone
|
138
|
+
|
136
139
|
return if event[:time] <= @start_time
|
137
140
|
return if (!@event_time.nil?) && (event[:time] > @event_time)
|
141
|
+
return unless event[:code].is_a? Proc
|
138
142
|
|
139
143
|
if (!@event_time.nil?) && (event[:time] == @event_time)
|
140
144
|
@current_events << event
|
@@ -108,7 +108,15 @@ module TEF
|
|
108
108
|
loop do
|
109
109
|
@sequenceMutex.synchronize do
|
110
110
|
@retryCollecting = false
|
111
|
-
@activeSequences.delete_if
|
111
|
+
@activeSequences.delete_if do |k, seq|
|
112
|
+
if(seq.parent_end_time <= Time.now())
|
113
|
+
seq.destroy!()
|
114
|
+
true
|
115
|
+
else
|
116
|
+
false
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
112
120
|
@activeSequences.each { |k, seq| seq.append_events @collector }
|
113
121
|
end
|
114
122
|
|
data/lib/tef/Sequencing/Sheet.rb
CHANGED
@@ -25,6 +25,11 @@ module TEF
|
|
25
25
|
# execution!
|
26
26
|
attr_accessor :end_time
|
27
27
|
|
28
|
+
# If set non-nil, determines the timespan after which a given
|
29
|
+
# sequence will repeat itself. Does not influence start_time and
|
30
|
+
# end_time.
|
31
|
+
attr_accessor :repeat_time
|
32
|
+
|
28
33
|
# @return [Numeric, nil] Tempo of the sheet. Defines the execution
|
29
34
|
# speed in BPM. If left nil, the execution speed of the parent
|
30
35
|
# sheet is used.
|
@@ -38,6 +43,7 @@ module TEF
|
|
38
43
|
# @see SheetSequence#at
|
39
44
|
# @see SheetSequence#after
|
40
45
|
# @see SheetSequence#play
|
46
|
+
attr_reader :fill_block
|
41
47
|
attr_reader :setup_block
|
42
48
|
attr_reader :teardown_block
|
43
49
|
|
@@ -47,13 +53,17 @@ module TEF
|
|
47
53
|
# {#start_time}, {#end_time} and by supplying at least a
|
48
54
|
# {#sequence}
|
49
55
|
def initialize()
|
50
|
-
@start_time =
|
56
|
+
@start_time = nil;
|
51
57
|
@end_time = nil;
|
58
|
+
@repeat_time = nil;
|
52
59
|
|
53
60
|
@tempo = nil
|
54
61
|
|
62
|
+
@fill_block = nil;
|
55
63
|
@setup_block = nil;
|
56
64
|
@teardown_block = nil;
|
65
|
+
|
66
|
+
yield(self) if(block_given?)
|
57
67
|
end
|
58
68
|
|
59
69
|
# Configure a block to call when setting up the {SheetSequence}.
|
@@ -64,10 +74,14 @@ module TEF
|
|
64
74
|
# @see SheetSequence#at
|
65
75
|
# @see SheetSequence#after
|
66
76
|
# @see SheetSequence#play
|
67
|
-
def
|
77
|
+
def setup(&block)
|
68
78
|
@setup_block = block;
|
69
79
|
end
|
70
80
|
|
81
|
+
def notes(&block)
|
82
|
+
@fill_block = block;
|
83
|
+
end
|
84
|
+
|
71
85
|
# Configure the block to call when the {SheetSequence} is about to
|
72
86
|
# be torn down. Use this to stop or delete any resources allocated
|
73
87
|
# to the sheet.
|
@@ -13,7 +13,6 @@ module TEF
|
|
13
13
|
# Think of the {Sheet} as being the script for a play or movie, while
|
14
14
|
# the {SheetSequence} has the job of actually performing everything.
|
15
15
|
class SheetSequence < BaseSequence
|
16
|
-
|
17
16
|
# Initialize a SheetSequence.
|
18
17
|
#
|
19
18
|
# This is mostly done via {Player#[]=} by passing a {Sheet}.
|
@@ -24,36 +23,43 @@ module TEF
|
|
24
23
|
# The user may also call {#at} and {#after} manually to add additional
|
25
24
|
# events.
|
26
25
|
def initialize(offset, slope, **options)
|
27
|
-
super(offset, slope, **options);
|
28
|
-
|
29
26
|
raise ArgumentError, 'Sheet must be supplied!' unless options[:sheet]
|
30
27
|
|
31
28
|
@sheet = options[:sheet]
|
32
29
|
|
33
30
|
if @sheet.tempo
|
34
|
-
|
31
|
+
slope *= (@sheet.tempo / (60.to_f * (options[:top_slope] || 1)))
|
35
32
|
end
|
36
33
|
|
37
|
-
|
38
|
-
@end_time = @sheet.end_time
|
34
|
+
super(offset, slope, **options);
|
39
35
|
|
40
36
|
@notes = []
|
41
37
|
@latest_note_time = nil;
|
42
38
|
|
43
39
|
@subprograms = []
|
44
|
-
|
45
40
|
@active_music = []
|
41
|
+
|
42
|
+
@start_time = @sheet.start_time
|
43
|
+
@end_time = @sheet.end_time
|
44
|
+
|
45
|
+
if block = @sheet.fill_block
|
46
|
+
instance_exec(@opts_hash, &block)
|
47
|
+
|
48
|
+
@start_time ||= @notes[0]&.dig(:time) || 0;
|
49
|
+
@end_time ||= @notes[-1]&.dig(:time) || 0;
|
50
|
+
|
51
|
+
@state = :idle;
|
52
|
+
end
|
46
53
|
end
|
47
54
|
|
48
55
|
def setup()
|
56
|
+
return unless @state == :idle
|
57
|
+
|
49
58
|
super();
|
50
59
|
|
51
60
|
if block = @sheet.setup_block
|
52
|
-
instance_exec(@opts_hash
|
61
|
+
instance_exec(@opts_hash, &block)
|
53
62
|
end
|
54
|
-
|
55
|
-
return unless @end_time.nil?
|
56
|
-
@end_time = (@notes[-1]&.dig(:time) || 0) + 0.01
|
57
63
|
end
|
58
64
|
|
59
65
|
def teardown()
|
@@ -65,12 +71,18 @@ module TEF
|
|
65
71
|
|
66
72
|
@subprograms.each(&:teardown)
|
67
73
|
|
68
|
-
@active_music.each
|
74
|
+
@active_music.each do |pid|
|
75
|
+
self.kill pid
|
76
|
+
end
|
69
77
|
|
70
|
-
|
71
|
-
|
78
|
+
super();
|
79
|
+
end
|
72
80
|
|
81
|
+
def destroy!()
|
73
82
|
super();
|
83
|
+
|
84
|
+
@notes = nil;
|
85
|
+
@subprograms.each(&:destroy!)
|
74
86
|
end
|
75
87
|
|
76
88
|
# Insert an event or subsheet into the event list.
|
@@ -91,6 +103,14 @@ module TEF
|
|
91
103
|
# the {Sheet#teardown} block. Sub-Sequences as well as notes
|
92
104
|
# played by {#play} are automatically torn down.
|
93
105
|
def at(time, **options, &block)
|
106
|
+
time = time.to_f
|
107
|
+
|
108
|
+
if repeat_time = @sheet.repeat_time
|
109
|
+
time = time % repeat_time
|
110
|
+
end
|
111
|
+
|
112
|
+
time = time.round(3);
|
113
|
+
|
94
114
|
@latest_note_time = time;
|
95
115
|
|
96
116
|
options[:sequence] = SheetSequence if options[:sheet]
|
@@ -101,6 +121,10 @@ module TEF
|
|
101
121
|
|
102
122
|
prog = prog.new(time, options[:slope], **options)
|
103
123
|
|
124
|
+
if e_time = options[:end_time]
|
125
|
+
prog.parent_end_time = e_time;
|
126
|
+
end
|
127
|
+
|
104
128
|
i = @subprograms.bsearch_index { |s| s.parent_start_time > prog.parent_start_time }
|
105
129
|
@subprograms.insert((i || -1), prog);
|
106
130
|
|
@@ -138,7 +162,7 @@ module TEF
|
|
138
162
|
|
139
163
|
Thread.new do
|
140
164
|
@active_music << play_pid
|
141
|
-
Process.wait(
|
165
|
+
Process.wait(play_pid)
|
142
166
|
@active_music.delete play_pid
|
143
167
|
end
|
144
168
|
|
@@ -148,22 +172,36 @@ module TEF
|
|
148
172
|
# Shorthand to kill
|
149
173
|
def kill(pid)
|
150
174
|
Process.kill('QUIT', pid);
|
175
|
+
rescue Errno::ESRCH
|
176
|
+
return false
|
151
177
|
end
|
152
178
|
|
153
179
|
private def overload_append_events(collector)
|
154
180
|
i = 0
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
181
|
+
|
182
|
+
if(@sheet.repeat_time)
|
183
|
+
repeat_cycle = (collector.start_time / @sheet.repeat_time).floor
|
184
|
+
repeat_shift_time = repeat_cycle * @sheet.repeat_time
|
185
|
+
|
186
|
+
# This will wrap around and thusly implement the repeating nature
|
187
|
+
# of this sequence
|
188
|
+
collector = collector.offset_collector(repeat_shift_time, 1);
|
189
|
+
end
|
190
|
+
|
191
|
+
@subprograms.each do |program|
|
192
|
+
if(collector.event_time &&
|
193
|
+
(collector.event_time < program.parent_start_time))
|
194
|
+
next
|
195
|
+
end
|
196
|
+
|
197
|
+
program.append_events collector
|
198
|
+
end
|
199
|
+
|
200
|
+
return if @notes.empty?
|
201
|
+
|
202
|
+
if(@sheet.repeat_time)
|
203
|
+
if(collector.start_time >= @notes[-1][:time])
|
204
|
+
collector = collector.offset_collector(@sheet.repeat_time, 1);
|
167
205
|
end
|
168
206
|
end
|
169
207
|
|
@@ -172,13 +210,9 @@ module TEF
|
|
172
210
|
|
173
211
|
note_time = @notes[i][:time]
|
174
212
|
|
175
|
-
|
176
|
-
|
177
|
-
collector.add_event
|
178
|
-
|
179
|
-
next_note = @notes[i += 1]
|
180
|
-
break unless next_note
|
181
|
-
break if next_note[:time] != note_time
|
213
|
+
until (note = @notes[i])&.dig(:time) != note_time
|
214
|
+
i += 1;
|
215
|
+
collector.add_event note
|
182
216
|
end
|
183
217
|
end
|
184
218
|
end
|
data/lib/tef/animation.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
|
2
2
|
require_relative 'Animation/Animation_Handler.rb'
|
3
3
|
require_relative 'Animation/Eyes.rb'
|
4
|
+
require_relative 'Animation/String.rb'
|
4
5
|
|
5
6
|
require_relative 'ParameterStack/Stack.rb'
|
6
7
|
|
@@ -9,3 +10,5 @@ require_relative 'ProgramSelection/SequenceCollection.rb'
|
|
9
10
|
require_relative 'ProgramSelection/SoundCollection.rb'
|
10
11
|
|
11
12
|
require_relative 'Sequencing/SequencePlayer.rb'
|
13
|
+
|
14
|
+
require_relative 'Sequencing/AudacityLabelReader.rb'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tef-animation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- TheSystem
|
@@ -65,6 +65,7 @@ files:
|
|
65
65
|
- lib/tef/Animation/Color.rb
|
66
66
|
- lib/tef/Animation/Coordinate.rb
|
67
67
|
- lib/tef/Animation/Eyes.rb
|
68
|
+
- lib/tef/Animation/String.rb
|
68
69
|
- lib/tef/Animation/Value.rb
|
69
70
|
- lib/tef/ParameterStack/Override.rb
|
70
71
|
- lib/tef/ParameterStack/Stack.rb
|
@@ -72,6 +73,7 @@ files:
|
|
72
73
|
- lib/tef/ProgramSelection/ProgramSelector.rb
|
73
74
|
- lib/tef/ProgramSelection/SequenceCollection.rb
|
74
75
|
- lib/tef/ProgramSelection/SoundCollection.rb
|
76
|
+
- lib/tef/Sequencing/AudacityLabelReader.rb
|
75
77
|
- lib/tef/Sequencing/BaseSequence.rb
|
76
78
|
- lib/tef/Sequencing/EventCollector.rb
|
77
79
|
- lib/tef/Sequencing/SequencePlayer.rb
|
@@ -97,8 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
97
99
|
- !ruby/object:Gem::Version
|
98
100
|
version: '0'
|
99
101
|
requirements: []
|
100
|
-
|
101
|
-
rubygems_version: 2.5.2.1
|
102
|
+
rubygems_version: 3.0.6
|
102
103
|
signing_key:
|
103
104
|
specification_version: 4
|
104
105
|
summary: TEF Animation and Sequencing code
|