]> git.donarmstrong.com Git - lilypond.git/blob - lily/sequential-iterator.cc
Release: bump Welcome versions.
[lilypond.git] / lily / sequential-iterator.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2015 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 #include "music.hh"
22 #include "translator-group.hh"
23 #include "context.hh"
24 #include "grace-fixup.hh"
25
26 /*
27   TODO: handling of grace notes is exquisite pain.  This handling
28   should be formally specified and then the implementation verified.
29 */
30
31 /*
32   Invariant for the data structure.
33
34
35   if (scm_is_pair (cursor_))
36   iter_->music_ == unsmob<Music> (scm_car (cursor_))
37   else
38   iter_ == 0;
39
40   The length of musiclist from start to up to cursor_ (cursor_ not
41   including), is summed
42
43   here_mom_  = sum (length (musiclist [start ... cursor>))  %)
44 */
45 Sequential_iterator::Sequential_iterator ()
46 {
47   here_mom_ = Moment (0);
48   cursor_ = SCM_EOL;
49   grace_fixups_ = 0;
50   iter_ = 0;
51 }
52
53 SCM
54 Sequential_iterator::get_music_list () const
55 {
56   Music *m = get_music ();
57   SCM proc = m->get_property ("elements-callback");
58   if (ly_is_procedure (proc))
59     return scm_call_1 (proc, m->self_scm ());
60   else
61     return SCM_EOL;
62 }
63
64 void
65 Sequential_iterator::do_quit ()
66 {
67   if (iter_)
68     iter_->quit ();
69 }
70
71 void
72 Sequential_iterator::derived_mark () const
73 {
74   if (iter_)
75     scm_gc_mark (iter_->self_scm ());
76   scm_gc_mark (cursor_);
77 }
78
79 void
80 Sequential_iterator::derived_substitute (Context *f, Context *t)
81 {
82   if (iter_)
83     iter_->substitute_outlet (f, t);
84 }
85
86 /*
87   TODO: this should be made lazily.
88 */
89 Grace_fixup *
90 create_grace_fixup_list (SCM cursor)
91 {
92   Moment here;
93   Moment last (-1);
94   Grace_fixup *head = 0;
95   Grace_fixup **tail = &head;
96
97   for (; scm_is_pair (cursor); cursor = scm_cdr (cursor))
98     {
99       Music *mus = unsmob<Music> (scm_car (cursor));
100       Moment s = mus->start_mom ();
101       Moment l = mus->get_length () - s;
102
103       if (s.grace_part_)
104         {
105           if (last != Moment (-1))
106             {
107               Grace_fixup *p = new Grace_fixup;
108               p->start_ = last;
109               p->length_ = here - last;
110               p->grace_start_ = s.grace_part_;
111               p->next_ = 0;
112               *tail = p;
113               tail = &(*tail)->next_;
114             }
115
116           here.grace_part_ = s.grace_part_;
117         }
118
119       if (l.to_bool ())
120         {
121           last = here;
122           here += l;
123         }
124     }
125
126   return head;
127 }
128
129 void
130 Sequential_iterator::construct_children ()
131 {
132   cursor_ = get_music_list ();
133
134   iter_ = 0;
135   if (scm_is_pair (cursor_))
136     {
137       Music *m = unsmob<Music> (scm_car (cursor_));
138       iter_ = unsmob<Music_iterator> (get_iterator (m));
139     }
140
141   while (iter_ && !iter_->ok ())
142     next_element (true);
143
144   last_mom_ = Moment (-1);
145   here_mom_ = get_music ()->start_mom ();
146   grace_fixups_ = create_grace_fixup_list (cursor_);
147
148   /*
149     iter_->ok () is tautology, but what the heck.
150   */
151   if (iter_ && iter_->ok ())
152     descend_to_child (iter_->get_outlet ());
153 }
154
155 /*
156   maintain invariants: change cursor, iter and here_mom_ in one fell
157   swoop.
158 */
159 void
160 Sequential_iterator::next_element (bool)
161 {
162   Moment len = iter_->music_get_length () - iter_->music_start_mom ();
163   assert (!grace_fixups_ || grace_fixups_->start_ >= here_mom_);
164
165   if (len.main_part_
166       && get_grace_fixup ())
167     {
168       Grace_fixup *gf = get_grace_fixup ();
169
170       last_mom_ = here_mom_;
171       here_mom_ += gf->length_;
172       here_mom_.grace_part_ += gf->grace_start_;
173
174       next_grace_fixup ();
175     }
176   else if (len.grace_part_ && !len.main_part_)
177     {
178       last_mom_ = here_mom_;
179       here_mom_.grace_part_ = 0;
180     }
181   else
182     {
183       /*
184         !len.grace_part_ || len.main_part_
185
186         We skip over a big chunk (mainpart != 0). Any starting graces
187         in that chunk should be in len.grace_part_
188
189       */
190       last_mom_ = here_mom_;
191       here_mom_ += len;
192     }
193
194   cursor_ = scm_cdr (cursor_);
195
196   iter_->quit ();
197   if (scm_is_pair (cursor_))
198     iter_ = unsmob<Music_iterator> (get_iterator (unsmob<Music> (scm_car (cursor_))));
199   else
200     iter_ = 0;
201 }
202
203 void
204 Sequential_iterator::process (Moment until)
205 {
206   while (iter_)
207     {
208       Grace_fixup *gf = get_grace_fixup ();
209       if (gf
210           && gf->start_ + gf->length_
211           + Moment (Rational (0), gf->grace_start_) == until)
212         {
213           /*
214             do the stuff/note/rest preceding a grace.
215           */
216           iter_->process (iter_->music_get_length ());
217         }
218       else
219         {
220           Moment w = until - here_mom_ + iter_->music_start_mom ();
221           iter_->process (w);
222         }
223
224       /*
225         if the iter is still OK, there must be events left that have
226
227         TIME > LEFT
228
229       */
230       if (iter_->ok ())
231         return;
232
233       descend_to_child (iter_->get_outlet ());
234       next_element (true);
235     }
236 }
237
238 Moment
239 Sequential_iterator::pending_moment () const
240 {
241   Moment cp = iter_->pending_moment ();
242
243   /*
244     Fix-up a grace note halfway in the music.
245   */
246   Grace_fixup *gf = get_grace_fixup ();
247   if (gf
248       && gf->length_ + iter_->music_start_mom () == cp)
249     return here_mom_ + gf->length_ + Moment (0, gf->grace_start_);
250
251   /*
252     Fix-up a grace note at  the start of the music.
253   */
254   return cp + here_mom_ - iter_->music_start_mom ();
255 }
256
257 bool
258 Sequential_iterator::ok () const
259 {
260   return iter_;
261 }
262
263 IMPLEMENT_CTOR_CALLBACK (Sequential_iterator);
264
265 bool
266 Sequential_iterator::run_always () const
267 {
268   return iter_ ? iter_->run_always () : false;
269 }
270
271 void
272 Sequential_iterator::next_grace_fixup ()
273 {
274   Grace_fixup *n = grace_fixups_->next_;
275   delete grace_fixups_;
276   grace_fixups_ = n;
277 }
278
279 Grace_fixup *
280 Sequential_iterator::get_grace_fixup () const
281 {
282   if (grace_fixups_ && grace_fixups_->start_ == here_mom_)
283     return grace_fixups_;
284   else
285     return 0;
286 }