*** empty log message ***
[lilypond.git] / lily / accidental-engraver.cc
blob259e340d04dbda171f7dc0a86346a838ef5f52ba
1 /*
2 accidental-engraver.cc -- implement accidental_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Modified 2001--2002 by Rune Zedeler <rz@daimi.au.dk>
8 */
10 #include "accidental-placement.hh"
11 #include "arpeggio.hh"
12 #include "context.hh"
13 #include "engraver.hh"
14 #include "protected-scm.hh"
15 #include "rhythmic-head.hh"
16 #include "side-position-interface.hh"
17 #include "tie.hh"
18 #include "warn.hh"
20 class Accidental_entry
22 public:
23 bool done_;
24 Music *melodic_;
25 Grob *accidental_;
26 Context *origin_;
27 Engraver *origin_trans_;
28 Grob *head_;
29 bool tied_;
31 Accidental_entry ();
34 Accidental_entry::Accidental_entry ()
36 tied_ = false;
37 done_ = false;
38 melodic_ = 0;
39 accidental_ = 0;
40 origin_ = 0;
41 head_ = 0;
44 class Accidental_engraver : public Engraver
46 public:
47 int get_bar_number ();
48 void update_local_key_signature ();
50 protected:
51 TRANSLATOR_DECLARATIONS (Accidental_engraver);
52 virtual void process_music ();
53 virtual void acknowledge_grob (Grob_info);
54 virtual void stop_translation_timestep ();
55 virtual void initialize ();
56 virtual void process_acknowledged_grobs ();
57 virtual void finalize ();
59 virtual void derived_mark () const;
60 public:
61 SCM last_keysig_; // ugh.
63 /* Urgh. Since the accidentals depend on lots of variables, we have
64 to store all information before we can really create the
65 accidentals. */
66 Link_array<Grob> left_objects_;
67 Link_array<Grob> right_objects_;
69 Grob *accidental_placement_;
71 Array<Accidental_entry> accidentals_;
72 Link_array<Spanner> ties_;
76 TODO:
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
81 those.
84 static void
85 set_property_on_children (Context *trans, char const *sym, SCM val)
87 trans->set_property (sym, ly_deep_copy (val));
88 for (SCM p = trans->children_contexts (); scm_is_pair (p); p = scm_cdr (p))
90 Context *trg = unsmob_context (scm_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;
101 void
102 Accidental_engraver::derived_mark () const
104 scm_gc_mark (last_keysig_);
107 void
108 Accidental_engraver::update_local_key_signature ()
110 last_keysig_ = get_property ("keySignature");
111 set_property_on_children (context (), "localKeySignature", last_keysig_);
113 Context *trans = context ()->get_parent_context ();
115 /* Huh. Don't understand what this is good for. --hwn. */
116 while (trans && trans->where_defined (ly_symbol2scm ("localKeySignature")))
118 trans->set_property ("localKeySignature", ly_deep_copy (last_keysig_));
119 trans = trans->get_parent_context ();
123 void
124 Accidental_engraver::initialize ()
126 update_local_key_signature ();
129 /** Calculate the number of accidentals on basis of the current local key
130 sig (passed as argument)
132 * First check step+octave (taking into account barnumbers if necessary).
134 * Then check the global signature (only step).
136 Return number of accidentals (0, 1 or 2). */
138 static bool
139 recent_enough (int bar_number, SCM alteration_def, SCM laziness)
141 if (scm_is_number (alteration_def)
142 || laziness == SCM_BOOL_T)
143 return true;
145 return (bar_number <= scm_to_int (scm_cdr (alteration_def)) + scm_to_int (laziness));
148 static int
149 extract_alteration (SCM alteration_def)
151 if (scm_is_number (alteration_def))
152 return scm_to_int (alteration_def);
153 else if (scm_is_pair (alteration_def))
154 return scm_to_int (scm_car (alteration_def));
155 else if (alteration_def == SCM_BOOL_F)
156 return 0;
157 else
158 assert (0);
159 return 0;
162 bool
163 is_tied (SCM alteration_def)
165 return (alteration_def == SCM_BOOL_T)
166 || (scm_is_pair (alteration_def) && scm_car (alteration_def) == SCM_BOOL_T);
169 static int
170 number_accidentals_from_sig (bool *different, SCM sig, Pitch *pitch,
171 int bar_number, SCM laziness, bool ignore_octave)
173 int n = pitch->get_notename ();
174 int o = pitch->get_octave ();
176 SCM previous_alteration = SCM_BOOL_F;
178 SCM from_same_octave = ly_assoc_get (scm_cons (scm_int2num (o),
179 scm_int2num (n)), sig, SCM_BOOL_F);
180 SCM from_key_signature = ly_assoc_get (scm_int2num (n), sig, SCM_BOOL_F);
181 SCM from_other_octaves = SCM_BOOL_F;
182 for (SCM s = sig; scm_is_pair (s); s = scm_cdr (s))
184 SCM entry = scm_car (s);
185 if (scm_is_pair (scm_car (entry))
186 && scm_cdar (entry) == scm_int2num (n))
187 from_other_octaves = scm_cdr (entry);
190 if (from_same_octave != SCM_BOOL_F
191 && recent_enough (bar_number, from_same_octave, laziness))
193 previous_alteration = from_same_octave;
195 else if (ignore_octave
196 && from_other_octaves != SCM_BOOL_F
197 && recent_enough (bar_number, from_other_octaves, laziness))
199 previous_alteration = from_other_octaves;
201 else if (from_key_signature != SCM_BOOL_F)
203 previous_alteration = from_key_signature;
206 int num = 1;
207 if (is_tied (previous_alteration))
209 num = 1;
210 *different = true;
212 else
214 int prev = extract_alteration (previous_alteration);
215 int alter = pitch->get_alteration ();
217 if (alter == prev)
218 num = 0;
219 else if ((abs (alter) < abs (prev) || prev * alter < 0) && alter != 0)
220 num = 2;
221 *different = (alter != prev);
223 return num;
226 static int
227 number_accidentals (bool *different,
228 Pitch *pitch, Context *origin,
229 SCM accidentals, int bar_number)
231 int number = 0;
233 *different = false;
234 if (scm_is_pair (accidentals) && !scm_is_symbol (scm_car (accidentals)))
235 warning (_f ("accidental typesetting list must begin with context-name: %s",
236 ly_scm2string (scm_car (accidentals)).to_str0 ()));
238 for (; scm_is_pair (accidentals) && origin;
239 accidentals = scm_cdr (accidentals))
241 // If pair then it is a new accidentals typesetting rule to be checked
242 SCM rule = scm_car (accidentals);
243 if (scm_is_pair (rule))
245 SCM type = scm_car (rule);
246 SCM laziness = scm_cdr (rule);
247 SCM localsig = origin->get_property ("localKeySignature");
249 bool same_octave_b
250 = scm_is_eq (ly_symbol2scm ("same-octave"), type);
251 bool any_octave_b
252 = scm_is_eq (ly_symbol2scm ("any-octave"), type);
254 if (same_octave_b || any_octave_b)
256 bool d = false;
257 int n = number_accidentals_from_sig
258 (&d, localsig, pitch, bar_number, laziness, any_octave_b);
259 *different = *different || d;
260 number = max (number, n);
262 else
263 warning (_f ("ignoring unknown accidental: %s",
264 ly_symbol2string (type).to_str0 ()));
267 /* if symbol then it is a context name. Scan parent contexts to
268 find it. */
269 else if (scm_is_symbol (rule))
271 Context *dad = origin;
272 while (dad && !dad->is_alias (rule))
273 dad = dad->get_parent_context ();
275 if (dad)
276 origin = dad;
278 else
279 warning (_f ("pair or context-name expected for accidental rule, found %s",
280 ly_scm2string (rule).to_str0 ()));
283 return number;
287 Accidental_engraver::get_bar_number ()
289 SCM barnum = get_property ("currentBarNumber");
290 SCM smp = get_property ("measurePosition");
292 int bn = robust_scm2int (barnum, 0);
294 Moment mp = robust_scm2moment (smp, Moment (0));
295 if (mp.main_part_ < Rational (0))
296 bn--;
298 return bn;
301 void
302 Accidental_engraver::process_acknowledged_grobs ()
304 if (accidentals_.size () && !accidentals_.top ().done_)
306 SCM accidentals = get_property ("autoAccidentals");
307 SCM cautionaries = get_property ("autoCautionaries");
308 int barnum = get_bar_number ();
310 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
311 for (int i = 0; i < accidentals_.size (); i++)
313 if (accidentals_[i].done_)
314 continue;
315 accidentals_[i].done_ = true;
316 Grob *support = accidentals_[i].head_;
317 Music *note = accidentals_[i].melodic_;
318 Context *origin = accidentals_[i].origin_;
320 Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
321 if (!pitch)
322 continue;
324 bool different = false;
325 bool different_caut = false;
327 int num = number_accidentals (&different,
328 pitch, origin,
329 accidentals, barnum);
330 int num_caut = number_accidentals (&different_caut,
331 pitch, origin,
332 cautionaries, barnum);
334 bool cautionary = to_boolean (note->get_property ("cautionary"));
336 if (num_caut > num)
338 num = num_caut;
339 different = different_caut;
340 cautionary = true;
343 if (num == 0 && to_boolean (note->get_property ("force-accidental")))
344 num = 1;
346 /* Cannot look for ties: it's not guaranteed that they reach
347 us before the notes. */
348 if (num)
351 We construct the accidentals at the originating Voice
352 level, so that we get the property settings for
353 Accidental from the respective Voice.
355 Grob *a
356 = make_item_from_properties (accidentals_[i].origin_trans_,
357 ly_symbol2scm ("Accidental"),
358 note->self_scm (),
359 "Accidental");
360 a->set_parent (support, Y_AXIS);
362 if (!accidental_placement_)
363 accidental_placement_ = make_item ("AccidentalPlacement",
364 a->self_scm ());
365 Accidental_placement::add_accidental (accidental_placement_, a);
366 SCM accs = scm_cons (scm_int2num (pitch->get_alteration ()),
367 SCM_EOL);
368 if (num == 2 && extra_natural_b)
369 accs = scm_cons (scm_int2num (0), accs);
371 /* TODO: add cautionary option in accidental. */
373 if (cautionary)
374 a->set_property ("cautionary", SCM_BOOL_T);
376 support->set_property ("accidental-grob", a->self_scm ());
378 a->set_property ("accidentals", accs);
379 accidentals_[i].accidental_ = a;
382 We add the accidentals to the support of the arpeggio,
383 so it is put left of the accidentals.
385 for (int i = 0; i < left_objects_.size (); i++)
386 Side_position_interface::add_support (left_objects_[i], a);
387 for (int i = 0; i < right_objects_.size (); i++)
388 Side_position_interface::add_support (a, right_objects_[i]);
394 void
395 Accidental_engraver::finalize ()
397 last_keysig_ = SCM_EOL;
400 void
401 Accidental_engraver::stop_translation_timestep ()
403 for (int j = ties_.size (); j--;)
405 Grob *r = Tie::head (ties_[j], RIGHT);
406 for (int i = accidentals_.size (); i--;)
407 if (accidentals_[i].head_ == r)
409 if (Grob *g = accidentals_[i].accidental_)
411 g->set_property ("tie", ties_[j]->self_scm ());
412 accidentals_[i].tied_ = true;
414 ties_.del (j);
415 break;
419 for (int i = accidentals_.size (); i--;)
421 int barnum = get_bar_number ();
423 Music *note = accidentals_[i].melodic_;
424 Context *origin = accidentals_[i].origin_;
426 Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
427 if (!pitch)
428 continue;
430 int n = pitch->get_notename ();
431 int o = pitch->get_octave ();
432 int a = pitch->get_alteration ();
433 SCM key = scm_cons (scm_int2num (o), scm_int2num (n));
435 while (origin
436 && origin->where_defined (ly_symbol2scm ("localKeySignature")))
439 huh? we set props all the way to the top?
441 SCM localsig = origin->get_property ("localKeySignature");
442 bool change = false;
443 if (accidentals_[i].tied_)
446 Remember an alteration that is different both from
447 that of the tied note and of the key signature.
449 localsig = ly_assoc_front_x
450 (localsig, key, scm_cons (SCM_BOOL_T, scm_int2num (barnum)));
452 change = true;
454 else
457 not really really correct if there are more than one
458 noteheads with the same notename.
460 localsig = ly_assoc_front_x (localsig, key,
461 scm_cons (scm_int2num (a),
462 scm_int2num (barnum)));
463 change = true;
466 if (change)
467 origin->set_property ("localKeySignature", localsig);
469 origin = origin->get_parent_context ();
473 accidental_placement_ = 0;
474 accidentals_.clear ();
475 left_objects_.clear ();
476 right_objects_.clear ();
479 void
480 Accidental_engraver::acknowledge_grob (Grob_info info)
482 Music *note = info.music_cause ();
484 if (note
485 && note->is_mus_type ("note-event")
486 && Rhythmic_head::has_interface (info.grob ()))
488 if (to_boolean (get_property ("harmonicAccidentals"))
489 || !ly_c_equal_p (info.grob ()->get_property ("style"),
490 ly_symbol2scm ("harmonic")))
493 Accidental_entry entry;
494 entry.head_ = info.grob ();
495 entry.origin_trans_ = dynamic_cast<Engraver *> (info.origin_translator ());
496 entry.origin_ = entry.origin_trans_->context ();
497 entry.melodic_ = note;
499 accidentals_.push (entry);
502 else if (Tie::has_interface (info.grob ()))
503 ties_.push (dynamic_cast<Spanner *> (info.grob ()));
504 else if (Arpeggio::has_interface (info.grob ()))
505 left_objects_.push (info.grob ());
506 else if (info.grob ()
507 ->internal_has_interface (ly_symbol2scm ("finger-interface")))
508 left_objects_.push (info.grob ());
511 void
512 Accidental_engraver::process_music ()
514 SCM sig = get_property ("keySignature");
515 /* Detect key sig changes.
516 Update all parents and children. */
517 if (last_keysig_ != sig)
518 update_local_key_signature ();
521 ADD_TRANSLATOR (Accidental_engraver,
522 "Make accidentals. "
523 "Catch note heads, ties and notices key-change events. "
524 "This engraver usually lives at Staff level, but "
525 "reads the settings for Accidental at @code{Voice} level, "
526 "so you can @code{\\override} them at @code{Voice}. ",
527 "Accidental",
529 "arpeggio-interface "
530 "finger-interface "
531 "rhythmic-head-interface "
532 "tie-interface ",
533 "autoAccidentals "
534 "autoCautionaries "
535 "extraNatural "
536 "harmonicAccidentals "
537 "localKeySignature",
538 "localKeySignature");