1 /*
2 This file is part of LilyPond, the GNU music typesetter.
3
4 Copyright (C) 1997--2021 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "sequential-iterator.hh"
21
22 #include "calculated-sequential-music.hh"
23 #include "international.hh"
24 #include "music.hh"
25 #include "music-sequence.hh"
26 #include "context.hh"
27 #include "warn.hh"
28
29 #include <algorithm>
30
31 void
do_quit()32 Sequential_iterator::do_quit ()
33 {
34 if (iter_)
35 iter_->quit ();
36 }
37
38 void
derived_mark() const39 Sequential_iterator::derived_mark () const
40 {
41 if (iter_)
42 scm_gc_mark (iter_->self_scm ());
43 scm_gc_mark (remaining_music_);
44 }
45
46 void
preorder_walk(const std::function<void (Music_iterator *)> & visit)47 Sequential_iterator::preorder_walk
48 (const std::function <void (Music_iterator *)> &visit)
49 {
50 Music_iterator::preorder_walk (visit);
51 if (iter_)
52 iter_->preorder_walk (visit);
53 }
54
55 void
create_children()56 Sequential_iterator::create_children ()
57 {
58 Music_iterator::create_children ();
59
60 remaining_music_ = Calculated_sequential_music::calc_elements (get_music ());
61 ahead_music_ = remaining_music_;
62 iter_start_mom_ = music_start_mom ();
63 ahead_mom_ = iter_start_mom_;
64
65 pop_element ();
66 }
67
68 void
create_contexts()69 Sequential_iterator::create_contexts ()
70 {
71 Music_iterator::create_contexts ();
72
73 if (iter_)
74 {
75 iter_->init_context (get_own_context ());
76 descend_to_child (iter_->get_context ());
77 }
78 }
79
look_ahead()80 void Sequential_iterator::look_ahead ()
81 {
82 // Move past elements that have no main duration, then move past the first
83 // one with duration.
84 while (scm_is_pair (ahead_music_))
85 {
86 auto *mus = unsmob<Music> (scm_car (ahead_music_));
87 ahead_music_ = scm_cdr (ahead_music_);
88
89 if (mus) // paranoia; other things should have complained already
90 {
91 const auto &end_mom = mus->get_length ();
92 if (end_mom.main_part_ > 0)
93 {
94 ahead_mom_.main_part_ += end_mom.main_part_;
95 break;
96 }
97 }
98 }
99
100 // The current state is similar to the initial state of sequential music
101 // before it has called the start-callback, now with fewer elements.
102 const auto &start_mom = Music_sequence::first_start (ahead_music_);
103 ahead_mom_.grace_part_ = start_mom.grace_part_;
104 // we keep the accumulated main part
105 }
106
107 // remove the next element to be processed and create an iterator for it
108 void
pop_element()109 Sequential_iterator::pop_element ()
110 {
111 iter_ = nullptr;
112
113 const auto have_ready_music = [this]
114 {
115 return !scm_is_eq (remaining_music_, ahead_music_);
116 };
117
118 if (have_ready_music () || (look_ahead (), have_ready_music ()))
119 {
120 SCM mus_scm = scm_car (remaining_music_); // pop the next element
121 remaining_music_ = scm_cdr (remaining_music_);
122
123 if (!scm_is_pair (remaining_music_)) // that was the last one
124 ahead_mom_ = Moment::infinity ();
125
126 if (auto *mus = unsmob<Music> (mus_scm))
127 {
128 iter_ = unsmob<Music_iterator> (create_child (mus));
129 scm_remember_upto_here_1 (mus_scm);
130 }
131 }
132
133 if (!iter_) // end of sequence
134 {
135 if (iter_start_mom_ != music_get_length ())
136 {
137 // Maybe a callback provided music inconsistent with the
138 // precomputed length.
139 // TODO: It might be nice to log the actual music that was
140 // iterated in a debug message.
141 warning (_ ("total length of sequential music elements "
142 "is different than anticipated"));
143 }
144 }
145 }
146
147 void
process(Moment until)148 Sequential_iterator::process (Moment until)
149 {
150 while (iter_)
151 {
152 // moments in the timeline of this sequence
153 const auto iter_zero = iter_start_mom_ - iter_->music_start_mom ();
154 const auto iter_end = iter_zero + iter_->music_get_length ();
155
156 if (iter_->ok ())
157 {
158 // When it is time to advance the main part, we try to finish all
159 // prior elements, even if it is before their time when grace notes
160 // are considered.
161 const bool fast_forward = (ahead_mom_ <= until);
162 const auto &proc_mom = fast_forward ? iter_end : until;
163 iter_->process (proc_mom - iter_zero);
164 if (iter_->ok ())
165 return;
166 }
167
168 const auto &next_mom = std::min (iter_end, ahead_mom_);
169 if ((until < next_mom) && (next_mom < Moment::infinity ()))
170 {
171 // iter_ is !ok earlier than the length of its music predicts.
172 // Mitigate by waiting until the expected time so that the next
173 // element starts in sync.
174 iter_->warning (_ ("music is shorter than anticipated"));
175 return;
176 }
177
178 iter_start_mom_ = next_mom;
179 descend_to_child (iter_->get_context ());
180 iter_->quit ();
181 iter_ = nullptr;
182
183 pop_element ();
184 if (iter_)
185 iter_->init_context (get_own_context ());
186
187 next_element (); // let subclasses do certain things
188 }
189
190 iter_start_mom_ = until;
191 }
192
193 Moment
pending_moment() const194 Sequential_iterator::pending_moment () const
195 {
196 if (!iter_)
197 {
198 // Defensive: If for any reason we haven't advanced the full length of
199 // the music, stay alive until the end to help keep things in sync.
200 // Normally, we'll skip to infinity here.
201 const auto &end = music_get_length ();
202 return (iter_start_mom_ < end) ? end : Moment::infinity ();
203 }
204
205 // moments in the timeline of this sequence
206 const auto iter_zero = iter_start_mom_ - iter_->music_start_mom ();
207 const auto iter_end = iter_zero + iter_->music_get_length ();
208 const auto iter_pend = iter_zero + iter_->pending_moment ();
209
210 // Don't overshoot either of these. The current element's ending time might
211 // fall within a span of grace notes that ahead_mom_ already looks beyond.
212 // ahead_mom_ might account for grace notes that need to borrow time from the
213 // current element.
214 const auto &next_mom = std::min (iter_end, ahead_mom_);
215 return std::min (iter_pend, next_mom);
216 }
217
218 IMPLEMENT_CTOR_CALLBACK (Sequential_iterator);
219
220 bool
run_always() const221 Sequential_iterator::run_always () const
222 {
223 return iter_ ? iter_->run_always () : false;
224 }
225