1 /*
2 This file is part of LilyPond, the GNU music typesetter.
3
4 Copyright (C) 1999--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 "engraver.hh"
21 #include "moment.hh"
22 #include "note-spacing.hh"
23 #include "paper-column.hh"
24 #include "pointer-group-interface.hh"
25 #include "pqueue.hh"
26 #include "spanner.hh"
27 #include "staff-spacing.hh"
28 #include "stream-event.hh"
29
30 #include "translator.icc"
31
32 using std::vector;
33
34 struct Rhythmic_tuple
35 {
36 Grob_info info_;
37 Moment end_;
38
Rhythmic_tupleRhythmic_tuple39 Rhythmic_tuple (Grob_info i, Moment m) : info_ (i), end_ (m) {}
40
41 static int time_compare (Rhythmic_tuple const &, Rhythmic_tuple const &);
42 };
43
44 inline int
compare(Rhythmic_tuple const & a,Rhythmic_tuple const & b)45 compare (Rhythmic_tuple const &a, Rhythmic_tuple const &b)
46 {
47 return Rhythmic_tuple::time_compare (a, b);
48 }
49
50 int
time_compare(Rhythmic_tuple const & h1,Rhythmic_tuple const & h2)51 Rhythmic_tuple::time_compare (Rhythmic_tuple const &h1,
52 Rhythmic_tuple const &h2)
53 {
54 return (h1.end_ - h2.end_).main_part_.sign ();
55 }
56
57 /****************************************************************/
58
59 /*
60 Acknowledge rhythmic elements, for initializing spacing fields in
61 the columns.
62 */
63 class Spacing_engraver : public Engraver
64 {
65 PQueue<Rhythmic_tuple> playing_durations_;
66 vector<Rhythmic_tuple> now_durations_;
67 vector<Rhythmic_tuple> stopped_durations_;
68 Moment now_;
69 Spanner *spacing_;
70 Stream_event *start_section_;
71
72 TRANSLATOR_DECLARATIONS (Spacing_engraver);
73
74 protected:
75 void acknowledge_staff_spacing (Grob_info);
76 void acknowledge_note_spacing (Grob_info);
77 void acknowledge_rhythmic_head (Grob_info);
78 void acknowledge_rhythmic_grob (Grob_info);
79 void listen_spacing_section (Stream_event *);
80
81 void start_translation_timestep ();
82 void stop_translation_timestep ();
83 void process_music ();
84 void add_starter_duration (Grob_info i);
85
86 void initialize () override;
87 void finalize () override;
88
89 void start_spanner ();
90 void stop_spanner ();
91 };
92
Spacing_engraver(Context * c)93 Spacing_engraver::Spacing_engraver (Context *c)
94 : Engraver (c)
95 {
96 spacing_ = 0;
97 start_section_ = 0;
98 }
99
100 void
listen_spacing_section(Stream_event * ev)101 Spacing_engraver::listen_spacing_section (Stream_event *ev)
102 {
103 ASSIGN_EVENT_ONCE (start_section_, ev);
104 }
105
106 void
process_music()107 Spacing_engraver::process_music ()
108 {
109 if (start_section_ && spacing_)
110 stop_spanner ();
111
112 if (!spacing_)
113 start_spanner ();
114 }
115
116 void
start_spanner()117 Spacing_engraver::start_spanner ()
118 {
119 assert (!spacing_);
120
121 spacing_ = make_spanner ("SpacingSpanner", SCM_EOL);
122 auto *col = unsmob<Grob> (get_property (this, "currentCommandColumn"));
123 spacing_->set_bound (LEFT, col);
124 }
125
126 void
initialize()127 Spacing_engraver::initialize ()
128 {
129 now_ = now_mom ();
130 }
131
132 void
finalize()133 Spacing_engraver::finalize ()
134 {
135 stop_spanner ();
136 }
137
138 void
stop_spanner()139 Spacing_engraver::stop_spanner ()
140 {
141 if (spacing_)
142 {
143 Grob *p = unsmob<Grob> (get_property (this, "currentCommandColumn"));
144
145 spacing_->set_bound (RIGHT, p);
146 spacing_ = 0;
147 }
148 }
149
150 void
acknowledge_note_spacing(Grob_info i)151 Spacing_engraver::acknowledge_note_spacing (Grob_info i)
152 {
153 Pointer_group_interface::add_grob (spacing_, ly_symbol2scm ("wishes"), i.grob ());
154 }
155
156 void
acknowledge_staff_spacing(Grob_info i)157 Spacing_engraver::acknowledge_staff_spacing (Grob_info i)
158 {
159 Pointer_group_interface::add_grob (spacing_, ly_symbol2scm ("wishes"), i.grob ());
160 }
161
162 void
acknowledge_rhythmic_grob(Grob_info i)163 Spacing_engraver::acknowledge_rhythmic_grob (Grob_info i)
164 {
165 add_starter_duration (i);
166 }
167
168 void
acknowledge_rhythmic_head(Grob_info i)169 Spacing_engraver::acknowledge_rhythmic_head (Grob_info i)
170 {
171 add_starter_duration (i);
172 }
173
174 void
add_starter_duration(Grob_info i)175 Spacing_engraver::add_starter_duration (Grob_info i)
176 {
177 if (i.grob ()->internal_has_interface (ly_symbol2scm ("lyric-syllable-interface"))
178 || i.grob ()->internal_has_interface (ly_symbol2scm ("multi-measure-interface")))
179 return;
180
181 /*
182 only pay attention to durations that are not grace notes.
183 */
184 if (!now_.grace_part_)
185 {
186 Stream_event *r = i.event_cause ();
187 if (r && r->in_event_class ("rhythmic-event"))
188 {
189 auto len = get_event_length (r, now_);
190 Rhythmic_tuple t (i, now_mom () + len);
191 now_durations_.push_back (t);
192 }
193 }
194 }
195
196 void
stop_translation_timestep()197 Spacing_engraver::stop_translation_timestep ()
198 {
199 Paper_column *musical_column
200 = unsmob<Paper_column> (get_property (this, "currentMusicalColumn"));
201
202 if (!spacing_)
203 start_spanner ();
204
205 set_object (musical_column, "spacing", spacing_->self_scm ());
206 set_object (unsmob<Grob> (get_property (this, "currentCommandColumn")),
207 "spacing", spacing_->self_scm ());
208
209 SCM proportional = get_property (this, "proportionalNotationDuration");
210 if (unsmob<Moment> (proportional))
211 {
212 set_property (musical_column, "shortest-playing-duration", proportional);
213 set_property (musical_column, "shortest-starter-duration", proportional);
214 set_property (musical_column, "used", SCM_BOOL_T);
215 return;
216 }
217
218 auto shortest_playing = Moment::infinity ();
219 for (vsize i = 0; i < playing_durations_.size (); i++)
220 {
221 Stream_event *ev = playing_durations_[i].info_.event_cause ();
222 if (ev)
223 {
224 auto m = get_event_length (ev);
225 shortest_playing = std::min (shortest_playing, m);
226 }
227 }
228 auto starter = Moment::infinity ();
229
230 for (vsize i = 0; i < now_durations_.size (); i++)
231 {
232 auto m = get_event_length (now_durations_[i].info_.event_cause ());
233 if (m)
234 {
235 starter = std::min (starter, m);
236 playing_durations_.insert (now_durations_[i]);
237 }
238 }
239 now_durations_.clear ();
240
241 shortest_playing = std::min (shortest_playing, starter);
242
243 assert (starter);
244 SCM sh = shortest_playing.smobbed_copy ();
245 SCM st = starter.smobbed_copy ();
246
247 set_property (musical_column, "shortest-playing-duration", sh);
248 set_property (musical_column, "shortest-starter-duration", st);
249 }
250
251 void
start_translation_timestep()252 Spacing_engraver::start_translation_timestep ()
253 {
254 start_section_ = 0;
255
256 now_ = now_mom ();
257 stopped_durations_.clear ();
258
259 while (playing_durations_.size () && playing_durations_.front ().end_ < now_)
260 playing_durations_.delmin ();
261 while (playing_durations_.size () && playing_durations_.front ().end_ == now_)
262 stopped_durations_.push_back (playing_durations_.get ());
263 }
264
265 void
boot()266 Spacing_engraver::boot ()
267 {
268 ADD_LISTENER (Spacing_engraver, spacing_section);
269 ADD_ACKNOWLEDGER (Spacing_engraver, staff_spacing);
270 ADD_ACKNOWLEDGER (Spacing_engraver, note_spacing);
271 ADD_ACKNOWLEDGER (Spacing_engraver, rhythmic_head);
272 ADD_ACKNOWLEDGER (Spacing_engraver, rhythmic_grob);
273 }
274
275 ADD_TRANSLATOR (Spacing_engraver,
276 /* doc */
277 "Make a @code{SpacingSpanner} and do bookkeeping of shortest"
278 " starting and playing notes.",
279
280 /* create */
281 "SpacingSpanner ",
282
283 /* read */
284 "currentMusicalColumn "
285 "currentCommandColumn "
286 "proportionalNotationDuration ",
287
288 /* write */
289 ""
290 );
291