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 ();
118 Reset parent contexts so that e.g. piano-accidentals won't remember old
119 cross-staff accidentals after key-sig-changes.
123 while (trans
&& trans
->where_defined (ly_symbol2scm ("localKeySignature"), &val
) == trans
)
125 trans
->set_property ("localKeySignature", ly_deep_copy (last_keysig_
));
126 trans
= trans
->get_parent_context ();
130 struct Accidental_result
137 need_restore
= need_acc
= false;
140 Accidental_result (bool restore
, bool acc
)
142 need_restore
= restore
;
146 Accidental_result (SCM scm
)
148 need_restore
= to_boolean (scm_car (scm
));
149 need_acc
= to_boolean (scm_cdr (scm
));
154 return need_acc
? 1 : 0
155 + need_restore
? 1 : 0;
161 check_pitch_against_rules (Pitch
const &pitch
, Context
*origin
,
162 SCM rules
, int bar_number
, SCM measurepos
)
164 Accidental_result result
;
165 SCM pitch_scm
= pitch
.smobbed_copy ();
166 SCM barnum_scm
= scm_from_int (bar_number
);
168 if (scm_is_pair (rules
) && !scm_is_symbol (scm_car (rules
)))
169 warning (_f ("accidental typesetting list must begin with context-name: %s",
170 ly_scm2string (scm_car (rules
)).c_str ()));
172 for (; scm_is_pair (rules
) && origin
; rules
= scm_cdr (rules
))
174 SCM rule
= scm_car (rules
);
175 if (ly_is_procedure (rule
))
177 SCM rule_result_scm
= scm_call_4 (rule
, origin
->self_scm (),
178 pitch_scm
, barnum_scm
, measurepos
);
179 Accidental_result
rule_result (rule_result_scm
);
181 result
.need_acc
|= rule_result
.need_acc
;
182 result
.need_restore
|= rule_result
.need_restore
;
186 If symbol then it is a context name. Scan parent contexts to
189 else if (scm_is_symbol (rule
))
191 Context
*dad
= origin
;
192 while (dad
&& !dad
->is_alias (rule
))
193 dad
= dad
->get_parent_context ();
199 warning (_f ("procedure or context-name expected for accidental rule, found %s",
200 print_scm_val (rule
).c_str ()));
207 Accidental_engraver::process_acknowledged ()
209 if (accidentals_
.size () && !accidentals_
.back ().done_
)
211 SCM accidental_rules
= get_property ("autoAccidentals");
212 SCM cautionary_rules
= get_property ("autoCautionaries");
213 SCM measure_position
= get_property ("measurePosition");
214 int barnum
= measure_number (context());
216 for (vsize i
= 0; i
< accidentals_
.size (); i
++)
218 if (accidentals_
[i
].done_
)
220 accidentals_
[i
].done_
= true;
222 Stream_event
*note
= accidentals_
[i
].melodic_
;
223 Context
*origin
= accidentals_
[i
].origin_
;
225 Pitch
*pitch
= unsmob_pitch (note
->get_property ("pitch"));
229 Accidental_result acc
= check_pitch_against_rules (*pitch
, origin
, accidental_rules
,
230 barnum
, measure_position
);
231 Accidental_result caut
= check_pitch_against_rules (*pitch
, origin
, cautionary_rules
,
232 barnum
, measure_position
);
234 bool cautionary
= to_boolean (note
->get_property ("cautionary"));
235 if (caut
.score () > acc
.score ())
237 acc
.need_acc
|= caut
.need_acc
;
238 acc
.need_restore
|= caut
.need_restore
;
243 bool forced
= to_boolean (note
->get_property ("force-accidental"));
244 if (!acc
.need_acc
&& forced
)
248 Cannot look for ties: it's not guaranteed that they reach
251 if (!note
->in_event_class ("trill-span-event"))
254 create_accidental (&accidentals_
[i
], acc
.need_restore
, cautionary
);
256 if (forced
|| cautionary
)
257 accidentals_
[i
].accidental_
->set_property ("forced", SCM_BOOL_T
);
264 Accidental_engraver::create_accidental (Accidental_entry
*entry
,
265 bool restore_natural
,
268 Stream_event
*note
= entry
->melodic_
;
269 Grob
*support
= entry
->head_
;
270 bool as_suggestion
= to_boolean (entry
->origin_
->get_property ("suggestAccidentals"));
273 a
= make_suggested_accidental (note
, support
, entry
->origin_engraver_
);
275 a
= make_standard_accidental (note
, support
, entry
->origin_engraver_
, cautionary
);
279 if (to_boolean (get_property ("extraNatural")))
280 a
->set_property ("restore-first", SCM_BOOL_T
);
283 entry
->accidental_
= a
;
287 Accidental_engraver::make_standard_accidental (Stream_event
* /* note */,
293 We construct the accidentals at the originating Voice
294 level, so that we get the property settings for
295 Accidental from the respective Voice.
299 a
= trans
->make_item ("AccidentalCautionary", note_head
->self_scm ());
301 a
= trans
->make_item ("Accidental", note_head
->self_scm ());
304 We add the accidentals to the support of the arpeggio,
305 so it is put left of the accidentals.
307 for (vsize i
= 0; i
< left_objects_
.size (); i
++)
309 if (left_objects_
[i
]->get_property ("side-axis") == scm_from_int (X_AXIS
))
310 Side_position_interface::add_support (left_objects_
[i
], a
);
313 for (vsize i
= 0; i
< right_objects_
.size (); i
++)
314 Side_position_interface::add_support (a
, right_objects_
[i
]);
316 a
->set_parent (note_head
, Y_AXIS
);
318 if (!accidental_placement_
)
319 accidental_placement_
= make_item ("AccidentalPlacement",
321 Accidental_placement::add_accidental (accidental_placement_
, a
);
323 note_head
->set_object ("accidental-grob", a
->self_scm ());
329 Accidental_engraver::make_suggested_accidental (Stream_event
* /* note */,
333 Grob
*a
= trans
->make_item ("AccidentalSuggestion", note_head
->self_scm ());
335 Side_position_interface::add_support (a
, note_head
);
336 if (Grob
*stem
= unsmob_grob (a
->get_object ("stem")))
337 Side_position_interface::add_support (a
, stem
);
339 a
->set_parent (note_head
, X_AXIS
);
344 Accidental_engraver::finalize ()
346 last_keysig_
= SCM_EOL
;
350 Accidental_engraver::stop_translation_timestep ()
352 for (vsize j
= ties_
.size (); j
--;)
354 Grob
*r
= Tie::head (ties_
[j
], RIGHT
);
355 for (vsize i
= accidentals_
.size (); i
--;)
356 if (accidentals_
[i
].head_
== r
)
358 if (Grob
*g
= accidentals_
[i
].accidental_
)
360 g
->set_object ("tie", ties_
[j
]->self_scm ());
361 accidentals_
[i
].tied_
= true;
363 ties_
.erase (ties_
.begin () + j
);
368 for (vsize i
= accidentals_
.size (); i
--;)
370 Stream_event
*note
= accidentals_
[i
].melodic_
;
371 Context
*origin
= accidentals_
[i
].origin_
;
373 int barnum
= measure_number (origin
);
375 Pitch
*pitch
= unsmob_pitch (note
->get_property ("pitch"));
379 int n
= pitch
->get_notename ();
380 int o
= pitch
->get_octave ();
381 Rational a
= pitch
->get_alteration ();
382 SCM key
= scm_cons (scm_from_int (o
), scm_from_int (n
));
384 Moment end_mp
= measure_position (context (),
385 unsmob_duration (note
->get_property ("duration")));
386 SCM position
= scm_cons (scm_from_int (barnum
), end_mp
.smobbed_copy ());
388 SCM localsig
= SCM_EOL
;
390 && origin
->where_defined (ly_symbol2scm ("localKeySignature"), &localsig
))
393 if (accidentals_
[i
].tied_
394 && !(to_boolean (accidentals_
[i
].accidental_
->get_property ("forced"))))
397 Remember an alteration that is different both from
398 that of the tied note and of the key signature.
400 localsig
= ly_assoc_prepend_x (localsig
, key
,scm_cons (ly_symbol2scm ("tied"),
407 not really really correct if there is more than one
408 note head with the same notename.
410 localsig
= ly_assoc_prepend_x (localsig
, key
,
411 scm_cons (ly_rational2scm (a
),
417 origin
->set_property ("localKeySignature", localsig
);
419 origin
= origin
->get_parent_context ();
423 if (accidental_placement_
)
424 for (vsize i
= 0; i
< note_columns_
.size (); i
++)
425 Separation_item::add_conditional_item (note_columns_
[i
], accidental_placement_
);
427 accidental_placement_
= 0;
428 accidentals_
.clear ();
429 left_objects_
.clear ();
430 right_objects_
.clear ();
434 Accidental_engraver::acknowledge_rhythmic_head (Grob_info info
)
436 Stream_event
*note
= info
.event_cause ();
438 && (note
->in_event_class ("note-event")
439 || note
->in_event_class ("trill-span-event")))
442 string harmonics usually don't have accidentals.
444 if (info
.grob ()->get_property ("style") != ly_symbol2scm ("harmonic")
445 || to_boolean (get_property ("harmonicAccidentals")))
447 Accidental_entry entry
;
448 entry
.head_
= info
.grob ();
449 entry
.origin_engraver_
= dynamic_cast<Engraver
*> (info
.origin_translator ());
450 entry
.origin_
= entry
.origin_engraver_
->context ();
451 entry
.melodic_
= note
;
453 accidentals_
.push_back (entry
);
459 Accidental_engraver::acknowledge_tie (Grob_info info
)
461 ties_
.push_back (dynamic_cast<Spanner
*> (info
.grob ()));
465 Accidental_engraver::acknowledge_note_column (Grob_info info
)
467 note_columns_
.push_back (info
.grob ());
471 Accidental_engraver::acknowledge_arpeggio (Grob_info info
)
473 left_objects_
.push_back (info
.grob ());
477 Accidental_engraver::acknowledge_finger (Grob_info info
)
479 left_objects_
.push_back (info
.grob ());
483 Accidental_engraver::process_music ()
485 SCM sig
= get_property ("keySignature");
486 if (last_keysig_
!= sig
)
487 update_local_key_signature (sig
);
490 ADD_ACKNOWLEDGER (Accidental_engraver
, arpeggio
);
491 ADD_ACKNOWLEDGER (Accidental_engraver
, finger
);
492 ADD_ACKNOWLEDGER (Accidental_engraver
, rhythmic_head
);
493 ADD_ACKNOWLEDGER (Accidental_engraver
, tie
);
494 ADD_ACKNOWLEDGER (Accidental_engraver
, note_column
);
496 ADD_TRANSLATOR (Accidental_engraver
,
499 " Catch note heads, ties and notices key-change events."
500 " This engraver usually lives at Staff level, but"
501 " reads the settings for Accidental at @code{Voice} level,"
502 " so you can @code{\\override} them at @code{Voice}.",
506 "AccidentalCautionary "
507 "AccidentalPlacement "
508 "AccidentalSuggestion ",
515 "harmonicAccidentals "
517 "localKeySignature ",