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 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