2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2010 Han-Wen Nienhuys <hanwen@xs4all.nl>
5 Modified 2001--2002 by Rune Zedeler <rz@daimi.au.dk>
7 LilyPond is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 LilyPond is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
21 #include "accidental-placement.hh"
22 #include "arpeggio.hh"
24 #include "duration.hh"
25 #include "engraver.hh"
26 #include "international.hh"
29 #include "protected-scm.hh"
30 #include "rhythmic-head.hh"
31 #include "separation-item.hh"
32 #include "side-position-interface.hh"
34 #include "stream-event.hh"
38 #include "translator.icc"
40 class Accidental_entry
44 Stream_event
*melodic_
;
47 Engraver
*origin_engraver_
;
54 Accidental_entry::Accidental_entry ()
64 class Accidental_engraver
: public Engraver
66 void update_local_key_signature (SCM new_signature
);
67 void create_accidental (Accidental_entry
*entry
, bool, bool);
68 Grob
*make_standard_accidental (Stream_event
*note
, Grob
*note_head
, Engraver
*trans
, bool);
69 Grob
*make_suggested_accidental (Stream_event
*note
, Grob
*note_head
, Engraver
*trans
);
72 TRANSLATOR_DECLARATIONS (Accidental_engraver
);
73 void process_music ();
75 void acknowledge_tie (Grob_info
);
76 void acknowledge_arpeggio (Grob_info
);
77 void acknowledge_rhythmic_head (Grob_info
);
78 void acknowledge_finger (Grob_info
);
79 void acknowledge_note_column (Grob_info
);
81 void stop_translation_timestep ();
82 void process_acknowledged ();
84 virtual void finalize ();
85 virtual void derived_mark () const;
90 vector
<Grob
*> left_objects_
;
91 vector
<Grob
*> right_objects_
;
93 Grob
*accidental_placement_
;
95 vector
<Accidental_entry
> accidentals_
;
96 vector
<Spanner
*> ties_
;
97 vector
<Grob
*> note_columns_
;
101 localKeySignature is changed at runtime, which means that references
102 in grobs should always store ly_deep_copy ()s of those.
106 Accidental_engraver::Accidental_engraver ()
108 accidental_placement_
= 0;
109 last_keysig_
= SCM_EOL
;
113 Accidental_engraver::derived_mark () const
115 scm_gc_mark (last_keysig_
);
119 Accidental_engraver::update_local_key_signature (SCM new_sig
)
121 last_keysig_
= new_sig
;
122 set_context_property_on_children (context (),
123 ly_symbol2scm ("localKeySignature"),
126 Context
*trans
= context ()->get_parent_context ();
129 Reset parent contexts so that e.g. piano-accidentals won't remember old
130 cross-staff accidentals after key-sig-changes.
134 while (trans
&& trans
->where_defined (ly_symbol2scm ("localKeySignature"), &val
) == trans
)
136 trans
->set_property ("localKeySignature", ly_deep_copy (last_keysig_
));
137 trans
= trans
->get_parent_context ();
141 struct Accidental_result
148 need_restore
= need_acc
= false;
151 Accidental_result (bool restore
, bool acc
)
153 need_restore
= restore
;
157 Accidental_result (SCM scm
)
159 need_restore
= to_boolean (scm_car (scm
));
160 need_acc
= to_boolean (scm_cdr (scm
));
165 return need_acc
? 1 : 0
166 + need_restore
? 1 : 0;
172 check_pitch_against_rules (Pitch
const &pitch
, Context
*origin
,
173 SCM rules
, int bar_number
, SCM measurepos
)
175 Accidental_result result
;
176 SCM pitch_scm
= pitch
.smobbed_copy ();
177 SCM barnum_scm
= scm_from_int (bar_number
);
179 if (scm_is_pair (rules
) && !scm_is_symbol (scm_car (rules
)))
180 warning (_f ("accidental typesetting list must begin with context-name: %s",
181 ly_scm2string (scm_car (rules
)).c_str ()));
183 for (; scm_is_pair (rules
) && origin
; rules
= scm_cdr (rules
))
185 SCM rule
= scm_car (rules
);
186 if (ly_is_procedure (rule
))
188 SCM rule_result_scm
= scm_call_4 (rule
, origin
->self_scm (),
189 pitch_scm
, barnum_scm
, measurepos
);
190 Accidental_result
rule_result (rule_result_scm
);
192 result
.need_acc
|= rule_result
.need_acc
;
193 result
.need_restore
|= rule_result
.need_restore
;
197 If symbol then it is a context name. Scan parent contexts to
200 else if (scm_is_symbol (rule
))
202 Context
*dad
= origin
;
203 while (dad
&& !dad
->is_alias (rule
))
204 dad
= dad
->get_parent_context ();
210 warning (_f ("procedure or context-name expected for accidental rule, found %s",
211 print_scm_val (rule
).c_str ()));
218 Accidental_engraver::process_acknowledged ()
220 if (accidentals_
.size () && !accidentals_
.back ().done_
)
222 SCM accidental_rules
= get_property ("autoAccidentals");
223 SCM cautionary_rules
= get_property ("autoCautionaries");
224 SCM measure_position
= get_property ("measurePosition");
225 int barnum
= measure_number (context());
227 for (vsize i
= 0; i
< accidentals_
.size (); i
++)
229 if (accidentals_
[i
].done_
)
231 accidentals_
[i
].done_
= true;
233 Stream_event
*note
= accidentals_
[i
].melodic_
;
234 Context
*origin
= accidentals_
[i
].origin_
;
236 Pitch
*pitch
= unsmob_pitch (note
->get_property ("pitch"));
240 Accidental_result acc
= check_pitch_against_rules (*pitch
, origin
, accidental_rules
,
241 barnum
, measure_position
);
242 Accidental_result caut
= check_pitch_against_rules (*pitch
, origin
, cautionary_rules
,
243 barnum
, measure_position
);
245 bool cautionary
= to_boolean (note
->get_property ("cautionary"));
246 if (caut
.score () > acc
.score ())
248 acc
.need_acc
|= caut
.need_acc
;
249 acc
.need_restore
|= caut
.need_restore
;
254 bool forced
= to_boolean (note
->get_property ("force-accidental"));
255 if (!acc
.need_acc
&& forced
)
259 Cannot look for ties: it's not guaranteed that they reach
262 if (!note
->in_event_class ("trill-span-event"))
265 create_accidental (&accidentals_
[i
], acc
.need_restore
, cautionary
);
267 if (forced
|| cautionary
)
268 accidentals_
[i
].accidental_
->set_property ("forced", SCM_BOOL_T
);
275 Accidental_engraver::create_accidental (Accidental_entry
*entry
,
276 bool restore_natural
,
279 Stream_event
*note
= entry
->melodic_
;
280 Grob
*support
= entry
->head_
;
281 bool as_suggestion
= to_boolean (entry
->origin_
->get_property ("suggestAccidentals"));
284 a
= make_suggested_accidental (note
, support
, entry
->origin_engraver_
);
286 a
= make_standard_accidental (note
, support
, entry
->origin_engraver_
, cautionary
);
290 if (to_boolean (get_property ("extraNatural")))
291 a
->set_property ("restore-first", SCM_BOOL_T
);
294 entry
->accidental_
= a
;
298 Accidental_engraver::make_standard_accidental (Stream_event
* /* note */,
304 We construct the accidentals at the originating Voice
305 level, so that we get the property settings for
306 Accidental from the respective Voice.
310 a
= trans
->make_item ("AccidentalCautionary", note_head
->self_scm ());
312 a
= trans
->make_item ("Accidental", note_head
->self_scm ());
315 We add the accidentals to the support of the arpeggio,
316 so it is put left of the accidentals.
318 for (vsize i
= 0; i
< left_objects_
.size (); i
++)
320 if (left_objects_
[i
]->get_property ("side-axis") == scm_from_int (X_AXIS
))
321 Side_position_interface::add_support (left_objects_
[i
], a
);
324 for (vsize i
= 0; i
< right_objects_
.size (); i
++)
325 Side_position_interface::add_support (a
, right_objects_
[i
]);
327 a
->set_parent (note_head
, Y_AXIS
);
329 if (!accidental_placement_
)
330 accidental_placement_
= make_item ("AccidentalPlacement",
332 Accidental_placement::add_accidental (accidental_placement_
, a
);
334 note_head
->set_object ("accidental-grob", a
->self_scm ());
340 Accidental_engraver::make_suggested_accidental (Stream_event
* /* note */,
344 Grob
*a
= trans
->make_item ("AccidentalSuggestion", note_head
->self_scm ());
346 Side_position_interface::add_support (a
, note_head
);
347 if (Grob
*stem
= unsmob_grob (a
->get_object ("stem")))
348 Side_position_interface::add_support (a
, stem
);
350 a
->set_parent (note_head
, X_AXIS
);
355 Accidental_engraver::finalize ()
357 last_keysig_
= SCM_EOL
;
361 Accidental_engraver::stop_translation_timestep ()
363 for (vsize j
= ties_
.size (); j
--;)
365 Grob
*r
= Tie::head (ties_
[j
], RIGHT
);
366 for (vsize i
= accidentals_
.size (); i
--;)
367 if (accidentals_
[i
].head_
== r
)
369 if (Grob
*g
= accidentals_
[i
].accidental_
)
371 g
->set_object ("tie", ties_
[j
]->self_scm ());
372 accidentals_
[i
].tied_
= true;
374 ties_
.erase (ties_
.begin () + j
);
379 for (vsize i
= accidentals_
.size (); i
--;)
381 Stream_event
*note
= accidentals_
[i
].melodic_
;
382 Context
*origin
= accidentals_
[i
].origin_
;
384 int barnum
= measure_number (origin
);
386 Pitch
*pitch
= unsmob_pitch (note
->get_property ("pitch"));
390 int n
= pitch
->get_notename ();
391 int o
= pitch
->get_octave ();
392 Rational a
= pitch
->get_alteration ();
393 SCM key
= scm_cons (scm_from_int (o
), scm_from_int (n
));
395 Moment end_mp
= measure_position (context (),
396 unsmob_duration (note
->get_property ("duration")));
397 SCM position
= scm_cons (scm_from_int (barnum
), end_mp
.smobbed_copy ());
399 SCM localsig
= SCM_EOL
;
401 && origin
->where_defined (ly_symbol2scm ("localKeySignature"), &localsig
))
404 if (accidentals_
[i
].tied_
405 && !(to_boolean (accidentals_
[i
].accidental_
->get_property ("forced"))))
408 Remember an alteration that is different both from
409 that of the tied note and of the key signature.
411 localsig
= ly_assoc_prepend_x (localsig
, key
,scm_cons (ly_symbol2scm ("tied"),
418 not really correct if there is more than one
419 note head with the same notename.
421 localsig
= ly_assoc_prepend_x (localsig
, key
,
422 scm_cons (ly_rational2scm (a
),
428 origin
->set_property ("localKeySignature", localsig
);
430 origin
= origin
->get_parent_context ();
434 if (accidental_placement_
)
435 for (vsize i
= 0; i
< note_columns_
.size (); i
++)
436 Separation_item::add_conditional_item (note_columns_
[i
], accidental_placement_
);
438 accidental_placement_
= 0;
439 accidentals_
.clear ();
440 left_objects_
.clear ();
441 right_objects_
.clear ();
445 Accidental_engraver::acknowledge_rhythmic_head (Grob_info info
)
447 Stream_event
*note
= info
.event_cause ();
449 && (note
->in_event_class ("note-event")
450 || note
->in_event_class ("trill-span-event")))
453 string harmonics usually don't have accidentals.
455 if (info
.grob ()->get_property ("style") != ly_symbol2scm ("harmonic")
456 || to_boolean (get_property ("harmonicAccidentals")))
458 Accidental_entry entry
;
459 entry
.head_
= info
.grob ();
460 entry
.origin_engraver_
= dynamic_cast<Engraver
*> (info
.origin_translator ());
461 entry
.origin_
= entry
.origin_engraver_
->context ();
462 entry
.melodic_
= note
;
464 accidentals_
.push_back (entry
);
470 Accidental_engraver::acknowledge_tie (Grob_info info
)
472 ties_
.push_back (dynamic_cast<Spanner
*> (info
.grob ()));
476 Accidental_engraver::acknowledge_note_column (Grob_info info
)
478 note_columns_
.push_back (info
.grob ());
482 Accidental_engraver::acknowledge_arpeggio (Grob_info info
)
484 left_objects_
.push_back (info
.grob ());
488 Accidental_engraver::acknowledge_finger (Grob_info info
)
490 left_objects_
.push_back (info
.grob ());
494 Accidental_engraver::process_music ()
496 SCM sig
= get_property ("keySignature");
497 if (last_keysig_
!= sig
)
498 update_local_key_signature (sig
);
501 ADD_ACKNOWLEDGER (Accidental_engraver
, arpeggio
);
502 ADD_ACKNOWLEDGER (Accidental_engraver
, finger
);
503 ADD_ACKNOWLEDGER (Accidental_engraver
, rhythmic_head
);
504 ADD_ACKNOWLEDGER (Accidental_engraver
, tie
);
505 ADD_ACKNOWLEDGER (Accidental_engraver
, note_column
);
507 ADD_TRANSLATOR (Accidental_engraver
,
510 " Catch note heads, ties and notices key-change events."
511 " This engraver usually lives at Staff level, but"
512 " reads the settings for Accidental at @code{Voice} level,"
513 " so you can @code{\\override} them at @code{Voice}.",
517 "AccidentalCautionary "
518 "AccidentalPlacement "
519 "AccidentalSuggestion ",
526 "harmonicAccidentals "
528 "localKeySignature ",