2 accidental-engraver.cc -- implement accidental_engraver
4 (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 Modified 2001--2002 by Rune Zedeler <rz@daimi.au.dk>
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"
20 #include "protected-scm.hh"
23 struct Accidental_entry {
33 Accidental_entry::Accidental_entry ()
43 struct Accidental_engraver : Engraver {
46 int get_bar_number ();
47 void update_local_key_signature ();
50 TRANSLATOR_DECLARATIONS (Accidental_engraver);
51 virtual void process_music ();
52 virtual void acknowledge_grob (Grob_info);
53 virtual void stop_translation_timestep ();
54 virtual void initialize ();
55 virtual void process_acknowledged_grobs ();
56 virtual void finalize ();
59 Protected_scm last_keysig_; // ugh.
62 Urgh. Since the accidentals depend on lots of variables, we have to
63 store all information before we can really create the accidentals.
65 Link_array<Grob> left_objects_;
66 Link_array<Grob> right_objects_;
68 Grob * accidental_placement_;
70 Array<Accidental_entry> accidentals_;
71 Link_array<Spanner> ties_;
78 ugh, it is not clear what properties are mutable and which
79 aren't. eg. localKeySignature is changed at runtime, which means
80 that references in grobs should always store ly_deep_copy ()s of
85 set_property_on_children (Context * trans, const char * sym, SCM val)
87 trans->set_property (sym, ly_deep_copy (val));
88 for (SCM p = trans->children_contexts (); ly_c_pair_p (p); p = ly_cdr (p))
90 Context *trg = unsmob_context (ly_car (p));
91 set_property_on_children (trg, sym, ly_deep_copy (val));
95 Accidental_engraver::Accidental_engraver ()
97 accidental_placement_ = 0;
98 last_keysig_ = SCM_EOL;
103 Accidental_engraver::update_local_key_signature ()
105 last_keysig_ = get_property ("keySignature");
106 set_property_on_children (context (), "localKeySignature", last_keysig_);
108 Context * trans = context ()->get_parent_context ();
111 Huh. Don't understand what this is good for. --hwn.
113 while (trans && trans->where_defined (ly_symbol2scm ("localKeySignature")))
115 trans->set_property ("localKeySignature",
116 ly_deep_copy (last_keysig_));
117 trans = trans->get_parent_context ();
122 Accidental_engraver::initialize ()
124 update_local_key_signature ();
129 calculates the number of accidentals on basis of the current local key
130 sig (passed as argument)
132 Returns number of accidentals (0, 1 or 2).
136 number_accidentals_from_sig (bool *different,
137 SCM sig, Pitch *pitch, int curbarnum, SCM lazyness,
140 int n = pitch->get_notename ();
141 int o = pitch->get_octave ();
142 int a = pitch->get_alteration ();
146 int bar_nums[3] = {-1,-1,-1};
151 prevs[prev_idx] = scm_assoc (scm_cons (scm_int2num (o), scm_int2num (n)), sig);
153 if (ly_c_pair_p (prevs[prev_idx]))
157 prevs[prev_idx] = scm_assoc (scm_int2num (n), sig);
158 if (ly_c_pair_p (prevs[prev_idx]))
161 for (int i= 0; i < prev_idx; i++)
163 if (ly_c_pair_p (prevs[i])
164 && ly_c_pair_p (ly_cdr (prevs[i])))
166 bar_nums[i] = ly_scm2int (ly_cddr (prevs[i]));
167 prevs[i] = scm_cons (ly_car (prevs[i]), ly_cadr (prevs[i]));
172 SCM prev_acc = scm_int2num (0);
173 for (int i= 0; i < prev_idx; i++)
176 || (ly_c_number_p (lazyness)
177 && curbarnum > accbarnum + ly_scm2int (lazyness)))
179 prev_acc = ly_cdr (prevs[i]);
185 UGH. prev_acc can be #t in case of ties. What is this for?
188 int p = ly_c_number_p (prev_acc) ? ly_scm2int (prev_acc) : 0;
192 if (a == p && ly_c_number_p (prev_acc))
194 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
199 *different = (a != p);
204 number_accidentals (bool *different,
205 Pitch *pitch, Context * origin,
206 SCM accidentals, int curbarnum)
211 if (ly_c_pair_p (accidentals) && !ly_c_symbol_p (ly_car (accidentals)))
212 warning (_f ("Accidental typesetting list must begin with context-name: %s",
213 ly_scm2string (ly_car (accidentals)).to_str0 ()));
215 for (; ly_c_pair_p (accidentals) && origin; accidentals = ly_cdr (accidentals))
217 // If pair then it is a new accidentals typesetting rule to be checked
218 SCM rule = ly_car (accidentals);
219 if (ly_c_pair_p (rule))
221 SCM type = ly_car (rule);
222 SCM lazyness = ly_cdr (rule);
223 SCM localsig = origin->get_property ("localKeySignature");
226 ly_c_eq_p (ly_symbol2scm ("same-octave"), type);
228 ly_c_eq_p (ly_symbol2scm ("any-octave"), type);
230 if (same_octave_b || any_octave_b)
233 int n = number_accidentals_from_sig
234 (&d, localsig, pitch, curbarnum, lazyness, any_octave_b);
235 *different = *different || d;
236 number = max (number, n);
239 warning (_f ("ignoring unknown accidental: %s",
240 ly_symbol2string (type).to_str0 ()));
245 if symbol then it is a context name. Scan parent contexts to find it.
247 else if (ly_c_symbol_p (rule))
249 Context * dad = origin;
250 while (dad && !dad->is_alias (rule))
251 dad = dad->get_parent_context ();
256 else warning (_f ("Accidental rule must be pair or context-name; Found %s",
257 ly_scm2string (rule).to_str0 ()));
264 Accidental_engraver::get_bar_number ()
266 SCM barnum = get_property ("currentBarNumber");
267 SCM smp = get_property ("measurePosition");
269 int bn = robust_scm2int (barnum, 0);
271 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
272 if (mp.main_part_ < Rational (0))
279 Accidental_engraver::process_acknowledged_grobs ()
281 if (accidentals_.size () && !accidentals_.top ().done_)
283 SCM accidentals = get_property ("autoAccidentals");
284 SCM cautionaries = get_property ("autoCautionaries");
285 int barnum = get_bar_number ();
287 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
288 for (int i = 0; i < accidentals_.size (); i++)
290 if (accidentals_[i].done_ )
292 accidentals_[i].done_ = true;
293 Grob * support = accidentals_[i].head_;
294 Music * note = accidentals_[i].melodic_;
295 Context * origin = accidentals_[i].origin_;
297 Pitch * pitch = unsmob_pitch (note->get_property ("pitch"));
301 bool different = false;
302 bool different_caut = false;
304 int num = number_accidentals (&different,
306 accidentals, barnum);
307 int num_caut = number_accidentals (&different_caut,
309 cautionaries, barnum);
311 bool cautionary = to_boolean (note->get_property ("cautionary"));
316 different = different_caut;
320 if (num == 0 && to_boolean (note->get_property ("force-accidental")))
325 Can not look for ties: it's not guaranteed that they reach
332 We construct the accidentals at the originating Voice
333 level, so that we get the property settings for
334 Accidental from the respective Voice.
336 Grob * a = make_item_from_properties (origin->implementation (),
337 ly_symbol2scm ("Accidental"),
340 a->set_parent (support, Y_AXIS);
342 if (!accidental_placement_)
344 accidental_placement_ = make_item ("AccidentalPlacement", a->self_scm ());
347 Accidental_placement::add_accidental (accidental_placement_, a);
350 SCM accs = scm_cons (scm_int2num (pitch->get_alteration ()), SCM_EOL);
351 if (num == 2 && extra_natural_b)
352 accs = scm_cons (scm_int2num (0), accs);
356 add cautionary option in accidental.
361 a->set_property ("cautionary", SCM_BOOL_T);
365 support->set_property ("accidental-grob", a->self_scm ());
367 a->set_property ("accidentals", accs);
368 accidentals_[i].accidental_ = a;
371 We add the accidentals to the support of the arpeggio,
372 so it is put left of the accidentals.
374 for (int i = 0; i < left_objects_.size (); i++)
375 Side_position_interface::add_support (left_objects_[i], a);
376 for (int i = 0; i < right_objects_.size (); i++)
377 Side_position_interface::add_support (a, right_objects_[i]);
387 Accidental_engraver::finalize ()
389 last_keysig_ = SCM_EOL;
393 Accidental_engraver::stop_translation_timestep ()
395 for (int j = ties_.size (); j --; )
397 Grob * r = Tie::head (ties_[j], RIGHT);
398 for (int i = accidentals_.size (); i--;)
399 if (accidentals_[i].head_ == r)
401 if (Grob * g = accidentals_[i].accidental_)
403 g->set_property ("tie", ties_[j]->self_scm ());
404 accidentals_[i].tied_ = true;
412 for (int i = accidentals_.size (); i--;)
414 int barnum = get_bar_number ();
416 Music * note = accidentals_[i].melodic_;
417 Context * origin = accidentals_[i].origin_;
419 Pitch * pitch = unsmob_pitch (note->get_property ("pitch"));
423 int n = pitch->get_notename ();
424 int o = pitch->get_octave ();
425 int a = pitch->get_alteration ();
426 SCM key = scm_cons (scm_int2num (o), scm_int2num (n));
428 while (origin && origin->where_defined (ly_symbol2scm ("localKeySignature")))
431 huh? we set props all the way to the top?
433 SCM localsig = origin->get_property ("localKeySignature");
435 if (accidentals_[i].tied_)
438 Remember an alteration that is different both from
439 that of the tied note and of the key signature.
441 localsig = ly_assoc_front_x
442 (localsig, key, scm_cons (SCM_BOOL_T, scm_int2num (barnum)));
449 not really really correct if there are more than one
450 noteheads with the same notename.
452 localsig = ly_assoc_front_x
453 (localsig, key, scm_cons (scm_int2num (a), scm_int2num (barnum)));
459 origin->set_property ("localKeySignature", localsig);
461 origin = origin->get_parent_context ();
466 accidental_placement_ = 0;
468 accidentals_.clear ();
469 left_objects_.clear ();
470 right_objects_.clear ();
474 Accidental_engraver::acknowledge_grob (Grob_info info)
476 Music * note = info.music_cause ();
479 && note->is_mus_type ("note-event")
480 && Rhythmic_head::has_interface (info.grob_))
482 if (to_boolean ( get_property ("harmonicAccidentals"))
483 || !ly_c_equal_p (info.grob_->get_property ("style"),
484 ly_symbol2scm ("harmonic")))
487 Accidental_entry entry ;
488 entry.head_ = info.grob_;
489 entry.origin_ = info.origin_trans_->context ();
490 entry.melodic_ = note;
492 accidentals_.push (entry);
495 else if (Tie::has_interface (info.grob_))
497 ties_.push (dynamic_cast<Spanner*> (info.grob_));
499 else if (Arpeggio::has_interface (info.grob_))
501 left_objects_.push (info.grob_);
503 else if (info.grob_->internal_has_interface (ly_symbol2scm ("finger-interface")))
505 left_objects_.push (info.grob_);
510 Accidental_engraver::process_music ()
512 SCM sig = get_property ("keySignature");
514 /* Detect key sig changes.
515 Update all parents and children
517 if (last_keysig_ != sig)
519 update_local_key_signature ();
527 ENTER_DESCRIPTION (Accidental_engraver,
528 "Make accidentals. Catches note heads, ties and notices key-change "
529 "events. This engraver usually lives at Staff level, but "
530 "reads the settings for Accidental at @code{Voice} level, "
531 "so you can @code{\\override} them at @code{Voice}. "
536 "finger-interface rhythmic-head-interface tie-interface arpeggio-interface",
537 "localKeySignature harmonicAccidentals extraNatural autoAccidentals autoCautionaries",
538 "localKeySignature");