2 accidental-engraver.cc -- implement Accidental_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 Modified 2001--2002 by Rune Zedeler <rz@daimi.au.dk>
10 #include "accidental-placement.hh"
11 #include "arpeggio.hh"
13 #include "duration.hh"
14 #include "engraver.hh"
15 #include "international.hh"
18 #include "protected-scm.hh"
19 #include "rhythmic-head.hh"
20 #include "separation-item.hh"
21 #include "side-position-interface.hh"
23 #include "stream-event.hh"
27 #include "translator.icc"
29 class Accidental_entry
33 Stream_event
*melodic_
;
36 Engraver
*origin_engraver_
;
43 Accidental_entry::Accidental_entry ()
53 class Accidental_engraver
: public Engraver
55 void update_local_key_signature (SCM new_signature
);
56 void create_accidental (Accidental_entry
*entry
, bool, bool);
57 Grob
*make_standard_accidental (Stream_event
*note
, Grob
*note_head
, Engraver
*trans
, bool);
58 Grob
*make_suggested_accidental (Stream_event
*note
, Grob
*note_head
, Engraver
*trans
);
61 TRANSLATOR_DECLARATIONS (Accidental_engraver
);
62 void process_music ();
64 void acknowledge_tie (Grob_info
);
65 void acknowledge_arpeggio (Grob_info
);
66 void acknowledge_rhythmic_head (Grob_info
);
67 void acknowledge_finger (Grob_info
);
68 void acknowledge_note_column (Grob_info
);
70 void stop_translation_timestep ();
71 void process_acknowledged ();
73 virtual void finalize ();
74 virtual void derived_mark () const;
79 vector
<Grob
*> left_objects_
;
80 vector
<Grob
*> right_objects_
;
82 Grob
*accidental_placement_
;
84 vector
<Accidental_entry
> accidentals_
;
85 vector
<Spanner
*> ties_
;
86 vector
<Grob
*> note_columns_
;
90 localKeySignature is changed at runtime, which means that references
91 in grobs should always store ly_deep_copy ()s of those.
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 (SCM new_sig
)
110 last_keysig_
= new_sig
;
111 set_context_property_on_children (context (),
112 ly_symbol2scm ("localKeySignature"),
115 Context
*trans
= context ()->get_parent_context ();
117 /* Reset parent contexts so that e.g. piano-accidentals won't remember old
118 cross-staff accidentals after key-sig-changes */
121 while (trans
&& trans
->where_defined (ly_symbol2scm ("localKeySignature"), &val
)==trans
)
123 trans
->set_property ("localKeySignature", ly_deep_copy (last_keysig_
));
124 trans
= trans
->get_parent_context ();
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_cadr (alteration_def
)) + scm_to_int (laziness
));
149 extract_alteration (SCM alteration_def
)
151 if (scm_is_number (alteration_def
))
152 return ly_scm2rational (alteration_def
);
153 else if (scm_is_pair (alteration_def
))
154 return ly_scm2rational (scm_car (alteration_def
));
155 else if (alteration_def
== SCM_BOOL_F
)
163 is_tied (SCM alteration_def
)
165 SCM tied
= ly_symbol2scm ("tied");
166 return (alteration_def
== tied
167 || (scm_is_pair (alteration_def
) && scm_car (alteration_def
) == tied
));
170 struct Accidental_result
177 need_restore
= need_acc
= false;
180 Accidental_result (bool restore
, bool acc
)
182 need_restore
= restore
;
186 Accidental_result (SCM scm
)
188 need_restore
= to_boolean (scm_car (scm
));
189 need_acc
= to_boolean (scm_cdr (scm
));
194 return need_acc
? 1 : 0
195 + need_restore
? 1 : 0;
200 check_pitch_against_signature (SCM key_signature
, Pitch
const &pitch
,
201 int bar_number
, SCM laziness
, bool ignore_octave
)
203 Accidental_result result
;
204 int n
= pitch
.get_notename ();
205 int o
= pitch
.get_octave ();
207 SCM previous_alteration
= SCM_BOOL_F
;
209 SCM from_same_octave
= ly_assoc_get (scm_cons (scm_from_int (o
),
210 scm_from_int (n
)), key_signature
, SCM_BOOL_F
);
211 SCM from_key_signature
= ly_assoc_get (scm_from_int (n
), key_signature
, SCM_BOOL_F
);
212 SCM from_other_octaves
= SCM_BOOL_F
;
213 for (SCM s
= key_signature
; scm_is_pair (s
); s
= scm_cdr (s
))
215 SCM entry
= scm_car (s
);
216 if (scm_is_pair (scm_car (entry
))
217 && scm_cdar (entry
) == scm_from_int (n
))
219 from_other_octaves
= scm_cdr (entry
);
225 && from_same_octave
!= SCM_BOOL_F
226 && recent_enough (bar_number
, from_same_octave
, laziness
))
227 previous_alteration
= from_same_octave
;
228 else if (ignore_octave
229 && from_other_octaves
!= SCM_BOOL_F
230 && recent_enough (bar_number
, from_other_octaves
, laziness
))
231 previous_alteration
= from_other_octaves
;
232 else if (from_key_signature
!= SCM_BOOL_F
)
233 previous_alteration
= from_key_signature
;
235 if (is_tied (previous_alteration
))
237 result
.need_acc
= true;
241 Rational prev
= extract_alteration (previous_alteration
);
242 Rational alter
= pitch
.get_alteration ();
246 result
.need_acc
= true;
248 && (alter
.abs () < prev
.abs ()
249 || (prev
* alter
).sign () < 0))
250 result
.need_restore
= true;
257 // TODO: consider moving check_pitch_against_signature to SCM (in which case
258 // we can delete this function).
259 LY_DEFINE (ly_find_accidentals_simple
, "ly:find-accidentals-simple", 5, 0, 0,
260 (SCM keysig
, SCM pitch_scm
, SCM barnum
, SCM laziness
, SCM octaveness
),
261 "Checks the need for an accidental and a @q{restore} accidental against a"
262 " key signature. The @var{laziness} is the number of bars for which reminder"
263 " accidentals are used (ie. if @var{laziness} is zero, we only cancel accidentals"
264 " in the same bar; if @var{laziness} is three, we cancel accidentals up to three"
265 " bars after they first appear. @var{octaveness} is either"
266 " @code{'same-octave} or @code{'any-octave} and it specifies whether"
267 " accidentals should be canceled in different octaves.")
269 LY_ASSERT_TYPE (unsmob_pitch
, pitch_scm
, 2);
270 LY_ASSERT_TYPE (scm_is_integer
, barnum
, 3);
271 LY_ASSERT_TYPE (ly_is_symbol
, octaveness
, 5);
273 bool symbol_ok
= octaveness
== ly_symbol2scm ("any-octave") ||
274 octaveness
== ly_symbol2scm ("same-octave");
276 SCM_ASSERT_TYPE (symbol_ok
, octaveness
, SCM_ARG5
, __FUNCTION__
, "'any-octave or 'same-octave");
278 Pitch
*pitch
= unsmob_pitch (pitch_scm
);
280 int bar_number
= scm_to_int (barnum
);
281 bool ignore_octave
= ly_symbol2scm ("any-octave") == octaveness
;
282 Accidental_result result
= check_pitch_against_signature (keysig
, *pitch
, bar_number
,
283 laziness
, ignore_octave
);
285 return scm_cons (scm_from_bool (result
.need_restore
), scm_from_bool (result
.need_acc
));
290 check_pitch_against_rules (Pitch
const &pitch
, Context
*origin
,
291 SCM rules
, int bar_number
, SCM measurepos
)
293 Accidental_result result
;
294 SCM pitch_scm
= pitch
.smobbed_copy ();
295 SCM barnum_scm
= scm_from_int (bar_number
);
297 if (scm_is_pair (rules
) && !scm_is_symbol (scm_car (rules
)))
298 warning (_f ("accidental typesetting list must begin with context-name: %s",
299 ly_scm2string (scm_car (rules
)).c_str ()));
301 for (; scm_is_pair (rules
) && origin
;
302 rules
= scm_cdr (rules
))
304 SCM rule
= scm_car (rules
);
305 if (ly_is_procedure (rule
))
307 SCM rule_result_scm
= scm_call_4 (rule
, origin
->self_scm (),
308 pitch_scm
, barnum_scm
, measurepos
);
310 Accidental_result
rule_result (rule_result_scm
);
312 result
.need_acc
|= rule_result
.need_acc
;
313 result
.need_restore
|= rule_result
.need_restore
;
316 /* if symbol then it is a context name. Scan parent contexts to
318 else if (scm_is_symbol (rule
))
320 Context
*dad
= origin
;
321 while (dad
&& !dad
->is_alias (rule
))
322 dad
= dad
->get_parent_context ();
328 warning (_f ("procedure or context-name expected for accidental rule, found %s",
329 print_scm_val (rule
).c_str ()));
336 Accidental_engraver::process_acknowledged ()
338 if (accidentals_
.size () && !accidentals_
.back ().done_
)
340 SCM accidental_rules
= get_property ("autoAccidentals");
341 SCM cautionary_rules
= get_property ("autoCautionaries");
342 SCM measure_position
= get_property ("measurePosition");
343 int barnum
= measure_number (context());
345 for (vsize i
= 0; i
< accidentals_
.size (); i
++)
347 if (accidentals_
[i
].done_
)
349 accidentals_
[i
].done_
= true;
351 Stream_event
*note
= accidentals_
[i
].melodic_
;
352 Context
*origin
= accidentals_
[i
].origin_
;
354 Pitch
*pitch
= unsmob_pitch (note
->get_property ("pitch"));
358 Accidental_result acc
= check_pitch_against_rules (*pitch
, origin
, accidental_rules
,
359 barnum
, measure_position
);
360 Accidental_result caut
= check_pitch_against_rules (*pitch
, origin
, cautionary_rules
,
361 barnum
, measure_position
);
363 bool cautionary
= to_boolean (note
->get_property ("cautionary"));
364 if (caut
.score () > acc
.score ())
366 acc
.need_acc
|= caut
.need_acc
;
367 acc
.need_restore
|= caut
.need_restore
;
372 bool forced
= to_boolean (note
->get_property ("force-accidental"));
373 if (!acc
.need_acc
&& forced
)
376 /* Cannot look for ties: it's not guaranteed that they reach
377 us before the notes. */
378 if (!note
->in_event_class ("trill-span-event"))
381 create_accidental (&accidentals_
[i
], acc
.need_restore
, cautionary
);
383 if (forced
|| cautionary
)
384 accidentals_
[i
].accidental_
->set_property ("forced", SCM_BOOL_T
);
391 Accidental_engraver::create_accidental (Accidental_entry
*entry
,
392 bool restore_natural
,
395 Stream_event
*note
= entry
->melodic_
;
396 Grob
*support
= entry
->head_
;
397 bool as_suggestion
= to_boolean (entry
->origin_
->get_property ("suggestAccidentals"));
400 a
= make_suggested_accidental (note
, support
, entry
->origin_engraver_
);
402 a
= make_standard_accidental (note
, support
, entry
->origin_engraver_
, cautionary
);
406 if (to_boolean (get_property ("extraNatural")))
407 a
->set_property ("restore-first", SCM_BOOL_T
);
410 entry
->accidental_
= a
;
414 Accidental_engraver::make_standard_accidental (Stream_event
* /* note */,
420 We construct the accidentals at the originating Voice
421 level, so that we get the property settings for
422 Accidental from the respective Voice.
426 a
= trans
->make_item ("AccidentalCautionary", note_head
->self_scm ());
428 a
= trans
->make_item ("Accidental", note_head
->self_scm ());
431 We add the accidentals to the support of the arpeggio,
432 so it is put left of the accidentals.
434 for (vsize i
= 0; i
< left_objects_
.size (); i
++)
436 if (left_objects_
[i
]->get_property ("side-axis") == scm_from_int (X_AXIS
))
437 Side_position_interface::add_support (left_objects_
[i
], a
);
440 for (vsize i
= 0; i
< right_objects_
.size (); i
++)
441 Side_position_interface::add_support (a
, right_objects_
[i
]);
443 a
->set_parent (note_head
, Y_AXIS
);
445 if (!accidental_placement_
)
446 accidental_placement_
= make_item ("AccidentalPlacement",
448 Accidental_placement::add_accidental (accidental_placement_
, a
);
450 note_head
->set_object ("accidental-grob", a
->self_scm ());
456 Accidental_engraver::make_suggested_accidental (Stream_event
* /* note */,
460 Grob
*a
= trans
->make_item ("AccidentalSuggestion", note_head
->self_scm ());
462 Side_position_interface::add_support (a
, note_head
);
463 if (Grob
*stem
= unsmob_grob (a
->get_object ("stem")))
464 Side_position_interface::add_support (a
, stem
);
466 a
->set_parent (note_head
, X_AXIS
);
471 Accidental_engraver::finalize ()
473 last_keysig_
= SCM_EOL
;
477 Accidental_engraver::stop_translation_timestep ()
479 for (vsize j
= ties_
.size (); j
--;)
481 Grob
*r
= Tie::head (ties_
[j
], RIGHT
);
482 for (vsize i
= accidentals_
.size (); i
--;)
483 if (accidentals_
[i
].head_
== r
)
485 if (Grob
*g
= accidentals_
[i
].accidental_
)
487 g
->set_object ("tie", ties_
[j
]->self_scm ());
488 accidentals_
[i
].tied_
= true;
490 ties_
.erase (ties_
.begin () + j
);
495 for (vsize i
= accidentals_
.size (); i
--;)
497 Stream_event
*note
= accidentals_
[i
].melodic_
;
498 Context
*origin
= accidentals_
[i
].origin_
;
500 int barnum
= measure_number (origin
);
502 Pitch
*pitch
= unsmob_pitch (note
->get_property ("pitch"));
506 int n
= pitch
->get_notename ();
507 int o
= pitch
->get_octave ();
508 Rational a
= pitch
->get_alteration ();
509 SCM key
= scm_cons (scm_from_int (o
), scm_from_int (n
));
511 Moment end_mp
= measure_position (context (),
512 unsmob_duration (note
->get_property ("duration")));
513 SCM position
= scm_cons (scm_from_int (barnum
), end_mp
.smobbed_copy ());
515 SCM localsig
= SCM_EOL
;
517 && origin
->where_defined (ly_symbol2scm ("localKeySignature"), &localsig
))
520 if (accidentals_
[i
].tied_
521 && !(to_boolean (accidentals_
[i
].accidental_
->get_property ("forced"))))
524 Remember an alteration that is different both from
525 that of the tied note and of the key signature.
527 localsig
= ly_assoc_prepend_x (localsig
, key
,scm_cons (ly_symbol2scm ("tied"),
535 not really really correct if there are more than one
536 noteheads with the same notename.
538 localsig
= ly_assoc_prepend_x (localsig
, key
,
539 scm_cons (ly_rational2scm (a
),
545 origin
->set_property ("localKeySignature", localsig
);
547 origin
= origin
->get_parent_context ();
551 if (accidental_placement_
)
552 for (vsize i
= 0; i
< note_columns_
.size (); i
++)
553 Separation_item::add_conditional_item (note_columns_
[i
], accidental_placement_
);
555 accidental_placement_
= 0;
556 accidentals_
.clear ();
557 left_objects_
.clear ();
558 right_objects_
.clear ();
562 Accidental_engraver::acknowledge_rhythmic_head (Grob_info info
)
564 Stream_event
*note
= info
.event_cause ();
566 && (note
->in_event_class ("note-event")
567 || note
->in_event_class ("trill-span-event")))
570 string harmonics usually don't have accidentals.
572 if (info
.grob ()->get_property ("style") != ly_symbol2scm ("harmonic")
573 || to_boolean (get_property ("harmonicAccidentals")))
575 Accidental_entry entry
;
576 entry
.head_
= info
.grob ();
577 entry
.origin_engraver_
= dynamic_cast<Engraver
*> (info
.origin_translator ());
578 entry
.origin_
= entry
.origin_engraver_
->context ();
579 entry
.melodic_
= note
;
581 accidentals_
.push_back (entry
);
587 Accidental_engraver::acknowledge_tie (Grob_info info
)
589 ties_
.push_back (dynamic_cast<Spanner
*> (info
.grob ()));
593 Accidental_engraver::acknowledge_note_column (Grob_info info
)
595 note_columns_
.push_back (info
.grob ());
599 Accidental_engraver::acknowledge_arpeggio (Grob_info info
)
601 left_objects_
.push_back (info
.grob ());
605 Accidental_engraver::acknowledge_finger (Grob_info info
)
607 left_objects_
.push_back (info
.grob ());
611 Accidental_engraver::process_music ()
613 SCM sig
= get_property ("keySignature");
614 if (last_keysig_
!= sig
)
615 update_local_key_signature (sig
);
618 ADD_ACKNOWLEDGER (Accidental_engraver
, arpeggio
);
619 ADD_ACKNOWLEDGER (Accidental_engraver
, finger
);
620 ADD_ACKNOWLEDGER (Accidental_engraver
, rhythmic_head
);
621 ADD_ACKNOWLEDGER (Accidental_engraver
, tie
);
622 ADD_ACKNOWLEDGER (Accidental_engraver
, note_column
);
624 ADD_TRANSLATOR (Accidental_engraver
,
627 " Catch note heads, ties and notices key-change events."
628 " This engraver usually lives at Staff level, but"
629 " reads the settings for Accidental at @code{Voice} level,"
630 " so you can @code{\\override} them at @code{Voice}.",
634 "AccidentalCautionary "
635 "AccidentalPlacement "
636 "AccidentalSuggestion ",
643 "harmonicAccidentals "
645 "localKeySignature ",