* lily/music-iterator.cc (quit, do_quit): new function: break link
[lilypond.git] / lily / accidental-engraver.cc
blob0c00497d0918ac726b7b753b373d3ca3b489d2e0
1 /*
2 accidental-engraver.cc -- implement accidental_engraver
4 (c) 1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 Modified 2001-2002 by Rune Zedeler <rz@daimi.au.dk>
6 */
8 #include "musical-request.hh"
9 #include "command-request.hh"
10 #include "item.hh"
11 #include "tie.hh"
12 #include "rhythmic-head.hh"
13 #include "engraver-group-engraver.hh"
14 #include "accidental-placement.hh"
15 #include "side-position-interface.hh"
16 #include "engraver.hh"
17 #include "arpeggio.hh"
18 #include "warn.hh"
20 #include "translator-group.hh"
22 /**
25 FIXME: should not compute vertical positioning of accidentals, but
26 get them from the noteheads
28 The algorithm for accidentals should be documented, and made
29 tweakable.
33 struct Accidental_entry {
34 bool done_;
35 Note_req * melodic_;
36 Grob * accidental_;
37 Translator_group *origin_;
38 Grob* head_;
39 Accidental_entry();
42 Accidental_entry::Accidental_entry()
44 done_ = false;
45 melodic_ =0;
46 accidental_ = 0;
47 origin_ = 0;
48 head_ = 0;
51 struct Accidental_engraver : Engraver {
52 protected:
53 TRANSLATOR_DECLARATIONS (Accidental_engraver);
54 virtual void process_music ();
55 virtual void acknowledge_grob (Grob_info);
56 virtual void stop_translation_timestep ();
57 virtual void initialize ();
58 virtual void process_acknowledged_grobs ();
59 virtual void finalize ();
60 public:
63 TODO -> property.
65 This is not a property, and it is not protected. This poses a
66 very small risk of the value being GC'd from under us.
68 SCM last_keysig_;
71 Urgh. Since the accidentals depend on lots of variables, we have to
72 store all information before we can really create the accidentals.
74 Link_array<Grob> arpeggios_;
76 Grob * accidental_placement_;
80 The next
82 Array<Accidental_entry> accidentals_;
84 Link_array<Grob> ties_;
90 static void set_property_on_children (Translator_group * trans, const char * sym, SCM val)
92 trans->set_property (sym, val);
93 for (SCM p = trans -> trans_group_list_; gh_pair_p (p); p = ly_cdr(p)) {
94 Translator_group *trg = dynamic_cast<Translator_group*> (unsmob_translator (ly_car (p)));
95 set_property_on_children(trg,sym,ly_deep_copy(val));
99 Accidental_engraver::Accidental_engraver ()
101 accidental_placement_ = 0;
102 last_keysig_ = SCM_EOL;
105 void
106 Accidental_engraver::initialize ()
108 last_keysig_ = get_property ("keySignature");
110 Translator_group * trans_ = daddy_trans_;
111 while (trans_)
113 trans_ -> set_property ("localKeySignature", ly_deep_copy (last_keysig_));
114 trans_ = trans_->daddy_trans_;
116 set_property_on_children (daddy_trans_,"localKeySignature", last_keysig_);
121 calculates the number of accidentals on basis of the current local key sig
122 (passed as argument)
123 Returns number of accidentals (0, 1 or 2).
124 Negative (-1 or -2) if accidental has changed.
127 static int
128 number_accidentals (SCM sig, Note_req * note, Pitch *pitch, SCM curbarnum, SCM lazyness,
129 bool ignore_octave_b)
131 int n = pitch->notename_;
132 int o = pitch->octave_;
133 int a = pitch->alteration_;
134 int curbarnum_i = gh_scm2int (curbarnum);
135 int accbarnum_i = 0;
137 SCM prev;
138 if (ignore_octave_b)
139 prev = ly_assoc_cdr (scm_int2num (n), sig);
140 else
141 prev = gh_assoc (gh_cons (scm_int2num (o), scm_int2num (n)), sig);
143 /* should really be true unless prev == SCM_BOOL_F */
144 if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
146 accbarnum_i = gh_scm2int (ly_cddr (prev));
147 prev = gh_cons (ly_car (prev), ly_cadr (prev));
150 /* If an accidental was not found or the accidental was too old */
151 if (prev == SCM_BOOL_F ||
152 (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
153 prev = gh_assoc (scm_int2num (n), sig);
156 SCM prev_acc = (prev == SCM_BOOL_F) ? scm_int2num (0) : ly_cdr (prev);
158 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
160 int num;
161 if (a == p
162 && !to_boolean (note->get_mus_property ("force-accidental"))
163 && gh_number_p (prev_acc))
164 num = 0;
165 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
166 num = 2;
167 else
168 num = 1;
170 return a == p ? num : -num;
173 static int
174 number_accidentals (Note_req * note, Pitch *pitch, Translator_group * origin,
175 SCM accidentals, SCM curbarnum)
177 int number = 0;
179 bool diff = false;
180 if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
181 warning (_f ("Accidental typesetting list must begin with context-name: %s",
182 ly_scm2string (ly_car (accidentals)).to_str0 ()));
184 while (gh_pair_p (accidentals) && origin)
186 // If pair then it is a new accidentals typesetting rule to be checked
187 if (gh_pair_p (ly_car (accidentals)))
189 SCM type = gh_caar (accidentals);
190 SCM lazyness = gh_cdar (accidentals);
191 SCM localsig = origin->get_property ("localKeySignature");
193 bool same_octave_b =
194 gh_eq_p (ly_symbol2scm ("same-octave"), type);
195 bool any_octave_b =
196 gh_eq_p (ly_symbol2scm ("any-octave"), type);
198 if (same_octave_b || any_octave_b)
200 int n = number_accidentals
201 (localsig, note, pitch, curbarnum, lazyness, any_octave_b);
202 diff = diff || (n < 0);
203 number = max (number, abs (n));
205 else
206 warning (_f ("unknown accidental typesetting: %s. Ignored",
207 ly_symbol2string (type).to_str0 ()));
212 if symbol then it is a context name. Scan parent contexts to find it.
214 else if (gh_symbol_p (ly_car (accidentals)))
216 String context = ly_symbol2string (ly_car (accidentals));
218 while (origin && !origin->is_alias_b (context))
219 origin = origin->daddy_trans_;
221 if (!origin)
222 warning (_f ("Symbol is not a parent context: %s. Ignored",
223 context.to_str0 ()));
225 else warning (_f ("Accidental typesetting must be pair or context-name: %s",
226 ly_scm2string (ly_car (accidentals)).to_str0 ()));
228 accidentals = ly_cdr (accidentals);
230 return diff ? -number : number;
233 void
234 Accidental_engraver::process_acknowledged_grobs ()
236 if (accidentals_.size () && !accidentals_.top().done_)
238 //SCM localsig = get_property ("localKeySignature");
239 SCM accidentals = get_property ("autoAccidentals");
240 SCM cautionaries = get_property ("autoCautionaries");
241 SCM barnum = get_property ("currentBarNumber");
243 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
244 for (int i = 0; i < accidentals_.size (); i++)
246 if (accidentals_[i].done_ )
247 continue;
248 accidentals_[i].done_ = true;
249 Grob * support = accidentals_[i].head_;
250 Note_req * note = accidentals_[i].melodic_;
251 Translator_group * origin = accidentals_[i].origin_;
253 Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
254 int num = number_accidentals (note, pitch, origin, accidentals, barnum);
255 int num_caut = number_accidentals (note, pitch, origin, cautionaries, barnum);
256 bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
258 if (abs (num_caut) > abs (num))
260 num = num_caut;
261 cautionary = true;
264 bool different = num < 0;
265 num = abs (num);
267 /* see if there's a tie that "changes" the accidental */
268 /* works because if there's a tie, the note to the left
269 is of the same pitch as the actual note */
271 Grob *tie_break_reminder = 0;
272 bool tie_changes = false;
273 for (int j = 0; j < ties_.size (); j++)
274 if (support == Tie::head (ties_[j], RIGHT))
276 tie_changes = different;
278 /* Enable accidentals for broken tie
280 We only want an accidental on a broken tie,
281 if the tie changes the accidental.
283 Maybe check property noTieBreakForceAccidental? */
284 if (different)
285 tie_break_reminder = ties_[j];
286 break;
289 if (num)
291 Grob * a = new Item (get_property ("Accidental"));
292 a->set_parent (support, Y_AXIS);
294 if (!accidental_placement_)
296 accidental_placement_ = new Item (get_property ("AccidentalPlacement"));
297 announce_grob (accidental_placement_, a->self_scm());
300 Accidental_placement::add_accidental (accidental_placement_, a);
301 announce_grob (a, SCM_EOL);
304 SCM accs = gh_cons (scm_int2num (pitch->alteration_), SCM_EOL);
305 if (num == 2 && extra_natural_b)
306 accs = gh_cons (scm_int2num (0), accs);
308 /* TODO:
310 add cautionary option in accidental.
313 if (cautionary)
315 a->set_grob_property ("cautionary", SCM_BOOL_T);
318 if (tie_break_reminder)
320 // TODO.
321 a->set_grob_property ("tie", tie_break_reminder->self_scm());
325 support->set_grob_property ("accidental-grob", a->self_scm ());
327 a->set_grob_property ("accidentals", accs);
328 accidentals_[i].accidental_ = a;
330 We add the accidentals to the support of the arpeggio, so it is put left of the
331 accidentals.
334 for (int i = 0; i < arpeggios_.size (); i++)
335 Side_position_interface::add_support (arpeggios_[i], a);
340 We should not record the accidental if it is the first
341 note and it is tied from the previous measure.
343 Checking whether it is tied also works mostly, but will it
344 always do the correct thing?
348 int n = pitch->notename_;
349 int o = pitch->octave_;
350 int a = pitch->alteration_;
351 SCM on_s = gh_cons (scm_int2num (o), scm_int2num (n));
354 TODO: Speed this up!
356 Perhaps only check translators mentioned in the auto-accidentals?
359 TODO: profile this.
361 I'd be surprised if the impact of this would be
362 measurable. Anyway, it seems localsig doesn't change
363 every time-step, but a set_property() is done every
364 time. We could save on that, probably.
366 --hwn.
371 while (origin)
373 SCM localsig = origin->get_property ("localKeySignature");
374 if (tie_changes)
377 Remember an alteration that is different both from
378 that of the tied note and of the key signature.
380 localsig = ly_assoc_front_x
381 (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
383 else
386 not really really correct if there are more than one
387 noteheads with the same notename.
389 localsig = ly_assoc_front_x
390 (localsig, on_s, gh_cons (scm_int2num (a), barnum));
392 origin->set_property ("localKeySignature", localsig);
393 origin = origin->daddy_trans_;
399 void
400 Accidental_engraver::finalize ()
405 void
406 Accidental_engraver::stop_translation_timestep ()
408 for (int i = 0; i < accidentals_.size(); i++)
410 Grob *a = accidentals_[i].accidental_;
411 if (a)
413 typeset_grob (a);
417 if (accidental_placement_)
418 typeset_grob(accidental_placement_);
419 accidental_placement_ = 00;
421 accidentals_.clear();
422 arpeggios_.clear ();
423 ties_.clear ();
426 void
427 Accidental_engraver::acknowledge_grob (Grob_info info)
429 Note_req * note = dynamic_cast <Note_req *> (info.music_cause ());
431 if (note && Rhythmic_head::has_interface (info.grob_))
433 Accidental_entry entry ;
434 entry.head_ = info.grob_;
435 entry.origin_ = info.origin_trans_->daddy_trans_;
436 entry.melodic_ = note;
438 accidentals_.push (entry);
440 else if (Tie::has_interface (info.grob_))
442 ties_.push (info.grob_);
444 else if (Arpeggio::has_interface (info.grob_))
446 arpeggios_.push (info.grob_);
451 void
452 Accidental_engraver::process_music ()
454 SCM sig = get_property ("keySignature");
456 /* Detect key sig changes.
457 Update all parents and children
459 if (last_keysig_ != sig)
461 Translator_group * trans_ = daddy_trans_;
462 while (trans_)
464 trans_ -> set_property ("localKeySignature", ly_deep_copy (sig));
465 trans_ = trans_->daddy_trans_;
467 set_property_on_children(daddy_trans_,"localKeySignature", sig);
469 last_keysig_ = sig;
477 ENTER_DESCRIPTION (Accidental_engraver,
478 "Make accidentals. Catches note heads, ties and notices key-change
479 events. Due to interaction with ties (which don't come together
480 with note heads), this needs to be in a context higher than Tie_engraver.",
482 "Accidental",
483 "rhythmic-head-interface tie-interface arpeggio-interface",
484 "localKeySignature extraNatural autoAccidentals autoCautionaries",
485 "localKeySignature");