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]));
173 for (int i= 0; i < prev_idx; i++)
176 || (ly_c_number_p (lazyness)
177 && curbarnum > accbarnum + ly_scm2int (lazyness)))
179 p = ly_scm2int (ly_cdr (prevs[i]));
187 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
192 *different = (a != p);
197 number_accidentals (bool *different,
198 Pitch *pitch, Context * origin,
199 SCM accidentals, int curbarnum)
204 if (ly_c_pair_p (accidentals) && !ly_c_symbol_p (ly_car (accidentals)))
205 warning (_f ("Accidental typesetting list must begin with context-name: %s",
206 ly_scm2string (ly_car (accidentals)).to_str0 ()));
208 for (; ly_c_pair_p (accidentals) && origin; accidentals = ly_cdr (accidentals))
210 // If pair then it is a new accidentals typesetting rule to be checked
211 SCM rule = ly_car (accidentals);
212 if (ly_c_pair_p (rule))
214 SCM type = ly_car (rule);
215 SCM lazyness = ly_cdr (rule);
216 SCM localsig = origin->get_property ("localKeySignature");
219 ly_c_eq_p (ly_symbol2scm ("same-octave"), type);
221 ly_c_eq_p (ly_symbol2scm ("any-octave"), type);
223 if (same_octave_b || any_octave_b)
226 int n = number_accidentals_from_sig
227 (&d, localsig, pitch, curbarnum, lazyness, any_octave_b);
228 *different = *different || d;
229 number = max (number, n);
232 warning (_f ("ignoring unknown accidental: %s",
233 ly_symbol2string (type).to_str0 ()));
238 if symbol then it is a context name. Scan parent contexts to find it.
240 else if (ly_c_symbol_p (rule))
242 Context * dad = origin;
243 while (dad && !dad->is_alias (rule))
244 dad = dad->get_parent_context ();
249 else warning (_f ("Accidental rule must be pair or context-name; Found %s",
250 ly_scm2string (rule).to_str0 ()));
257 Accidental_engraver::get_bar_number ()
259 SCM barnum = get_property ("currentBarNumber");
260 SCM smp = get_property ("measurePosition");
262 int bn = robust_scm2int (barnum, 0);
264 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
265 if (mp.main_part_ < Rational (0))
272 Accidental_engraver::process_acknowledged_grobs ()
274 if (accidentals_.size () && !accidentals_.top ().done_)
276 SCM accidentals = get_property ("autoAccidentals");
277 SCM cautionaries = get_property ("autoCautionaries");
278 int barnum = get_bar_number ();
280 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
281 for (int i = 0; i < accidentals_.size (); i++)
283 if (accidentals_[i].done_ )
285 accidentals_[i].done_ = true;
286 Grob * support = accidentals_[i].head_;
287 Music * note = accidentals_[i].melodic_;
288 Context * origin = accidentals_[i].origin_;
290 Pitch * pitch = unsmob_pitch (note->get_property ("pitch"));
294 bool different = false;
295 bool different_caut = false;
297 int num = number_accidentals (&different,
299 accidentals, barnum);
300 int num_caut = number_accidentals (&different_caut,
302 cautionaries, barnum);
304 bool cautionary = to_boolean (note->get_property ("cautionary"));
309 different = different_caut;
313 if (num == 0 && to_boolean (note->get_property ("force-accidental")))
318 Can not look for ties: it's not guaranteed that they reach
325 We construct the accidentals at the originating Voice
326 level, so that we get the property settings for
327 Accidental from the respective Voice.
329 Grob * a = make_item_from_properties (origin->implementation (),
330 ly_symbol2scm ("Accidental"),
333 a->set_parent (support, Y_AXIS);
335 if (!accidental_placement_)
337 accidental_placement_ = make_item ("AccidentalPlacement", a->self_scm ());
340 Accidental_placement::add_accidental (accidental_placement_, a);
343 SCM accs = scm_cons (scm_int2num (pitch->get_alteration ()), SCM_EOL);
344 if (num == 2 && extra_natural_b)
345 accs = scm_cons (scm_int2num (0), accs);
349 add cautionary option in accidental.
354 a->set_property ("cautionary", SCM_BOOL_T);
358 support->set_property ("accidental-grob", a->self_scm ());
360 a->set_property ("accidentals", accs);
361 accidentals_[i].accidental_ = a;
364 We add the accidentals to the support of the arpeggio,
365 so it is put left of the accidentals.
367 for (int i = 0; i < left_objects_.size (); i++)
368 Side_position_interface::add_support (left_objects_[i], a);
369 for (int i = 0; i < right_objects_.size (); i++)
370 Side_position_interface::add_support (a, right_objects_[i]);
380 Accidental_engraver::finalize ()
382 last_keysig_ = SCM_EOL;
386 Accidental_engraver::stop_translation_timestep ()
388 for (int j = ties_.size (); j --; )
390 Grob * r = Tie::head (ties_[j], RIGHT);
391 for (int i = accidentals_.size (); i--;)
392 if (accidentals_[i].head_ == r)
394 if (Grob * g = accidentals_[i].accidental_)
396 g->set_property ("tie", ties_[j]->self_scm ());
397 accidentals_[i].tied_ = true;
405 for (int i = accidentals_.size (); i--;)
407 int barnum = get_bar_number ();
409 Music * note = accidentals_[i].melodic_;
410 Context * origin = accidentals_[i].origin_;
412 Pitch * pitch = unsmob_pitch (note->get_property ("pitch"));
416 int n = pitch->get_notename ();
417 int o = pitch->get_octave ();
418 int a = pitch->get_alteration ();
419 SCM on_s = scm_cons (scm_int2num (o), scm_int2num (n));
421 while (origin && origin->where_defined (ly_symbol2scm ("localKeySignature")))
424 huh? we set props all the way to the top?
426 SCM localsig = origin->get_property ("localKeySignature");
428 if (accidentals_[i].tied_)
431 Remember an alteration that is different both from
432 that of the tied note and of the key signature.
434 localsig = ly_assoc_front_x
435 (localsig, on_s, scm_cons (SCM_BOOL_T, scm_int2num (barnum)));
442 not really really correct if there are more than one
443 noteheads with the same notename.
445 localsig = ly_assoc_front_x
446 (localsig, on_s, scm_cons (scm_int2num (a), scm_int2num (barnum)));
452 origin->set_property ("localKeySignature", localsig);
454 origin = origin->get_parent_context ();
459 accidental_placement_ = 0;
461 accidentals_.clear ();
462 left_objects_.clear ();
463 right_objects_.clear ();
467 Accidental_engraver::acknowledge_grob (Grob_info info)
469 Music * note = info.music_cause ();
472 && note->is_mus_type ("note-event")
473 && Rhythmic_head::has_interface (info.grob_))
475 if (to_boolean ( get_property ("harmonicAccidentals"))
476 || !ly_c_equal_p (info.grob_->get_property ("style"),
477 ly_symbol2scm ("harmonic")))
480 Accidental_entry entry ;
481 entry.head_ = info.grob_;
482 entry.origin_ = info.origin_trans_->context ();
483 entry.melodic_ = note;
485 accidentals_.push (entry);
488 else if (Tie::has_interface (info.grob_))
490 ties_.push (dynamic_cast<Spanner*> (info.grob_));
492 else if (Arpeggio::has_interface (info.grob_))
494 left_objects_.push (info.grob_);
496 else if (info.grob_->internal_has_interface (ly_symbol2scm ("finger-interface")))
498 left_objects_.push (info.grob_);
503 Accidental_engraver::process_music ()
505 SCM sig = get_property ("keySignature");
507 /* Detect key sig changes.
508 Update all parents and children
510 if (last_keysig_ != sig)
512 update_local_key_signature ();
520 ENTER_DESCRIPTION (Accidental_engraver,
521 "Make accidentals. Catches note heads, ties and notices key-change "
522 "events. This engraver usually lives at Staff level, but "
523 "reads the settings for Accidental at @code{Voice} level, "
524 "so you can @code{\\override} them at @code{Voice}. "
529 "finger-interface rhythmic-head-interface tie-interface arpeggio-interface",
530 "localKeySignature harmonicAccidentals extraNatural autoAccidentals autoCautionaries",
531 "localKeySignature");