2 new-accidental-engraver.cc -- implement new_accidental_engraver
4 (c) 1997--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 Modified 2001--2003 by Rune Zedeler <rz@daimi.au.dk>
7 This is an experimental file - producing correct accidentals but
8 unfortunately ruining the spacing. -rz
16 #include "rhythmic-head.hh"
17 #include "engraver-group-engraver.hh"
18 #include "accidental-placement.hh"
19 #include "side-position-interface.hh"
20 #include "engraver.hh"
21 #include "arpeggio.hh"
24 #include "translator-group.hh"
29 FIXME: should not compute vertical positioning of accidentals, but
30 get them from the noteheads
32 The algorithm for accidentals should be documented, and made
37 struct New_accidental_entry {
39 int number_accidentals_;
40 int number_cautionaries_;
44 Translator_group *origin_;
46 New_accidental_entry();
49 New_accidental_entry::New_accidental_entry()
52 number_accidentals_ = 0;
53 number_cautionaries_ = 0;
61 struct New_accidental_engraver : Engraver {
63 TRANSLATOR_DECLARATIONS (New_accidental_engraver);
64 virtual void process_music ();
65 virtual void acknowledge_grob (Grob_info);
66 virtual void stop_translation_timestep ();
67 virtual void process_acknowledged_grobs ();
68 virtual void finalize ();
69 virtual void process_grobs_first_pass ();
70 virtual void process_grobs_second_pass ();
77 This is not a property, and it is not protected. This poses a
78 very small risk of the value being GC'd from under us.
83 Urgh. Since the accidentals depend on lots of variables, we have to
84 store all information before we can really create the accidentals.
86 Link_array<Grob> arpeggios_;
88 Grob * accidental_placement_;
94 Array<New_accidental_entry> accidentals_;
96 Link_array<Grob> ties_;
102 New_accidental_engraver::New_accidental_engraver ()
104 last_keysig_ = SCM_BOOL_F;
105 accidental_placement_ = 0;
108 /* inserts the source alist into the destination alist, erasing old entries.
109 result is: dest = merged
111 static SCM merge_alists_front_x (SCM src, SCM dest) {
113 dest = merge_alists_front_x(ly_cdr(src),dest);
114 dest = ly_assoc_front_x(dest, ly_caar(src), ly_cdar(src));
119 static void merge_property_on_children (Translator_group * trans,
120 const char * from_sym, const char * to_sym)
122 SCM from = trans->get_property(from_sym);
123 SCM to = trans->get_property(to_sym);
124 to = merge_alists_front_x(from, to);
125 trans->set_property (to_sym, to);
126 trans->set_property (from_sym, SCM_EOL);
127 for (SCM p = trans -> trans_group_list_; gh_pair_p (p); p = ly_cdr(p)) {
128 Translator_group *trg = dynamic_cast<Translator_group*> (unsmob_translator (ly_car (p)));
129 merge_property_on_children(trg, from_sym, to_sym);
133 static void merge_property_on_family (Translator_group * trans,
134 const char * from_sym, const char * to_sym)
136 merge_property_on_children (trans, from_sym, to_sym);
137 trans = trans->daddy_trans_;
140 SCM from = trans->get_property(from_sym);
141 SCM to = trans->get_property(to_sym);
142 to = merge_alists_front_x(from, to);
143 trans->set_property (to_sym, to);
144 trans->set_property (from_sym, SCM_EOL);
145 trans = trans->daddy_trans_;
149 static void set_property_on_children (Translator_group * trans, const char * sym, SCM val)
151 trans->set_property (sym, val);
152 for (SCM p = trans -> trans_group_list_; gh_pair_p (p); p = ly_cdr(p)) {
153 Translator_group *trg = dynamic_cast<Translator_group*> (unsmob_translator (ly_car (p)));
154 set_property_on_children(trg,sym,ly_deep_copy(val));
158 static void set_property_on_family(Translator_group * trans, const char * sym, SCM val)
160 set_property_on_children (trans, sym, val);
161 trans = trans->daddy_trans_;
164 trans -> set_property (sym, ly_deep_copy (val));
165 trans = trans->daddy_trans_;
170 calculates the number of accidentals on basis of the current local key sig
172 Returns number of accidentals (0, 1 or 2).
173 Negative (-1 or -2) if accidental has changed.
177 number_accidentals (SCM sig, Music * note, Pitch *pitch, SCM curbarnum, SCM lazyness,
178 bool ignore_octave_b)
180 int n = pitch->get_notename ();
181 int o = pitch->get_octave ();
182 int a = pitch->get_alteration ();
183 int curbarnum_i = gh_scm2int (curbarnum);
188 prev = ly_assoc_cdr (gh_int2scm (n), sig);
190 prev = scm_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), sig);
192 /* should really be true unless prev == SCM_BOOL_F */
193 if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
195 accbarnum_i = gh_scm2int (ly_cddr (prev));
196 prev = gh_cons (ly_car (prev), ly_cadr (prev));
199 /* If an accidental was not found or the accidental was too old */
200 if (prev == SCM_BOOL_F ||
201 (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
202 prev = scm_assoc (gh_int2scm (n), sig);
205 SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
207 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
211 && !to_boolean (note->get_mus_property ("force-accidental"))
212 && gh_number_p (prev_acc))
214 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
219 return a == p ? num : -num;
223 number_accidentals (Music * note, Pitch *pitch, Translator_group * origin,
224 SCM accidentals, SCM curbarnum)
229 if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
230 warning (_f ("Accidental typesetting list must begin with context-name: %s",
231 ly_scm2string (ly_car (accidentals)).to_str0 ()));
233 while (gh_pair_p (accidentals) && origin)
235 // If pair then it is a new accidentals typesetting rule to be checked
236 if (gh_pair_p (ly_car (accidentals)))
238 SCM type = gh_caar (accidentals);
239 SCM lazyness = gh_cdar (accidentals);
240 SCM localsig = origin->get_property ("localKeySignature");
243 gh_eq_p (ly_symbol2scm ("same-octave"), type);
245 gh_eq_p (ly_symbol2scm ("any-octave"), type);
247 if (same_octave_b || any_octave_b)
249 int n = number_accidentals
250 (localsig, note, pitch, curbarnum, lazyness, any_octave_b);
251 diff = diff || (n < 0);
252 number = max (number, abs (n));
255 warning (_f ("unknown accidental typesetting: %s. Ignored",
256 ly_symbol2string (type).to_str0 ()));
261 if symbol then it is a context name. Scan parent contexts to find it.
263 else if (gh_symbol_p (ly_car (accidentals)))
265 SCM context = ly_car (accidentals);
267 while (origin && !origin->is_alias (context))
268 origin = origin->daddy_trans_;
271 warning (_f ("Symbol is not a parent context: %s. Ignored",
272 ly_symbol2string (context).to_str0 ()));
274 else warning (_f ("Accidental typesetting must be pair or context-name: %s",
275 ly_scm2string (ly_car (accidentals)).to_str0 ()));
277 accidentals = ly_cdr (accidentals);
279 return diff ? -number : number;
284 Perhaps one should join the two functions into one function taking an
286 OTOH even though code would be smaller, spaghetti-level would increase.
289 New_accidental_engraver::process_grobs_first_pass ()
291 SCM accidentals = get_property ("autoAccidentals");
292 SCM cautionaries = get_property ("autoCautionaries");
293 SCM barnum = get_property ("currentBarNumber");
295 for (int i = 0; i < accidentals_.size (); i++)
297 if (accidentals_[i].pass_done_ >= 1)
299 accidentals_[i].pass_done_ = 1;
301 Grob * support = accidentals_[i].head_;
302 Music * note = accidentals_[i].melodic_;
303 Translator_group * origin = accidentals_[i].origin_;
304 Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
307 num = number_accidentals (note, pitch, origin, accidentals, barnum);
308 accidentals_[i].number_accidentals_ = abs(num);
309 accidentals_[i].different_ = num<0;
311 num = number_accidentals (note, pitch, origin, cautionaries, barnum);
312 accidentals_[i].number_cautionaries_ = abs(num);
313 accidentals_[i].different_ = accidentals_[i].different_ || num<0;
315 bool tie_changes = false;
316 for (int j = 0; j < ties_.size (); j++)
317 if (support == Tie::head (ties_[j], RIGHT))
318 tie_changes = accidentals_[i].different_;
319 int n = pitch->get_notename ();
320 int o = pitch->get_octave ();
321 int a = pitch->get_alteration ();
322 SCM o_s = gh_int2scm (o);
323 SCM n_s = gh_int2scm (n);
324 SCM on_s = gh_cons (o_s,n_s);
328 SCM sigch = origin->get_property ("localKeySignatureChanges");
332 Remember an alteration that is different both from
333 that of the tied note and of the key signature.
337 alt = gh_int2scm (a);
338 bool other_alt_same_oct = false;
339 bool other_alt_any_oct = false;
340 for (SCM j = sigch; gh_pair_p(j); j = ly_cdr(j)) {
341 SCM entry = ly_car(j);
342 /* if same notename has a different alt already recorded: */
343 if(gh_equal_p(ly_cdar(entry),n_s) && !gh_equal_p(ly_cadr(entry),alt))
345 /* if it is also in same octave */
346 if(gh_equal_p(ly_caar(entry),o_s))
347 other_alt_same_oct = true;
349 other_alt_any_oct = true;
352 if(other_alt_same_oct)
354 sigch = ly_assoc_front_x (sigch, on_s, gh_cons(alt,barnum));
355 if(other_alt_any_oct && !other_alt_same_oct) {
356 sigch = ly_assoc_front_x (sigch, on_s, gh_cons(SCM_BOOL_T,barnum));
358 origin->set_property ("localKeySignatureChanges", sigch);
359 origin = origin->daddy_trans_;
365 New_accidental_engraver::process_grobs_second_pass ()
367 SCM accidentals = get_property ("autoAccidentals");
368 SCM cautionaries = get_property ("autoCautionaries");
369 SCM barnum = get_property ("currentBarNumber");
371 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
372 for (int i = 0; i < accidentals_.size (); i++)
374 if (accidentals_[i].pass_done_ >= 2)
376 accidentals_[i].pass_done_ = 2;
377 Grob * support = accidentals_[i].head_;
378 Music * note = accidentals_[i].melodic_;
379 Translator_group * origin = accidentals_[i].origin_;
381 Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
384 num = number_accidentals (note, pitch, origin, accidentals, barnum);
385 accidentals_[i].number_accidentals_ =
386 max (accidentals_[i].number_accidentals_, abs(num));
387 accidentals_[i].different_ = accidentals_[i].different_ || num<0;
389 num = number_accidentals (note, pitch, origin, cautionaries, barnum);
390 accidentals_[i].number_cautionaries_ =
391 max (accidentals_[i].number_cautionaries_, abs(num));
392 accidentals_[i].different_ = accidentals_[i].different_ || num<0;
395 bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
397 if (accidentals_[i].number_cautionaries_ >accidentals_[i].number_accidentals_ )
399 num = accidentals_[i].number_cautionaries_;
403 num = accidentals_[i].number_accidentals_;
405 bool tie_changes = false;
406 Grob *tie_break_reminder = 0;
407 for (int j = 0; j < ties_.size (); j++)
408 if (support == Tie::head (ties_[j], RIGHT))
410 tie_changes = accidentals_[i].different_;
411 tie_break_reminder = ties_[j];
416 Grob * a = make_item ("Accidental");
417 a->set_parent (support, Y_AXIS);
419 if (!accidental_placement_)
421 accidental_placement_ = make_item ("AccidentalPlacement");
422 announce_grob (accidental_placement_, a->self_scm());
425 Accidental_placement::add_accidental (accidental_placement_, a);
426 announce_grob (a, SCM_EOL);
429 SCM accs = gh_cons (gh_int2scm (pitch->get_alteration ()), SCM_EOL);
430 if (num == 2 && extra_natural_b)
431 accs = gh_cons (gh_int2scm (0), accs);
435 a->set_grob_property ("cautionary", SCM_BOOL_T);
438 if (tie_break_reminder)
440 a->set_grob_property ("tie", tie_break_reminder->self_scm());
444 support->set_grob_property ("accidental-grob", a->self_scm ());
446 a->set_grob_property ("accidentals", accs);
447 accidentals_[i].accidental_ = a;
449 We add the accidentals to the support of the arpeggio, so it is
450 put left of the accidentals.
452 for (int i = 0; i < arpeggios_.size (); i++)
453 Side_position_interface::add_support (arpeggios_[i], a);
459 New_accidental_engraver::process_acknowledged_grobs ()
461 if (accidentals_.size () && accidentals_.top().pass_done_ < 1)
462 process_grobs_first_pass ();
466 New_accidental_engraver::finalize ()
472 New_accidental_engraver::stop_translation_timestep ()
474 merge_property_on_family(daddy_trans_, "localKeySignatureChanges", "localKeySignature");
475 if (accidentals_.size () && accidentals_.top().pass_done_ < 2)
476 process_grobs_second_pass ();
478 for (int i = 0; i < accidentals_.size(); i++)
480 Grob *a = accidentals_[i].accidental_;
487 if (accidental_placement_)
488 typeset_grob(accidental_placement_);
489 accidental_placement_ = 00;
491 set_property_on_family(daddy_trans_, "localKeySignatureChanges", SCM_EOL);
492 accidentals_.clear();
498 New_accidental_engraver::acknowledge_grob (Grob_info info)
500 Music * note = info.music_cause ();
503 && note->is_mus_type ("note-event")
504 && Rhythmic_head::has_interface (info.grob_))
506 New_accidental_entry entry ;
507 entry.head_ = info.grob_;
508 entry.origin_ = info.origin_trans_->daddy_trans_;
509 entry.melodic_ = note;
511 accidentals_.push (entry);
513 else if (Tie::has_interface (info.grob_))
515 ties_.push (info.grob_);
517 else if (Arpeggio::has_interface (info.grob_))
519 arpeggios_.push (info.grob_);
525 New_accidental_engraver::process_music ()
527 SCM sig = get_property ("keySignature");
529 /* Detect key sig changes.
530 Update all parents and children
532 if (last_keysig_ != sig)
534 set_property_on_family(daddy_trans_, "localKeySignature", sig);
535 set_property_on_family(daddy_trans_, "localKeySignatureChanges", SCM_EOL); //This souldn't be neccesary
544 ENTER_DESCRIPTION (New_accidental_engraver,
545 "Make accidentals. Catches note heads, ties and notices key-change "
546 "events. Due to interaction with ties (which don't come together "
547 "with note heads), this needs to be in a context higher than Tie_engraver.",
551 "rhythmic-head-interface tie-interface arpeggio-interface",
552 "localKeySignature localKeySignatureChanges extraNatural autoAccidentals autoCautionaries",
553 "localKeySignature localKeySignatureChanges");