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