release commit
[lilypond.git] / lily / new-accidental-engraver.cc
blob4d2cad0b29b6714740b892ab9ff2f5504ad2ff16
1 /*
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
12 #include "event.hh"
14 #include "item.hh"
15 #include "tie.hh"
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"
22 #include "warn.hh"
24 #include "translator-group.hh"
26 /**
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
33 tweakable.
37 struct New_accidental_entry {
38 int pass_done_;
39 int number_accidentals_;
40 int number_cautionaries_;
41 bool different_;
42 Music * melodic_;
43 Grob * accidental_;
44 Translator_group *origin_;
45 Grob* head_;
46 New_accidental_entry();
49 New_accidental_entry::New_accidental_entry()
51 pass_done_ = 0;
52 number_accidentals_ = 0;
53 number_cautionaries_ = 0;
54 different_ = false;
55 melodic_ = 0;
56 accidental_ = 0;
57 origin_ = 0;
58 head_ = 0;
61 struct New_accidental_engraver : Engraver {
62 protected:
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 initialize ();
68 virtual void process_acknowledged_grobs ();
69 virtual void finalize ();
70 virtual void process_grobs_first_pass ();
71 virtual void process_grobs_second_pass ();
73 public:
76 TODO -> property.
78 This is not a property, and it is not protected. This poses a
79 very small risk of the value being GC'd from under us.
81 SCM last_keysig_;
84 Urgh. Since the accidentals depend on lots of variables, we have to
85 store all information before we can really create the accidentals.
87 Link_array<Grob> arpeggios_;
89 Grob * accidental_placement_;
93 The next
95 Array<New_accidental_entry> accidentals_;
97 Link_array<Grob> ties_;
103 New_accidental_engraver::New_accidental_engraver ()
105 accidental_placement_ = 0;
106 last_keysig_ = SCM_EOL;
109 /* inserts the source alist into the destination alist, erasing old entries.
110 result is: dest = merged
112 static SCM merge_alists_front_x (SCM src, SCM dest) {
113 if(gh_pair_p(src)) {
114 dest = merge_alists_front_x(ly_cdr(src),dest);
115 dest = ly_assoc_front_x(dest, ly_caar(src), ly_cdar(src));
117 return dest;
120 static void merge_property_on_children (Translator_group * trans,
121 const char * from_sym, const char * to_sym)
123 SCM from = trans->get_property(from_sym);
124 SCM to = trans->get_property(to_sym);
125 to = merge_alists_front_x(from, to);
126 trans->set_property (to_sym, to);
127 trans->set_property (from_sym, SCM_EOL);
128 for (SCM p = trans -> trans_group_list_; gh_pair_p (p); p = ly_cdr(p)) {
129 Translator_group *trg = dynamic_cast<Translator_group*> (unsmob_translator (ly_car (p)));
130 merge_property_on_children(trg, from_sym, to_sym);
134 static void merge_property_on_family (Translator_group * trans,
135 const char * from_sym, const char * to_sym)
137 merge_property_on_children (trans, from_sym, to_sym);
138 trans = trans->daddy_trans_;
139 while (trans)
141 SCM from = trans->get_property(from_sym);
142 SCM to = trans->get_property(to_sym);
143 to = merge_alists_front_x(from, to);
144 trans->set_property (to_sym, to);
145 trans->set_property (from_sym, SCM_EOL);
146 trans = trans->daddy_trans_;
150 static void set_property_on_children (Translator_group * trans, const char * sym, SCM val)
152 trans->set_property (sym, val);
153 for (SCM p = trans -> trans_group_list_; gh_pair_p (p); p = ly_cdr(p)) {
154 Translator_group *trg = dynamic_cast<Translator_group*> (unsmob_translator (ly_car (p)));
155 set_property_on_children(trg,sym,ly_deep_copy(val));
159 static void set_property_on_family(Translator_group * trans, const char * sym, SCM val)
161 set_property_on_children (trans, sym, val);
162 trans = trans->daddy_trans_;
163 while (trans)
165 trans -> set_property (sym, ly_deep_copy (val));
166 trans = trans->daddy_trans_;
170 void
171 New_accidental_engraver::initialize ()
173 // to ensure that process_music will initialize last_keysig_
174 last_keysig_ = SCM_BOOL_F;
178 calculates the number of accidentals on basis of the current local key sig
179 (passed as argument)
180 Returns number of accidentals (0, 1 or 2).
181 Negative (-1 or -2) if accidental has changed.
184 static int
185 number_accidentals (SCM sig, Music * note, Pitch *pitch, SCM curbarnum, SCM lazyness,
186 bool ignore_octave_b)
188 int n = pitch->get_notename ();
189 int o = pitch->get_octave ();
190 int a = pitch->get_alteration ();
191 int curbarnum_i = gh_scm2int (curbarnum);
192 int accbarnum_i = 0;
194 SCM prev;
195 if (ignore_octave_b)
196 prev = ly_assoc_cdr (gh_int2scm (n), sig);
197 else
198 prev = scm_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), sig);
200 /* should really be true unless prev == SCM_BOOL_F */
201 if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
203 accbarnum_i = gh_scm2int (ly_cddr (prev));
204 prev = gh_cons (ly_car (prev), ly_cadr (prev));
207 /* If an accidental was not found or the accidental was too old */
208 if (prev == SCM_BOOL_F ||
209 (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
210 prev = scm_assoc (gh_int2scm (n), sig);
213 SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
215 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
217 int num;
218 if (a == p
219 && !to_boolean (note->get_mus_property ("force-accidental"))
220 && gh_number_p (prev_acc))
221 num = 0;
222 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
223 num = 2;
224 else
225 num = 1;
227 return a == p ? num : -num;
230 static int
231 number_accidentals (Music * note, Pitch *pitch, Translator_group * origin,
232 SCM accidentals, SCM curbarnum)
234 int number = 0;
236 bool diff = false;
237 if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
238 warning (_f ("Accidental typesetting list must begin with context-name: %s",
239 ly_scm2string (ly_car (accidentals)).to_str0 ()));
241 while (gh_pair_p (accidentals) && origin)
243 // If pair then it is a new accidentals typesetting rule to be checked
244 if (gh_pair_p (ly_car (accidentals)))
246 SCM type = gh_caar (accidentals);
247 SCM lazyness = gh_cdar (accidentals);
248 SCM localsig = origin->get_property ("localKeySignature");
250 bool same_octave_b =
251 gh_eq_p (ly_symbol2scm ("same-octave"), type);
252 bool any_octave_b =
253 gh_eq_p (ly_symbol2scm ("any-octave"), type);
255 if (same_octave_b || any_octave_b)
257 int n = number_accidentals
258 (localsig, note, pitch, curbarnum, lazyness, any_octave_b);
259 diff = diff || (n < 0);
260 number = max (number, abs (n));
262 else
263 warning (_f ("unknown accidental typesetting: %s. Ignored",
264 ly_symbol2string (type).to_str0 ()));
269 if symbol then it is a context name. Scan parent contexts to find it.
271 else if (gh_symbol_p (ly_car (accidentals)))
273 SCM context = ly_car (accidentals);
275 while (origin && !origin->is_alias_b (context))
276 origin = origin->daddy_trans_;
278 if (!origin)
279 warning (_f ("Symbol is not a parent context: %s. Ignored",
280 ly_symbol2string (context).to_str0 ()));
282 else warning (_f ("Accidental typesetting must be pair or context-name: %s",
283 ly_scm2string (ly_car (accidentals)).to_str0 ()));
285 accidentals = ly_cdr (accidentals);
287 return diff ? -number : number;
292 Perhaps one should join the two functions into one function taking an
293 argument (pass).
294 OTOH even though code would be smaller, spaghetti-level would increase.
296 void
297 New_accidental_engraver::process_grobs_first_pass ()
299 SCM accidentals = get_property ("autoAccidentals");
300 SCM cautionaries = get_property ("autoCautionaries");
301 SCM barnum = get_property ("currentBarNumber");
303 for (int i = 0; i < accidentals_.size (); i++)
305 if (accidentals_[i].pass_done_ >= 1)
306 continue;
307 accidentals_[i].pass_done_ = 1;
309 Grob * support = accidentals_[i].head_;
310 Music * note = accidentals_[i].melodic_;
311 Translator_group * origin = accidentals_[i].origin_;
312 Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
314 int num;
315 num = number_accidentals (note, pitch, origin, accidentals, barnum);
316 accidentals_[i].number_accidentals_ = abs(num);
317 accidentals_[i].different_ = num<0;
319 num = number_accidentals (note, pitch, origin, cautionaries, barnum);
320 accidentals_[i].number_cautionaries_ = abs(num);
321 accidentals_[i].different_ = accidentals_[i].different_ || num<0;
323 bool tie_changes = false;
324 for (int j = 0; j < ties_.size (); j++)
325 if (support == Tie::head (ties_[j], RIGHT))
326 tie_changes = accidentals_[i].different_;
327 int n = pitch->get_notename ();
328 int o = pitch->get_octave ();
329 int a = pitch->get_alteration ();
330 SCM o_s = gh_int2scm (o);
331 SCM n_s = gh_int2scm (n);
332 SCM on_s = gh_cons (o_s,n_s);
334 while (origin)
336 SCM sigch = origin->get_property ("localKeySignatureChanges");
337 SCM alt;
338 if (tie_changes)
340 Remember an alteration that is different both from
341 that of the tied note and of the key signature.
343 alt = SCM_BOOL_T;
344 else
345 alt = gh_int2scm (a);
346 bool other_alt_same_oct = false;
347 bool other_alt_any_oct = false;
348 for (SCM j = sigch; gh_pair_p(j); j = ly_cdr(j)) {
349 SCM entry = ly_car(j);
350 /* if same notename has a different alt already recorded: */
351 if(gh_equal_p(ly_cdar(entry),n_s) && !gh_equal_p(ly_cadr(entry),alt))
353 /* if it is also in same octave */
354 if(gh_equal_p(ly_caar(entry),o_s))
355 other_alt_same_oct = true;
356 else
357 other_alt_any_oct = true;
360 if(other_alt_same_oct)
361 alt = SCM_BOOL_T;
362 sigch = ly_assoc_front_x (sigch, on_s, gh_cons(alt,barnum));
363 if(other_alt_any_oct && !other_alt_same_oct) {
364 sigch = ly_assoc_front_x (sigch, on_s, gh_cons(SCM_BOOL_T,barnum));
366 origin->set_property ("localKeySignatureChanges", sigch);
367 origin = origin->daddy_trans_;
372 void
373 New_accidental_engraver::process_grobs_second_pass ()
375 SCM accidentals = get_property ("autoAccidentals");
376 SCM cautionaries = get_property ("autoCautionaries");
377 SCM barnum = get_property ("currentBarNumber");
379 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
380 for (int i = 0; i < accidentals_.size (); i++)
382 if (accidentals_[i].pass_done_ >= 2)
383 continue;
384 accidentals_[i].pass_done_ = 2;
385 Grob * support = accidentals_[i].head_;
386 Music * note = accidentals_[i].melodic_;
387 Translator_group * origin = accidentals_[i].origin_;
389 Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
391 int num;
392 num = number_accidentals (note, pitch, origin, accidentals, barnum);
393 accidentals_[i].number_accidentals_ =
394 max (accidentals_[i].number_accidentals_, abs(num));
395 accidentals_[i].different_ = accidentals_[i].different_ || num<0;
397 num = number_accidentals (note, pitch, origin, cautionaries, barnum);
398 accidentals_[i].number_cautionaries_ =
399 max (accidentals_[i].number_cautionaries_, abs(num));
400 accidentals_[i].different_ = accidentals_[i].different_ || num<0;
403 bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
405 if (accidentals_[i].number_cautionaries_ >accidentals_[i].number_accidentals_ )
407 num = accidentals_[i].number_cautionaries_;
408 cautionary = true;
410 else
411 num = accidentals_[i].number_accidentals_;
413 bool tie_changes = false;
414 Grob *tie_break_reminder = 0;
415 for (int j = 0; j < ties_.size (); j++)
416 if (support == Tie::head (ties_[j], RIGHT))
418 tie_changes = accidentals_[i].different_;
419 tie_break_reminder = ties_[j];
422 if (num)
424 Grob * a = new Item (get_property ("Accidental"));
425 a->set_parent (support, Y_AXIS);
427 if (!accidental_placement_)
429 accidental_placement_ = new Item (get_property ("AccidentalPlacement"));
430 announce_grob (accidental_placement_, a->self_scm());
433 Accidental_placement::add_accidental (accidental_placement_, a);
434 announce_grob (a, SCM_EOL);
437 SCM accs = gh_cons (gh_int2scm (pitch->get_alteration ()), SCM_EOL);
438 if (num == 2 && extra_natural_b)
439 accs = gh_cons (gh_int2scm (0), accs);
441 if (cautionary)
443 a->set_grob_property ("cautionary", SCM_BOOL_T);
446 if (tie_break_reminder)
448 a->set_grob_property ("tie", tie_break_reminder->self_scm());
452 support->set_grob_property ("accidental-grob", a->self_scm ());
454 a->set_grob_property ("accidentals", accs);
455 accidentals_[i].accidental_ = a;
457 We add the accidentals to the support of the arpeggio, so it is
458 put left of the accidentals.
460 for (int i = 0; i < arpeggios_.size (); i++)
461 Side_position_interface::add_support (arpeggios_[i], a);
466 void
467 New_accidental_engraver::process_acknowledged_grobs ()
469 if (accidentals_.size () && accidentals_.top().pass_done_ < 1)
470 process_grobs_first_pass ();
473 void
474 New_accidental_engraver::finalize ()
479 void
480 New_accidental_engraver::stop_translation_timestep ()
482 merge_property_on_family(daddy_trans_, "localKeySignatureChanges", "localKeySignature");
483 if (accidentals_.size () && accidentals_.top().pass_done_ < 2)
484 process_grobs_second_pass ();
486 for (int i = 0; i < accidentals_.size(); i++)
488 Grob *a = accidentals_[i].accidental_;
489 if (a)
491 typeset_grob (a);
495 if (accidental_placement_)
496 typeset_grob(accidental_placement_);
497 accidental_placement_ = 00;
499 set_property_on_family(daddy_trans_, "localKeySignatureChanges", SCM_EOL);
500 accidentals_.clear();
501 arpeggios_.clear ();
502 ties_.clear ();
505 void
506 New_accidental_engraver::acknowledge_grob (Grob_info info)
508 Music * note = info.music_cause ();
510 if (note
511 && note->is_mus_type ("note-event")
512 && Rhythmic_head::has_interface (info.grob_))
514 New_accidental_entry entry ;
515 entry.head_ = info.grob_;
516 entry.origin_ = info.origin_trans_->daddy_trans_;
517 entry.melodic_ = note;
519 accidentals_.push (entry);
521 else if (Tie::has_interface (info.grob_))
523 ties_.push (info.grob_);
525 else if (Arpeggio::has_interface (info.grob_))
527 arpeggios_.push (info.grob_);
532 void
533 New_accidental_engraver::process_music ()
535 SCM sig = get_property ("keySignature");
537 /* Detect key sig changes.
538 Update all parents and children
540 if (last_keysig_ != sig)
542 set_property_on_family(daddy_trans_, "localKeySignature", sig);
543 set_property_on_family(daddy_trans_, "localKeySignatureChanges", SCM_EOL); //This souldn't be neccesary
544 last_keysig_ = sig;
552 ENTER_DESCRIPTION (New_accidental_engraver,
553 "Make accidentals. Catches note heads, ties and notices key-change "
554 "events. Due to interaction with ties (which don't come together "
555 "with note heads), this needs to be in a context higher than Tie_engraver.",
557 "Accidental",
558 /* accepts */ "",
559 "rhythmic-head-interface tie-interface arpeggio-interface",
560 "localKeySignature localKeySignatureChanges extraNatural autoAccidentals autoCautionaries",
561 "localKeySignature localKeySignatureChanges");