2 accidental-engraver.cc -- implement accidental_engraver
4 (c) 1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 Modified 2001-2002 by Rune Zedeler <rz@daimi.au.dk>
8 #include "musical-request.hh"
9 #include "command-request.hh"
12 #include "rhythmic-head.hh"
13 #include "engraver-group-engraver.hh"
14 #include "accidental-placement.hh"
15 #include "side-position-interface.hh"
16 #include "engraver.hh"
17 #include "arpeggio.hh"
20 #include "translator-group.hh"
25 FIXME: should not compute vertical positioning of accidentals, but
26 get them from the noteheads
28 The algorithm for accidentals should be documented, and made
33 struct Accidental_entry
{
37 Translator_group
*origin_
;
42 Accidental_entry::Accidental_entry()
51 struct Accidental_engraver
: Engraver
{
53 TRANSLATOR_DECLARATIONS (Accidental_engraver
);
54 virtual void process_music ();
55 virtual void acknowledge_grob (Grob_info
);
56 virtual void stop_translation_timestep ();
57 virtual void initialize ();
58 virtual void process_acknowledged_grobs ();
59 virtual void finalize ();
65 This is not a property, and it is not protected. This poses a
66 very small risk of the value being GC'd from under us.
71 Urgh. Since the accidentals depend on lots of variables, we have to
72 store all information before we can really create the accidentals.
74 Link_array
<Grob
> arpeggios_
;
76 Grob
* accidental_placement_
;
82 Array
<Accidental_entry
> accidentals_
;
84 Link_array
<Grob
> ties_
;
90 static void set_property_on_children (Translator_group
* trans
, const char * sym
, SCM val
)
92 trans
->set_property (sym
, val
);
93 for (SCM p
= trans
-> trans_group_list_
; gh_pair_p (p
); p
= ly_cdr(p
)) {
94 Translator_group
*trg
= dynamic_cast<Translator_group
*> (unsmob_translator (ly_car (p
)));
95 set_property_on_children(trg
,sym
,ly_deep_copy(val
));
99 Accidental_engraver::Accidental_engraver ()
101 accidental_placement_
= 0;
102 last_keysig_
= SCM_EOL
;
106 Accidental_engraver::initialize ()
108 last_keysig_
= get_property ("keySignature");
110 Translator_group
* trans_
= daddy_trans_
;
113 trans_
-> set_property ("localKeySignature", ly_deep_copy (last_keysig_
));
114 trans_
= trans_
->daddy_trans_
;
116 set_property_on_children (daddy_trans_
,"localKeySignature", last_keysig_
);
121 calculates the number of accidentals on basis of the current local key sig
123 Returns number of accidentals (0, 1 or 2).
124 Negative (-1 or -2) if accidental has changed.
128 number_accidentals (SCM sig
, Note_req
* note
, Pitch
*pitch
, SCM curbarnum
, SCM lazyness
,
129 bool ignore_octave_b
)
131 int n
= pitch
->notename_
;
132 int o
= pitch
->octave_
;
133 int a
= pitch
->alteration_
;
134 int curbarnum_i
= gh_scm2int (curbarnum
);
139 prev
= ly_assoc_cdr (scm_int2num (n
), sig
);
141 prev
= gh_assoc (gh_cons (scm_int2num (o
), scm_int2num (n
)), sig
);
143 /* should really be true unless prev == SCM_BOOL_F */
144 if (gh_pair_p (prev
) && gh_pair_p (ly_cdr (prev
)))
146 accbarnum_i
= gh_scm2int (ly_cddr (prev
));
147 prev
= gh_cons (ly_car (prev
), ly_cadr (prev
));
150 /* If an accidental was not found or the accidental was too old */
151 if (prev
== SCM_BOOL_F
||
152 (gh_number_p (lazyness
) && curbarnum_i
> accbarnum_i
+ gh_scm2int (lazyness
)))
153 prev
= gh_assoc (scm_int2num (n
), sig
);
156 SCM prev_acc
= (prev
== SCM_BOOL_F
) ? scm_int2num (0) : ly_cdr (prev
);
158 int p
= gh_number_p (prev_acc
) ? gh_scm2int (prev_acc
) : 0;
162 && !to_boolean (note
->get_mus_property ("force-accidental"))
163 && gh_number_p (prev_acc
))
165 else if ( (abs (a
)<abs (p
) || p
*a
<0) && a
!= 0 )
170 return a
== p
? num
: -num
;
174 number_accidentals (Note_req
* note
, Pitch
*pitch
, Translator_group
* origin
,
175 SCM accidentals
, SCM curbarnum
)
180 if (gh_pair_p (accidentals
) && !gh_symbol_p (ly_car (accidentals
)))
181 warning (_f ("Accidental typesetting list must begin with context-name: %s",
182 ly_scm2string (ly_car (accidentals
)).to_str0 ()));
184 while (gh_pair_p (accidentals
) && origin
)
186 // If pair then it is a new accidentals typesetting rule to be checked
187 if (gh_pair_p (ly_car (accidentals
)))
189 SCM type
= gh_caar (accidentals
);
190 SCM lazyness
= gh_cdar (accidentals
);
191 SCM localsig
= origin
->get_property ("localKeySignature");
194 gh_eq_p (ly_symbol2scm ("same-octave"), type
);
196 gh_eq_p (ly_symbol2scm ("any-octave"), type
);
198 if (same_octave_b
|| any_octave_b
)
200 int n
= number_accidentals
201 (localsig
, note
, pitch
, curbarnum
, lazyness
, any_octave_b
);
202 diff
= diff
|| (n
< 0);
203 number
= max (number
, abs (n
));
206 warning (_f ("unknown accidental typesetting: %s. Ignored",
207 ly_symbol2string (type
).to_str0 ()));
212 if symbol then it is a context name. Scan parent contexts to find it.
214 else if (gh_symbol_p (ly_car (accidentals
)))
216 String context
= ly_symbol2string (ly_car (accidentals
));
218 while (origin
&& !origin
->is_alias_b (context
))
219 origin
= origin
->daddy_trans_
;
222 warning (_f ("Symbol is not a parent context: %s. Ignored",
223 context
.to_str0 ()));
225 else warning (_f ("Accidental typesetting must be pair or context-name: %s",
226 ly_scm2string (ly_car (accidentals
)).to_str0 ()));
228 accidentals
= ly_cdr (accidentals
);
230 return diff
? -number
: number
;
234 Accidental_engraver::process_acknowledged_grobs ()
236 if (accidentals_
.size () && !accidentals_
.top().done_
)
238 //SCM localsig = get_property ("localKeySignature");
239 SCM accidentals
= get_property ("autoAccidentals");
240 SCM cautionaries
= get_property ("autoCautionaries");
241 SCM barnum
= get_property ("currentBarNumber");
243 bool extra_natural_b
= get_property ("extraNatural") == SCM_BOOL_T
;
244 for (int i
= 0; i
< accidentals_
.size (); i
++)
246 if (accidentals_
[i
].done_
)
248 accidentals_
[i
].done_
= true;
249 Grob
* support
= accidentals_
[i
].head_
;
250 Note_req
* note
= accidentals_
[i
].melodic_
;
251 Translator_group
* origin
= accidentals_
[i
].origin_
;
253 Pitch
* pitch
= unsmob_pitch (note
->get_mus_property ("pitch"));
254 int num
= number_accidentals (note
, pitch
, origin
, accidentals
, barnum
);
255 int num_caut
= number_accidentals (note
, pitch
, origin
, cautionaries
, barnum
);
256 bool cautionary
= to_boolean (note
->get_mus_property ("cautionary"));
258 if (abs (num_caut
) > abs (num
))
264 bool different
= num
< 0;
267 /* see if there's a tie that "changes" the accidental */
268 /* works because if there's a tie, the note to the left
269 is of the same pitch as the actual note */
271 Grob
*tie_break_reminder
= 0;
272 bool tie_changes
= false;
273 for (int j
= 0; j
< ties_
.size (); j
++)
274 if (support
== Tie::head (ties_
[j
], RIGHT
))
276 tie_changes
= different
;
278 /* Enable accidentals for broken tie
280 We only want an accidental on a broken tie,
281 if the tie changes the accidental.
283 Maybe check property noTieBreakForceAccidental? */
285 tie_break_reminder
= ties_
[j
];
291 Grob
* a
= new Item (get_property ("Accidental"));
292 a
->set_parent (support
, Y_AXIS
);
294 if (!accidental_placement_
)
296 accidental_placement_
= new Item (get_property ("AccidentalPlacement"));
297 announce_grob (accidental_placement_
, a
->self_scm());
300 Accidental_placement::add_accidental (accidental_placement_
, a
);
301 announce_grob (a
, SCM_EOL
);
304 SCM accs
= gh_cons (scm_int2num (pitch
->alteration_
), SCM_EOL
);
305 if (num
== 2 && extra_natural_b
)
306 accs
= gh_cons (scm_int2num (0), accs
);
310 add cautionary option in accidental.
315 a
->set_grob_property ("cautionary", SCM_BOOL_T
);
318 if (tie_break_reminder
)
321 a
->set_grob_property ("tie", tie_break_reminder
->self_scm());
325 support
->set_grob_property ("accidental-grob", a
->self_scm ());
327 a
->set_grob_property ("accidentals", accs
);
328 accidentals_
[i
].accidental_
= a
;
330 We add the accidentals to the support of the arpeggio, so it is put left of the
334 for (int i
= 0; i
< arpeggios_
.size (); i
++)
335 Side_position_interface::add_support (arpeggios_
[i
], a
);
340 We should not record the accidental if it is the first
341 note and it is tied from the previous measure.
343 Checking whether it is tied also works mostly, but will it
344 always do the correct thing?
348 int n
= pitch
->notename_
;
349 int o
= pitch
->octave_
;
350 int a
= pitch
->alteration_
;
351 SCM on_s
= gh_cons (scm_int2num (o
), scm_int2num (n
));
356 Perhaps only check translators mentioned in the auto-accidentals?
361 I'd be surprised if the impact of this would be
362 measurable. Anyway, it seems localsig doesn't change
363 every time-step, but a set_property() is done every
364 time. We could save on that, probably.
373 SCM localsig
= origin
->get_property ("localKeySignature");
377 Remember an alteration that is different both from
378 that of the tied note and of the key signature.
380 localsig
= ly_assoc_front_x
381 (localsig
, on_s
, gh_cons (SCM_BOOL_T
, barnum
));
386 not really really correct if there are more than one
387 noteheads with the same notename.
389 localsig
= ly_assoc_front_x
390 (localsig
, on_s
, gh_cons (scm_int2num (a
), barnum
));
392 origin
->set_property ("localKeySignature", localsig
);
393 origin
= origin
->daddy_trans_
;
400 Accidental_engraver::finalize ()
406 Accidental_engraver::stop_translation_timestep ()
408 for (int i
= 0; i
< accidentals_
.size(); i
++)
410 Grob
*a
= accidentals_
[i
].accidental_
;
417 if (accidental_placement_
)
418 typeset_grob(accidental_placement_
);
419 accidental_placement_
= 00;
421 accidentals_
.clear();
427 Accidental_engraver::acknowledge_grob (Grob_info info
)
429 Note_req
* note
= dynamic_cast <Note_req
*> (info
.music_cause ());
431 if (note
&& Rhythmic_head::has_interface (info
.grob_
))
433 Accidental_entry entry
;
434 entry
.head_
= info
.grob_
;
435 entry
.origin_
= info
.origin_trans_
->daddy_trans_
;
436 entry
.melodic_
= note
;
438 accidentals_
.push (entry
);
440 else if (Tie::has_interface (info
.grob_
))
442 ties_
.push (info
.grob_
);
444 else if (Arpeggio::has_interface (info
.grob_
))
446 arpeggios_
.push (info
.grob_
);
452 Accidental_engraver::process_music ()
454 SCM sig
= get_property ("keySignature");
456 /* Detect key sig changes.
457 Update all parents and children
459 if (last_keysig_
!= sig
)
461 Translator_group
* trans_
= daddy_trans_
;
464 trans_
-> set_property ("localKeySignature", ly_deep_copy (sig
));
465 trans_
= trans_
->daddy_trans_
;
467 set_property_on_children(daddy_trans_
,"localKeySignature", sig
);
477 ENTER_DESCRIPTION (Accidental_engraver
,
478 "Make accidentals. Catches note heads, ties and notices key-change
479 events. Due to interaction with ties (which don't come together
480 with note heads), this needs to be in a context higher than Tie_engraver.",
483 "rhythmic-head-interface tie-interface arpeggio-interface",
484 "localKeySignature extraNatural autoAccidentals autoCautionaries",
485 "localKeySignature");