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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 92421ddea4eb5a58abffa7fdb3ec987f645281d7
4
- data.tar.gz: c2fc1d82fdffc6ecc435ed47554f3dca0bb5834b
2
+ SHA256:
3
+ metadata.gz: 1db58af80b8a46f264d810c4119617e776dcb4036374041ce2051753003442d7
4
+ data.tar.gz: 823d5ada4c84f7e5e83e04943a7005516deb343a140c7e0352d3bd9570b06fbf
5
5
  SHA512:
6
- metadata.gz: df1fd4e30609e37ee34281ccad04a759af87155757af7886d163c7082caa3e9427854b09353d042342ebd2e8e5d00c4ec3b79289bb7c7ecbff21353d1abb9e10
7
- data.tar.gz: 96069806358729bff76ecebe28a925caf52d8587acc9ba05dc7950595e9ad1f3b5980b32a16ca84a074fd5f9ef896eda0c5d496061ab1c8c08f42806051cead0
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 = clean_key key
82
-
83
- @active_animations[key]&.module_id = nil;
100
+ @animation_mutex.synchronize do
101
+ internal_set_object(key, new_obj)
102
+ end
103
+ end
84
104
 
85
- if new_obj.nil?
86
- @pending_deletions[key] = true
87
- @pending_creations.delete key
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
- elsif new_obj.is_a? Animatable
90
- new_obj.module_id = key
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
- @active_animations[key] = new_obj
93
- @pending_creations[key] = new_obj.creation_string
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 (!animation.death_time.nil?) && animation.death_time < Time.now()
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
- deletions = []
143
- @pending_deletions.each do |key, val|
144
- deletions << "#{key};" unless val == :silent
145
- @active_animations.delete key
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
@@ -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, 0x10
13
- animatable_attr :iris_y, 0x11
14
- animatable_attr :top, 0x12
15
- animatable_attr :bottom, 0x13
16
- animatable_attr :eye_closure, 0x14
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
- # - :torn_down (after teardown() was called)
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
- raise 'Program had to be uninitialized!' unless @state == :uninitialized
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 = :torn_down
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 @state == :uninitialized
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
- if @state == :running
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 { |k, seq| seq.state == :torn_down }
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
 
@@ -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 = 0;
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 sequence(&block)
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
- @slope *= @sheet.tempo / (60 * (options[:top_slope] || 1))
31
+ slope *= (@sheet.tempo / (60.to_f * (options[:top_slope] || 1)))
35
32
  end
36
33
 
37
- @start_time = @sheet.start_time
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[:sheet_options], &block)
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 { |pid| Process.kill('QUIT', pid); }
74
+ @active_music.each do |pid|
75
+ self.kill pid
76
+ end
69
77
 
70
- @subprograms = nil;
71
- @notes = nil;
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(@active_music)
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
- loop do
156
- next_program = @subprograms[i]
157
- break if next_program.nil?
158
- break if collector.event_time &&
159
- (collector.event_time < next_program.parent_start_time)
160
-
161
- next_program.append_events collector
162
-
163
- if next_program.state == :torn_down
164
- @subprograms.delete_at i
165
- else
166
- i += 1
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
- next_note = @notes[i];
176
- loop do
177
- collector.add_event next_note;
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
@@ -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.1.1
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
- rubyforge_project:
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