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>
10 #include "accidental-placement.hh"
11 #include "arpeggio.hh"
13 #include "engraver.hh"
14 #include "protected-scm.hh"
15 #include "rhythmic-head.hh"
16 #include "side-position-interface.hh"
20 class Accidental_entry
27 Engraver
*origin_trans_
;
34 Accidental_entry::Accidental_entry ()
44 class Accidental_engraver
: public Engraver
47 int get_bar_number ();
48 void update_local_key_signature ();
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;
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
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_
;
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
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
;
102 Accidental_engraver::derived_mark () const
104 scm_gc_mark (last_keysig_
);
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 ();
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). */
139 recent_enough (int bar_number
, SCM alteration_def
, SCM laziness
)
141 if (scm_is_number (alteration_def
)
142 || laziness
== SCM_BOOL_T
)
145 return (bar_number
<= scm_to_int (scm_cdr (alteration_def
)) + scm_to_int (laziness
));
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
)
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
);
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
;
207 if (is_tied (previous_alteration
))
214 int prev
= extract_alteration (previous_alteration
);
215 int alter
= pitch
->get_alteration ();
219 else if ((abs (alter
) < abs (prev
) || prev
* alter
< 0) && alter
!= 0)
221 *different
= (alter
!= prev
);
227 number_accidentals (bool *different
,
228 Pitch
*pitch
, Context
*origin
,
229 SCM accidentals
, int bar_number
)
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");
250 = scm_is_eq (ly_symbol2scm ("same-octave"), type
);
252 = scm_is_eq (ly_symbol2scm ("any-octave"), type
);
254 if (same_octave_b
|| any_octave_b
)
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
);
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
269 else if (scm_is_symbol (rule
))
271 Context
*dad
= origin
;
272 while (dad
&& !dad
->is_alias (rule
))
273 dad
= dad
->get_parent_context ();
279 warning (_f ("pair or context-name expected for accidental rule, found %s",
280 ly_scm2string (rule
).to_str0 ()));
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))
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_
)
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"));
324 bool different
= false;
325 bool different_caut
= false;
327 int num
= number_accidentals (&different
,
329 accidentals
, barnum
);
330 int num_caut
= number_accidentals (&different_caut
,
332 cautionaries
, barnum
);
334 bool cautionary
= to_boolean (note
->get_property ("cautionary"));
339 different
= different_caut
;
343 if (num
== 0 && to_boolean (note
->get_property ("force-accidental")))
346 /* Cannot look for ties: it's not guaranteed that they reach
347 us before the notes. */
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.
356 = make_item_from_properties (accidentals_
[i
].origin_trans_
,
357 ly_symbol2scm ("Accidental"),
360 a
->set_parent (support
, Y_AXIS
);
362 if (!accidental_placement_
)
363 accidental_placement_
= make_item ("AccidentalPlacement",
365 Accidental_placement::add_accidental (accidental_placement_
, a
);
366 SCM accs
= scm_cons (scm_int2num (pitch
->get_alteration ()),
368 if (num
== 2 && extra_natural_b
)
369 accs
= scm_cons (scm_int2num (0), accs
);
371 /* TODO: add cautionary option in accidental. */
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
]);
395 Accidental_engraver::finalize ()
397 last_keysig_
= SCM_EOL
;
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;
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"));
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
));
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");
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
)));
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
)));
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 ();
480 Accidental_engraver::acknowledge_grob (Grob_info info
)
482 Music
*note
= info
.music_cause ();
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 ());
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
,
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}. ",
529 "arpeggio-interface "
531 "rhythmic-head-interface "
536 "harmonicAccidentals "
538 "localKeySignature");