* stepmake/stepmake/metafont-rules.make: backport 1.7 fixes.
[lilypond.git] / lily / new-accidental-engraver.cc
blob9f3b83d98533cf787cea1e2c3eba73a5469edcfd
1 /*
2 new-accidental-engraver.cc -- implement new_accidental_engraver
4 (c) 1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 Modified 2001--2002 by Rune Zedeler <rz@daimi.au.dk>
7 This is an experimental file - producing correct accidentals but
8 unfortunately ruining the spacing. -rz
9 */
11 #include "musical-request.hh"
12 #include "command-request.hh"
13 #include "item.hh"
14 #include "tie.hh"
15 #include "rhythmic-head.hh"
16 #include "engraver-group-engraver.hh"
17 #include "accidental-placement.hh"
18 #include "side-position-interface.hh"
19 #include "engraver.hh"
20 #include "arpeggio.hh"
21 #include "warn.hh"
23 #include "translator-group.hh"
25 /**
28 FIXME: should not compute vertical positioning of accidentals, but
29 get them from the noteheads
31 The algorithm for accidentals should be documented, and made
32 tweakable.
36 struct New_accidental_entry {
37 int pass_done_;
38 int number_accidentals_;
39 int number_cautionaries_;
40 bool different_;
41 Note_req * melodic_;
42 Grob * accidental_;
43 Translator_group *origin_;
44 Grob* head_;
45 New_accidental_entry();
48 New_accidental_entry::New_accidental_entry()
50 pass_done_ = 0;
51 number_accidentals_ = 0;
52 number_cautionaries_ = 0;
53 different_ = false;
54 melodic_ = 0;
55 accidental_ = 0;
56 origin_ = 0;
57 head_ = 0;
60 struct New_accidental_engraver : Engraver {
61 protected:
62 TRANSLATOR_DECLARATIONS (New_accidental_engraver);
63 virtual void process_music ();
64 virtual void acknowledge_grob (Grob_info);
65 virtual void stop_translation_timestep ();
66 virtual void initialize ();
67 virtual void process_acknowledged_grobs ();
68 virtual void finalize ();
69 virtual void process_grobs_first_pass ();
70 virtual void process_grobs_second_pass ();
72 public:
75 TODO -> property.
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.
80 SCM last_keysig_;
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_;
92 The next
94 Array<New_accidental_entry> accidentals_;
96 Link_array<Grob> ties_;
102 New_accidental_engraver::New_accidental_engraver ()
104 accidental_placement_ = 0;
105 last_keysig_ = SCM_EOL;
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) {
112 if(gh_pair_p(src)) {
113 dest = merge_alists_front_x(ly_cdr(src),dest);
114 dest = ly_assoc_front_x(dest, ly_caar(src), ly_cdar(src));
116 return dest;
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_;
138 while (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_;
162 while (trans)
164 trans -> set_property (sym, ly_deep_copy (val));
165 trans = trans->daddy_trans_;
169 void
170 New_accidental_engraver::initialize ()
172 // to ensure that process_music will initialize last_keysig_
173 last_keysig_ = SCM_BOOL_F;
177 calculates the number of accidentals on basis of the current local key sig
178 (passed as argument)
179 Returns number of accidentals (0, 1 or 2).
180 Negative (-1 or -2) if accidental has changed.
183 static int
184 number_accidentals (SCM sig, Note_req * note, Pitch *pitch, SCM curbarnum, SCM lazyness,
185 bool ignore_octave_b)
187 int n = pitch->notename_;
188 int o = pitch->octave_;
189 int a = pitch->alteration_;
190 int curbarnum_i = gh_scm2int (curbarnum);
191 int accbarnum_i = 0;
193 SCM prev;
194 if (ignore_octave_b)
195 prev = ly_assoc_cdr (gh_int2scm (n), sig);
196 else
197 prev = gh_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), sig);
199 /* should really be true unless prev == SCM_BOOL_F */
200 if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
202 accbarnum_i = gh_scm2int (ly_cddr (prev));
203 prev = gh_cons (ly_car (prev), ly_cadr (prev));
206 /* If an accidental was not found or the accidental was too old */
207 if (prev == SCM_BOOL_F ||
208 (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
209 prev = gh_assoc (gh_int2scm (n), sig);
212 SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
214 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
216 int num;
217 if (a == p
218 && !to_boolean (note->get_mus_property ("force-accidental"))
219 && gh_number_p (prev_acc))
220 num = 0;
221 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
222 num = 2;
223 else
224 num = 1;
226 return a == p ? num : -num;
229 static int
230 number_accidentals (Note_req * note, Pitch *pitch, Translator_group * origin,
231 SCM accidentals, SCM curbarnum)
233 int number = 0;
235 bool diff = false;
236 if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
237 warning (_f ("Accidental typesetting list must begin with context-name: %s",
238 ly_scm2string (ly_car (accidentals)).to_str0 ()));
240 while (gh_pair_p (accidentals) && origin)
242 // If pair then it is a new accidentals typesetting rule to be checked
243 if (gh_pair_p (ly_car (accidentals)))
245 SCM type = gh_caar (accidentals);
246 SCM lazyness = gh_cdar (accidentals);
247 SCM localsig = origin->get_property ("localKeySignature");
249 bool same_octave_b =
250 gh_eq_p (ly_symbol2scm ("same-octave"), type);
251 bool any_octave_b =
252 gh_eq_p (ly_symbol2scm ("any-octave"), type);
254 if (same_octave_b || any_octave_b)
256 int n = number_accidentals
257 (localsig, note, pitch, curbarnum, lazyness, any_octave_b);
258 diff = diff || (n < 0);
259 number = max (number, abs (n));
261 else
262 warning (_f ("unknown accidental typesetting: %s. Ignored",
263 ly_symbol2string (type).to_str0 ()));
268 if symbol then it is a context name. Scan parent contexts to find it.
270 else if (gh_symbol_p (ly_car (accidentals)))
272 String context = ly_symbol2string (ly_car (accidentals));
274 while (origin && !origin->is_alias_b (context))
275 origin = origin->daddy_trans_;
277 if (!origin)
278 warning (_f ("Symbol is not a parent context: %s. Ignored",
279 context.to_str0 ()));
281 else warning (_f ("Accidental typesetting must be pair or context-name: %s",
282 ly_scm2string (ly_car (accidentals)).to_str0 ()));
284 accidentals = ly_cdr (accidentals);
286 return diff ? -number : number;
291 Perhaps one should join the two functions into one function taking an
292 argument (pass).
293 OTOH even though code would be smaller, spaghetti-level would increase.
295 void
296 New_accidental_engraver::process_grobs_first_pass ()
298 SCM accidentals = get_property ("autoAccidentals");
299 SCM cautionaries = get_property ("autoCautionaries");
300 SCM barnum = get_property ("currentBarNumber");
302 for (int i = 0; i < accidentals_.size (); i++)
304 if (accidentals_[i].pass_done_ >= 1)
305 continue;
306 accidentals_[i].pass_done_ = 1;
308 Grob * support = accidentals_[i].head_;
309 Note_req * note = accidentals_[i].melodic_;
310 Translator_group * origin = accidentals_[i].origin_;
311 Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
313 int num;
314 num = number_accidentals (note, pitch, origin, accidentals, barnum);
315 accidentals_[i].number_accidentals_ = abs(num);
316 accidentals_[i].different_ = num<0;
318 num = number_accidentals (note, pitch, origin, cautionaries, barnum);
319 accidentals_[i].number_cautionaries_ = abs(num);
320 accidentals_[i].different_ = accidentals_[i].different_ || num<0;
322 bool tie_changes = false;
323 for (int j = 0; j < ties_.size (); j++)
324 if (support == Tie::head (ties_[j], RIGHT))
325 tie_changes = accidentals_[i].different_;
326 int n = pitch->notename_;
327 int o = pitch->octave_;
328 int a = pitch->alteration_;
329 SCM o_s = gh_int2scm (o);
330 SCM n_s = gh_int2scm (n);
331 SCM on_s = gh_cons (o_s,n_s);
333 while (origin)
335 SCM sigch = origin->get_property ("localKeySignatureChanges");
336 SCM alt;
337 if (tie_changes)
339 Remember an alteration that is different both from
340 that of the tied note and of the key signature.
342 alt = SCM_BOOL_T;
343 else
344 alt = gh_int2scm (a);
345 bool other_alt_same_oct = false;
346 bool other_alt_any_oct = false;
347 for (SCM j = sigch; gh_pair_p(j); j = ly_cdr(j)) {
348 SCM entry = ly_car(j);
349 /* if same notename has a different alt already recorded: */
350 if(gh_equal_p(ly_cdar(entry),n_s) && !gh_equal_p(ly_cadr(entry),alt))
352 /* if it is also in same octave */
353 if(gh_equal_p(ly_caar(entry),o_s))
354 other_alt_same_oct = true;
355 else
356 other_alt_any_oct = true;
359 if(other_alt_same_oct)
360 alt = SCM_BOOL_T;
361 sigch = ly_assoc_front_x (sigch, on_s, gh_cons(alt,barnum));
362 if(other_alt_any_oct && !other_alt_same_oct) {
363 sigch = ly_assoc_front_x (sigch, on_s, gh_cons(SCM_BOOL_T,barnum));
365 origin->set_property ("localKeySignatureChanges", sigch);
366 origin = origin->daddy_trans_;
371 void
372 New_accidental_engraver::process_grobs_second_pass ()
374 SCM accidentals = get_property ("autoAccidentals");
375 SCM cautionaries = get_property ("autoCautionaries");
376 SCM barnum = get_property ("currentBarNumber");
378 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
379 for (int i = 0; i < accidentals_.size (); i++)
381 if (accidentals_[i].pass_done_ >= 2)
382 continue;
383 accidentals_[i].pass_done_ = 2;
384 Grob * support = accidentals_[i].head_;
385 Note_req * note = accidentals_[i].melodic_;
386 Translator_group * origin = accidentals_[i].origin_;
388 Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
390 int num;
391 num = number_accidentals (note, pitch, origin, accidentals, barnum);
392 accidentals_[i].number_accidentals_ =
393 max (accidentals_[i].number_accidentals_, abs(num));
394 accidentals_[i].different_ = accidentals_[i].different_ || num<0;
396 num = number_accidentals (note, pitch, origin, cautionaries, barnum);
397 accidentals_[i].number_cautionaries_ =
398 max (accidentals_[i].number_cautionaries_, abs(num));
399 accidentals_[i].different_ = accidentals_[i].different_ || num<0;
402 bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
404 if (accidentals_[i].number_cautionaries_ >accidentals_[i].number_accidentals_ )
406 num = accidentals_[i].number_cautionaries_;
407 cautionary = true;
409 else
410 num = accidentals_[i].number_accidentals_;
412 bool tie_changes = false;
413 Grob *tie_break_reminder = 0;
414 for (int j = 0; j < ties_.size (); j++)
415 if (support == Tie::head (ties_[j], RIGHT))
417 tie_changes = accidentals_[i].different_;
418 tie_break_reminder = ties_[j];
421 if (num)
423 Grob * a = new Item (get_property ("Accidental"));
424 a->set_parent (support, Y_AXIS);
426 if (!accidental_placement_)
428 accidental_placement_ = new Item (get_property ("AccidentalPlacement"));
429 announce_grob (accidental_placement_, a->self_scm());
432 Accidental_placement::add_accidental (accidental_placement_, a);
433 announce_grob (a, SCM_EOL);
436 SCM accs = gh_cons (gh_int2scm (pitch->alteration_), SCM_EOL);
437 if (num == 2 && extra_natural_b)
438 accs = gh_cons (gh_int2scm (0), accs);
440 if (cautionary)
442 a->set_grob_property ("cautionary", SCM_BOOL_T);
445 if (tie_break_reminder)
447 a->set_grob_property ("tie", tie_break_reminder->self_scm());
451 support->set_grob_property ("accidental-grob", a->self_scm ());
453 a->set_grob_property ("accidentals", accs);
454 accidentals_[i].accidental_ = a;
456 We add the accidentals to the support of the arpeggio, so it is
457 put left of the accidentals.
459 for (int i = 0; i < arpeggios_.size (); i++)
460 Side_position_interface::add_support (arpeggios_[i], a);
465 void
466 New_accidental_engraver::process_acknowledged_grobs ()
468 if (accidentals_.size () && accidentals_.top().pass_done_ < 1)
469 process_grobs_first_pass ();
472 void
473 New_accidental_engraver::finalize ()
478 void
479 New_accidental_engraver::stop_translation_timestep ()
481 merge_property_on_family(daddy_trans_, "localKeySignatureChanges", "localKeySignature");
482 if (accidentals_.size () && accidentals_.top().pass_done_ < 2)
483 process_grobs_second_pass ();
485 for (int i = 0; i < accidentals_.size(); i++)
487 Grob *a = accidentals_[i].accidental_;
488 if (a)
490 typeset_grob (a);
494 if (accidental_placement_)
495 typeset_grob(accidental_placement_);
496 accidental_placement_ = 00;
498 set_property_on_family(daddy_trans_, "localKeySignatureChanges", SCM_EOL);
499 accidentals_.clear();
500 arpeggios_.clear ();
501 ties_.clear ();
504 void
505 New_accidental_engraver::acknowledge_grob (Grob_info info)
507 Note_req * note = dynamic_cast <Note_req *> (info.music_cause ());
509 if (note && Rhythmic_head::has_interface (info.grob_))
511 New_accidental_entry entry ;
512 entry.head_ = info.grob_;
513 entry.origin_ = info.origin_trans_->daddy_trans_;
514 entry.melodic_ = note;
516 accidentals_.push (entry);
518 else if (Tie::has_interface (info.grob_))
520 ties_.push (info.grob_);
522 else if (Arpeggio::has_interface (info.grob_))
524 arpeggios_.push (info.grob_);
529 void
530 New_accidental_engraver::process_music ()
532 SCM sig = get_property ("keySignature");
534 /* Detect key sig changes.
535 Update all parents and children
537 if (last_keysig_ != sig)
539 set_property_on_family(daddy_trans_, "localKeySignature", sig);
540 set_property_on_family(daddy_trans_, "localKeySignatureChanges", SCM_EOL); //This souldn't be neccesary
541 last_keysig_ = sig;
549 ENTER_DESCRIPTION (New_accidental_engraver,
550 "Make accidentals. Catches note heads, ties and notices key-change
551 events. Due to interaction with ties (which don't come together
552 with note heads), this needs to be in a context higher than Tie_engraver.",
554 "Accidental",
555 "rhythmic-head-interface tie-interface arpeggio-interface",
556 "localKeySignature localKeySignatureChanges extraNatural autoAccidentals autoCautionaries",
557 "localKeySignature localKeySignatureChanges");