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>
8 #include "musical-request.hh"
9 #include "command-request.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"
20 #include "translator-group.hh"
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
33 struct New_accidental_entry {
35 int number_accidentals_;
36 int number_cautionaries_;
40 Translator_group *origin_;
42 New_accidental_entry();
45 New_accidental_entry::New_accidental_entry()
48 number_accidentals_ = 0;
49 number_cautionaries_ = 0;
57 struct New_accidental_engraver : Engraver {
59 TRANSLATOR_DECLARATIONS (New_accidental_engraver);
60 virtual void process_music ();
61 virtual void acknowledge_grob (Grob_info);
62 virtual void stop_translation_timestep ();
63 virtual void initialize ();
64 virtual void process_acknowledged_grobs ();
65 virtual void finalize ();
66 virtual void process_grobs_first_pass ();
67 virtual void process_grobs_second_pass ();
74 This is not a property, and it is not protected. This poses a
75 very small risk of the value being GC'd from under us.
80 Urgh. Since the accidentals depend on lots of variables, we have to
81 store all information before we can really create the accidentals.
83 Link_array<Grob> arpeggios_;
85 Grob * accidental_placement_;
91 Array<New_accidental_entry> accidental_arr_;
93 Link_array<Grob> tie_arr_;
99 New_accidental_engraver::New_accidental_engraver ()
101 accidental_placement_ = 0;
102 last_keysig_ = SCM_EOL;
105 /* inserts the source alist into the destination alist, erasing old entries.
106 result is: dest = merged
108 static SCM merge_alists_front_x (SCM src, SCM dest) {
110 dest = merge_alists_front_x(ly_cdr(src),dest);
111 dest = ly_assoc_front_x(dest, ly_caar(src), ly_cdar(src));
116 static void merge_property_on_children (Translator_group * trans,
117 const char * from_sym, const char * to_sym)
119 SCM from = trans->get_property(from_sym);
120 SCM to = trans->get_property(to_sym);
121 to = merge_alists_front_x(from, to);
122 trans->set_property (to_sym, to);
123 trans->set_property (from_sym, SCM_EOL);
124 for (SCM p = trans -> trans_group_list_; gh_pair_p (p); p = ly_cdr(p)) {
125 Translator_group *trg = dynamic_cast<Translator_group*> (unsmob_translator (ly_car (p)));
126 merge_property_on_children(trg, from_sym, to_sym);
130 static void merge_property_on_family (Translator_group * trans,
131 const char * from_sym, const char * to_sym)
133 merge_property_on_children (trans, from_sym, to_sym);
134 trans = trans->daddy_trans_l_;
137 SCM from = trans->get_property(from_sym);
138 SCM to = trans->get_property(to_sym);
139 to = merge_alists_front_x(from, to);
140 trans->set_property (to_sym, to);
141 trans->set_property (from_sym, SCM_EOL);
142 trans = trans->daddy_trans_l_;
146 static void set_property_on_children (Translator_group * trans, const char * sym, SCM val)
148 trans->set_property (sym, val);
149 for (SCM p = trans -> trans_group_list_; gh_pair_p (p); p = ly_cdr(p)) {
150 Translator_group *trg = dynamic_cast<Translator_group*> (unsmob_translator (ly_car (p)));
151 set_property_on_children(trg,sym,ly_deep_copy(val));
155 static void set_property_on_family(Translator_group * trans, const char * sym, SCM val)
157 set_property_on_children (trans, sym, val);
158 trans = trans->daddy_trans_l_;
161 trans -> set_property (sym, ly_deep_copy (val));
162 trans = trans->daddy_trans_l_;
167 New_accidental_engraver::initialize ()
169 // to ensure that process_music will initialize last_keysig_
170 last_keysig_ = SCM_BOOL_F;
174 calculates the number of accidentals on basis of the current local key sig
176 Returns number of accidentals (0, 1 or 2).
177 Negative (-1 or -2) if accidental has changed.
181 number_accidentals (SCM sig, Note_req * note_l, Pitch *pitch, SCM curbarnum, SCM lazyness,
182 bool ignore_octave_b)
184 int n = pitch->notename_i_;
185 int o = pitch->octave_i_;
186 int a = pitch->alteration_i_;
187 int curbarnum_i = gh_scm2int (curbarnum);
192 prev = ly_assoc_cdr (gh_int2scm (n), sig);
194 prev = gh_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), sig);
196 /* should really be true unless prev == SCM_BOOL_F */
197 if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
199 accbarnum_i = gh_scm2int (ly_cddr (prev));
200 prev = gh_cons (ly_car (prev), ly_cadr (prev));
203 /* If an accidental was not found or the accidental was too old */
204 if (prev == SCM_BOOL_F ||
205 (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
206 prev = gh_assoc (gh_int2scm (n), sig);
209 SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
211 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
215 && !to_boolean (note_l->get_mus_property ("force-accidental"))
216 && gh_number_p (prev_acc))
218 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
223 return a == p ? num : -num;
227 number_accidentals (Note_req * note_l, Pitch *pitch, Translator_group * origin_l,
228 SCM accidentals, SCM curbarnum)
233 if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
234 warning (_f ("Accidental typesetting list must begin with context-name: %s",
235 ly_scm2string (ly_car (accidentals)).ch_C ()));
237 while (gh_pair_p (accidentals) && origin_l)
239 // If pair then it is a new accidentals typesetting rule to be checked
240 if (gh_pair_p (ly_car (accidentals)))
242 SCM type = gh_caar (accidentals);
243 SCM lazyness = gh_cdar (accidentals);
244 SCM localsig = origin_l->get_property ("localKeySignature");
247 gh_eq_p (ly_symbol2scm ("same-octave"), type);
249 gh_eq_p (ly_symbol2scm ("any-octave"), type);
251 if (same_octave_b || any_octave_b)
253 int n = number_accidentals
254 (localsig, note_l, pitch, curbarnum, lazyness, any_octave_b);
255 diff = diff || (n < 0);
256 number = max (number, abs (n));
259 warning (_f ("unknown accidental typesetting: %s. Ignored",
260 ly_symbol2string (type).ch_C ()));
265 if symbol then it is a context name. Scan parent contexts to find it.
267 else if (gh_symbol_p (ly_car (accidentals)))
269 String context = ly_symbol2string (ly_car (accidentals));
271 while (origin_l && !origin_l->is_alias_b (context))
272 origin_l = origin_l->daddy_trans_l_;
275 warning (_f ("Symbol is not a parent context: %s. Ignored",
278 else warning (_f ("Accidental typesetting must be pair or context-name: %s",
279 ly_scm2string (ly_car (accidentals)).ch_C ()));
281 accidentals = ly_cdr (accidentals);
283 return diff ? -number : number;
288 Perhaps one should join the two functions into one function taking an
290 OTOH even though code would be smaller, spaghetti-level would increase.
293 New_accidental_engraver::process_grobs_first_pass ()
295 SCM accidentals = get_property ("autoAccidentals");
296 SCM cautionaries = get_property ("autoCautionaries");
297 SCM barnum = get_property ("currentBarNumber");
299 for (int i = 0; i < accidental_arr_.size (); i++)
301 if (accidental_arr_[i].pass_done_ >= 1)
303 accidental_arr_[i].pass_done_ = 1;
305 Grob * support_l = accidental_arr_[i].head_;
306 Note_req * note_l = accidental_arr_[i].melodic_;
307 Translator_group * origin_l = accidental_arr_[i].origin_;
308 Pitch * pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
311 num = number_accidentals (note_l, pitch, origin_l, accidentals, barnum);
312 accidental_arr_[i].number_accidentals_ = abs(num);
313 accidental_arr_[i].different_ = num<0;
315 num = number_accidentals (note_l, pitch, origin_l, cautionaries, barnum);
316 accidental_arr_[i].number_cautionaries_ = abs(num);
317 accidental_arr_[i].different_ = accidental_arr_[i].different_ || num<0;
319 bool tie_changes = false;
320 for (int j = 0; j < tie_arr_.size (); j++)
321 if (support_l == Tie::head (tie_arr_[j], RIGHT))
322 tie_changes = accidental_arr_[i].different_;
323 int n = pitch->notename_i_;
324 int o = pitch->octave_i_;
325 int a = pitch->alteration_i_;
326 SCM o_s = gh_int2scm (o);
327 SCM n_s = gh_int2scm (n);
328 SCM on_s = gh_cons (o_s,n_s);
332 SCM sigch = origin_l->get_property ("localKeySignatureChanges");
336 Remember an alteration that is different both from
337 that of the tied note and of the key signature.
341 alt = gh_int2scm (a);
342 bool other_alt_same_oct = false;
343 bool other_alt_any_oct = false;
344 for (SCM j = sigch; gh_pair_p(j); j = ly_cdr(j)) {
345 SCM entry = ly_car(j);
346 /* if same notename has a different alt already recorded: */
347 if(gh_equal_p(ly_cdar(entry),n_s) && !gh_equal_p(ly_cadr(entry),alt))
349 /* if it is also in same octave */
350 if(gh_equal_p(ly_caar(entry),o_s))
351 other_alt_same_oct = true;
353 other_alt_any_oct = true;
356 if(other_alt_same_oct)
358 sigch = ly_assoc_front_x (sigch, on_s, gh_cons(alt,barnum));
359 if(other_alt_any_oct && !other_alt_same_oct) {
360 sigch = ly_assoc_front_x (sigch, on_s, gh_cons(SCM_BOOL_T,barnum));
362 origin_l->set_property ("localKeySignatureChanges", sigch);
363 origin_l = origin_l->daddy_trans_l_;
369 New_accidental_engraver::process_grobs_second_pass ()
371 SCM accidentals = get_property ("autoAccidentals");
372 SCM cautionaries = get_property ("autoCautionaries");
373 SCM barnum = get_property ("currentBarNumber");
375 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
376 for (int i = 0; i < accidental_arr_.size (); i++)
378 if (accidental_arr_[i].pass_done_ >= 2)
380 accidental_arr_[i].pass_done_ = 2;
381 Grob * support_l = accidental_arr_[i].head_;
382 Note_req * note_l = accidental_arr_[i].melodic_;
383 Translator_group * origin_l = accidental_arr_[i].origin_;
385 Pitch * pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
388 num = number_accidentals (note_l, pitch, origin_l, accidentals, barnum);
389 accidental_arr_[i].number_accidentals_ =
390 max (accidental_arr_[i].number_accidentals_, abs(num));
391 accidental_arr_[i].different_ = accidental_arr_[i].different_ || num<0;
393 num = number_accidentals (note_l, pitch, origin_l, cautionaries, barnum);
394 accidental_arr_[i].number_cautionaries_ =
395 max (accidental_arr_[i].number_cautionaries_, abs(num));
396 accidental_arr_[i].different_ = accidental_arr_[i].different_ || num<0;
399 bool cautionary = to_boolean (note_l->get_mus_property ("cautionary"));
401 if (accidental_arr_[i].number_cautionaries_ >accidental_arr_[i].number_accidentals_ )
403 num = accidental_arr_[i].number_cautionaries_;
407 num = accidental_arr_[i].number_accidentals_;
409 bool tie_changes = false;
410 Grob *tie_break_reminder = 0;
411 for (int j = 0; j < tie_arr_.size (); j++)
412 if (support_l == Tie::head (tie_arr_[j], RIGHT))
414 tie_changes = accidental_arr_[i].different_;
415 tie_break_reminder = tie_arr_[j];
420 Grob * a = new Item (get_property ("Accidental"));
421 a->set_parent (support_l, Y_AXIS);
423 if (!accidental_placement_)
425 accidental_placement_ = new Item (get_property ("AccidentalPlacement"));
426 announce_grob (accidental_placement_, a->self_scm());
429 Accidental_placement::add_accidental (accidental_placement_, a);
430 announce_grob (a, SCM_EOL);
433 SCM accs = gh_cons (gh_int2scm (pitch->alteration_i_), SCM_EOL);
434 if (num == 2 && extra_natural_b)
435 accs = gh_cons (gh_int2scm (0), accs);
439 a->set_grob_property ("cautionary", SCM_BOOL_T);
442 if (tie_break_reminder)
444 a->set_grob_property ("tie", tie_break_reminder->self_scm());
448 support_l->set_grob_property ("accidental-grob", a->self_scm ());
450 a->set_grob_property ("accidentals", accs);
451 accidental_arr_[i].accidental_ = a;
453 We add the accidentals to the support of the arpeggio, so it is
454 put left of the accidentals.
456 for (int i = 0; i < arpeggios_.size (); i++)
457 Side_position_interface::add_support (arpeggios_[i], a);
463 New_accidental_engraver::process_acknowledged_grobs ()
465 if (accidental_arr_.size () && accidental_arr_.top().pass_done_ < 1)
466 process_grobs_first_pass ();
470 New_accidental_engraver::finalize ()
476 New_accidental_engraver::stop_translation_timestep ()
478 merge_property_on_family(daddy_trans_l_, "localKeySignatureChanges", "localKeySignature");
479 if (accidental_arr_.size () && accidental_arr_.top().pass_done_ < 2)
480 process_grobs_second_pass ();
482 for (int i = 0; i < accidental_arr_.size(); i++)
484 Grob *a = accidental_arr_[i].accidental_;
491 if (accidental_placement_)
492 typeset_grob(accidental_placement_);
493 accidental_placement_ = 00;
495 set_property_on_family(daddy_trans_l_, "localKeySignatureChanges", SCM_EOL);
496 accidental_arr_.clear();
502 New_accidental_engraver::acknowledge_grob (Grob_info info)
504 Note_req * note_l = dynamic_cast <Note_req *> (info.music_cause ());
506 if (note_l && Rhythmic_head::has_interface (info.grob_l_))
508 New_accidental_entry entry ;
509 entry.head_ = info.grob_l_;
510 entry.origin_ = info.origin_trans_l_->daddy_trans_l_;
511 entry.melodic_ = note_l;
513 accidental_arr_.push (entry);
515 else if (Tie::has_interface (info.grob_l_))
517 tie_arr_.push (info.grob_l_);
519 else if (Arpeggio::has_interface (info.grob_l_))
521 arpeggios_.push (info.grob_l_);
527 New_accidental_engraver::process_music ()
529 SCM sig = get_property ("keySignature");
531 /* Detect key sig changes.
532 Update all parents and children
534 if (last_keysig_ != sig)
536 set_property_on_family(daddy_trans_l_, "localKeySignature", sig);
537 set_property_on_family(daddy_trans_l_, "localKeySignatureChanges", SCM_EOL); //This souldn't be neccesary
546 ENTER_DESCRIPTION (New_accidental_engraver,
547 "Make accidentals. Catches note heads, ties and notices key-change
548 events. Due to interaction with ties (which don't come together
549 with note heads), this needs to be in a context higher than Tie_engraver.",
552 "rhythmic-head-interface tie-interface arpeggio-interface",
553 "localKeySignature localKeySignatureChanges extraNatural autoAccidentals autoCautionaries",
554 "localKeySignature localKeySignatureChanges");