tef-animation 0.1.1 → 0.2.0
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.
- 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
|