2 new-accidental-engraver.cc -- implement new_accidental_engraver
4 (c) 1997--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 Modified 2001--2003 by Rune Zedeler <rz@daimi.au.dk>
7 This is an experimental file - producing correct accidentals but
8 unfortunately ruining the spacing. -rz
16 #include "rhythmic-head.hh"
17 #include "engraver-group-engraver.hh"
18 #include "accidental-placement.hh"
19 #include "side-position-interface.hh"
20 #include "engraver.hh"
21 #include "arpeggio.hh"
24 #include "translator-group.hh"
29 FIXME: should not compute vertical positioning of accidentals, but
30 get them from the noteheads
32 The algorithm for accidentals should be documented, and made
37 struct New_accidental_entry
{
39 int number_accidentals_
;
40 int number_cautionaries_
;
44 Translator_group
*origin_
;
46 New_accidental_entry();
49 New_accidental_entry::New_accidental_entry()
52 number_accidentals_
= 0;
53 number_cautionaries_
= 0;
61 struct New_accidental_engraver
: Engraver
{
63 TRANSLATOR_DECLARATIONS (New_accidental_engraver
);
64 virtual void process_music ();
65 virtual void acknowledge_grob (Grob_info
);
66 virtual void stop_translation_timestep ();
67 virtual void initialize ();
68 virtual void process_acknowledged_grobs ();
69 virtual void finalize ();
70 virtual void process_grobs_first_pass ();
71 virtual void process_grobs_second_pass ();
78 This is not a property, and it is not protected. This poses a
79 very small risk of the value being GC'd from under us.
84 Urgh. Since the accidentals depend on lots of variables, we have to
85 store all information before we can really create the accidentals.
87 Link_array
<Grob
> arpeggios_
;
89 Grob
* accidental_placement_
;
95 Array
<New_accidental_entry
> accidentals_
;
97 Link_array
<Grob
> ties_
;
103 New_accidental_engraver::New_accidental_engraver ()
105 accidental_placement_
= 0;
106 last_keysig_
= SCM_EOL
;
109 /* inserts the source alist into the destination alist, erasing old entries.
110 result is: dest = merged
112 static SCM
merge_alists_front_x (SCM src
, SCM dest
) {
114 dest
= merge_alists_front_x(ly_cdr(src
),dest
);
115 dest
= ly_assoc_front_x(dest
, ly_caar(src
), ly_cdar(src
));
120 static void merge_property_on_children (Translator_group
* trans
,
121 const char * from_sym
, const char * to_sym
)
123 SCM from
= trans
->get_property(from_sym
);
124 SCM to
= trans
->get_property(to_sym
);
125 to
= merge_alists_front_x(from
, to
);
126 trans
->set_property (to_sym
, to
);
127 trans
->set_property (from_sym
, SCM_EOL
);
128 for (SCM p
= trans
-> trans_group_list_
; gh_pair_p (p
); p
= ly_cdr(p
)) {
129 Translator_group
*trg
= dynamic_cast<Translator_group
*> (unsmob_translator (ly_car (p
)));
130 merge_property_on_children(trg
, from_sym
, to_sym
);
134 static void merge_property_on_family (Translator_group
* trans
,
135 const char * from_sym
, const char * to_sym
)
137 merge_property_on_children (trans
, from_sym
, to_sym
);
138 trans
= trans
->daddy_trans_
;
141 SCM from
= trans
->get_property(from_sym
);
142 SCM to
= trans
->get_property(to_sym
);
143 to
= merge_alists_front_x(from
, to
);
144 trans
->set_property (to_sym
, to
);
145 trans
->set_property (from_sym
, SCM_EOL
);
146 trans
= trans
->daddy_trans_
;
150 static void set_property_on_children (Translator_group
* trans
, const char * sym
, SCM val
)
152 trans
->set_property (sym
, val
);
153 for (SCM p
= trans
-> trans_group_list_
; gh_pair_p (p
); p
= ly_cdr(p
)) {
154 Translator_group
*trg
= dynamic_cast<Translator_group
*> (unsmob_translator (ly_car (p
)));
155 set_property_on_children(trg
,sym
,ly_deep_copy(val
));
159 static void set_property_on_family(Translator_group
* trans
, const char * sym
, SCM val
)
161 set_property_on_children (trans
, sym
, val
);
162 trans
= trans
->daddy_trans_
;
165 trans
-> set_property (sym
, ly_deep_copy (val
));
166 trans
= trans
->daddy_trans_
;
171 New_accidental_engraver::initialize ()
173 // to ensure that process_music will initialize last_keysig_
174 last_keysig_
= SCM_BOOL_F
;
178 calculates the number of accidentals on basis of the current local key sig
180 Returns number of accidentals (0, 1 or 2).
181 Negative (-1 or -2) if accidental has changed.
185 number_accidentals (SCM sig
, Music
* note
, Pitch
*pitch
, SCM curbarnum
, SCM lazyness
,
186 bool ignore_octave_b
)
188 int n
= pitch
->get_notename ();
189 int o
= pitch
->get_octave ();
190 int a
= pitch
->get_alteration ();
191 int curbarnum_i
= gh_scm2int (curbarnum
);
196 prev
= ly_assoc_cdr (gh_int2scm (n
), sig
);
198 prev
= scm_assoc (gh_cons (gh_int2scm (o
), gh_int2scm (n
)), sig
);
200 /* should really be true unless prev == SCM_BOOL_F */
201 if (gh_pair_p (prev
) && gh_pair_p (ly_cdr (prev
)))
203 accbarnum_i
= gh_scm2int (ly_cddr (prev
));
204 prev
= gh_cons (ly_car (prev
), ly_cadr (prev
));
207 /* If an accidental was not found or the accidental was too old */
208 if (prev
== SCM_BOOL_F
||
209 (gh_number_p (lazyness
) && curbarnum_i
> accbarnum_i
+ gh_scm2int (lazyness
)))
210 prev
= scm_assoc (gh_int2scm (n
), sig
);
213 SCM prev_acc
= (prev
== SCM_BOOL_F
) ? gh_int2scm (0) : ly_cdr (prev
);
215 int p
= gh_number_p (prev_acc
) ? gh_scm2int (prev_acc
) : 0;
219 && !to_boolean (note
->get_mus_property ("force-accidental"))
220 && gh_number_p (prev_acc
))
222 else if ( (abs (a
)<abs (p
) || p
*a
<0) && a
!= 0 )
227 return a
== p
? num
: -num
;
231 number_accidentals (Music
* note
, Pitch
*pitch
, Translator_group
* origin
,
232 SCM accidentals
, SCM curbarnum
)
237 if (gh_pair_p (accidentals
) && !gh_symbol_p (ly_car (accidentals
)))
238 warning (_f ("Accidental typesetting list must begin with context-name: %s",
239 ly_scm2string (ly_car (accidentals
)).to_str0 ()));
241 while (gh_pair_p (accidentals
) && origin
)
243 // If pair then it is a new accidentals typesetting rule to be checked
244 if (gh_pair_p (ly_car (accidentals
)))
246 SCM type
= gh_caar (accidentals
);
247 SCM lazyness
= gh_cdar (accidentals
);
248 SCM localsig
= origin
->get_property ("localKeySignature");
251 gh_eq_p (ly_symbol2scm ("same-octave"), type
);
253 gh_eq_p (ly_symbol2scm ("any-octave"), type
);
255 if (same_octave_b
|| any_octave_b
)
257 int n
= number_accidentals
258 (localsig
, note
, pitch
, curbarnum
, lazyness
, any_octave_b
);
259 diff
= diff
|| (n
< 0);
260 number
= max (number
, abs (n
));
263 warning (_f ("unknown accidental typesetting: %s. Ignored",
264 ly_symbol2string (type
).to_str0 ()));
269 if symbol then it is a context name. Scan parent contexts to find it.
271 else if (gh_symbol_p (ly_car (accidentals
)))
273 SCM context
= ly_car (accidentals
);
275 while (origin
&& !origin
->is_alias_b (context
))
276 origin
= origin
->daddy_trans_
;
279 warning (_f ("Symbol is not a parent context: %s. Ignored",
280 ly_symbol2string (context
).to_str0 ()));
282 else warning (_f ("Accidental typesetting must be pair or context-name: %s",
283 ly_scm2string (ly_car (accidentals
)).to_str0 ()));
285 accidentals
= ly_cdr (accidentals
);
287 return diff
? -number
: number
;
292 Perhaps one should join the two functions into one function taking an
294 OTOH even though code would be smaller, spaghetti-level would increase.
297 New_accidental_engraver::process_grobs_first_pass ()
299 SCM accidentals
= get_property ("autoAccidentals");
300 SCM cautionaries
= get_property ("autoCautionaries");
301 SCM barnum
= get_property ("currentBarNumber");
303 for (int i
= 0; i
< accidentals_
.size (); i
++)
305 if (accidentals_
[i
].pass_done_
>= 1)
307 accidentals_
[i
].pass_done_
= 1;
309 Grob
* support
= accidentals_
[i
].head_
;
310 Music
* note
= accidentals_
[i
].melodic_
;
311 Translator_group
* origin
= accidentals_
[i
].origin_
;
312 Pitch
* pitch
= unsmob_pitch (note
->get_mus_property ("pitch"));
315 num
= number_accidentals (note
, pitch
, origin
, accidentals
, barnum
);
316 accidentals_
[i
].number_accidentals_
= abs(num
);
317 accidentals_
[i
].different_
= num
<0;
319 num
= number_accidentals (note
, pitch
, origin
, cautionaries
, barnum
);
320 accidentals_
[i
].number_cautionaries_
= abs(num
);
321 accidentals_
[i
].different_
= accidentals_
[i
].different_
|| num
<0;
323 bool tie_changes
= false;
324 for (int j
= 0; j
< ties_
.size (); j
++)
325 if (support
== Tie::head (ties_
[j
], RIGHT
))
326 tie_changes
= accidentals_
[i
].different_
;
327 int n
= pitch
->get_notename ();
328 int o
= pitch
->get_octave ();
329 int a
= pitch
->get_alteration ();
330 SCM o_s
= gh_int2scm (o
);
331 SCM n_s
= gh_int2scm (n
);
332 SCM on_s
= gh_cons (o_s
,n_s
);
336 SCM sigch
= origin
->get_property ("localKeySignatureChanges");
340 Remember an alteration that is different both from
341 that of the tied note and of the key signature.
345 alt
= gh_int2scm (a
);
346 bool other_alt_same_oct
= false;
347 bool other_alt_any_oct
= false;
348 for (SCM j
= sigch
; gh_pair_p(j
); j
= ly_cdr(j
)) {
349 SCM entry
= ly_car(j
);
350 /* if same notename has a different alt already recorded: */
351 if(gh_equal_p(ly_cdar(entry
),n_s
) && !gh_equal_p(ly_cadr(entry
),alt
))
353 /* if it is also in same octave */
354 if(gh_equal_p(ly_caar(entry
),o_s
))
355 other_alt_same_oct
= true;
357 other_alt_any_oct
= true;
360 if(other_alt_same_oct
)
362 sigch
= ly_assoc_front_x (sigch
, on_s
, gh_cons(alt
,barnum
));
363 if(other_alt_any_oct
&& !other_alt_same_oct
) {
364 sigch
= ly_assoc_front_x (sigch
, on_s
, gh_cons(SCM_BOOL_T
,barnum
));
366 origin
->set_property ("localKeySignatureChanges", sigch
);
367 origin
= origin
->daddy_trans_
;
373 New_accidental_engraver::process_grobs_second_pass ()
375 SCM accidentals
= get_property ("autoAccidentals");
376 SCM cautionaries
= get_property ("autoCautionaries");
377 SCM barnum
= get_property ("currentBarNumber");
379 bool extra_natural_b
= get_property ("extraNatural") == SCM_BOOL_T
;
380 for (int i
= 0; i
< accidentals_
.size (); i
++)
382 if (accidentals_
[i
].pass_done_
>= 2)
384 accidentals_
[i
].pass_done_
= 2;
385 Grob
* support
= accidentals_
[i
].head_
;
386 Music
* note
= accidentals_
[i
].melodic_
;
387 Translator_group
* origin
= accidentals_
[i
].origin_
;
389 Pitch
* pitch
= unsmob_pitch (note
->get_mus_property ("pitch"));
392 num
= number_accidentals (note
, pitch
, origin
, accidentals
, barnum
);
393 accidentals_
[i
].number_accidentals_
=
394 max (accidentals_
[i
].number_accidentals_
, abs(num
));
395 accidentals_
[i
].different_
= accidentals_
[i
].different_
|| num
<0;
397 num
= number_accidentals (note
, pitch
, origin
, cautionaries
, barnum
);
398 accidentals_
[i
].number_cautionaries_
=
399 max (accidentals_
[i
].number_cautionaries_
, abs(num
));
400 accidentals_
[i
].different_
= accidentals_
[i
].different_
|| num
<0;
403 bool cautionary
= to_boolean (note
->get_mus_property ("cautionary"));
405 if (accidentals_
[i
].number_cautionaries_
>accidentals_
[i
].number_accidentals_
)
407 num
= accidentals_
[i
].number_cautionaries_
;
411 num
= accidentals_
[i
].number_accidentals_
;
413 bool tie_changes
= false;
414 Grob
*tie_break_reminder
= 0;
415 for (int j
= 0; j
< ties_
.size (); j
++)
416 if (support
== Tie::head (ties_
[j
], RIGHT
))
418 tie_changes
= accidentals_
[i
].different_
;
419 tie_break_reminder
= ties_
[j
];
424 Grob
* a
= new Item (get_property ("Accidental"));
425 a
->set_parent (support
, Y_AXIS
);
427 if (!accidental_placement_
)
429 accidental_placement_
= new Item (get_property ("AccidentalPlacement"));
430 announce_grob (accidental_placement_
, a
->self_scm());
433 Accidental_placement::add_accidental (accidental_placement_
, a
);
434 announce_grob (a
, SCM_EOL
);
437 SCM accs
= gh_cons (gh_int2scm (pitch
->get_alteration ()), SCM_EOL
);
438 if (num
== 2 && extra_natural_b
)
439 accs
= gh_cons (gh_int2scm (0), accs
);
443 a
->set_grob_property ("cautionary", SCM_BOOL_T
);
446 if (tie_break_reminder
)
448 a
->set_grob_property ("tie", tie_break_reminder
->self_scm());
452 support
->set_grob_property ("accidental-grob", a
->self_scm ());
454 a
->set_grob_property ("accidentals", accs
);
455 accidentals_
[i
].accidental_
= a
;
457 We add the accidentals to the support of the arpeggio, so it is
458 put left of the accidentals.
460 for (int i
= 0; i
< arpeggios_
.size (); i
++)
461 Side_position_interface::add_support (arpeggios_
[i
], a
);
467 New_accidental_engraver::process_acknowledged_grobs ()
469 if (accidentals_
.size () && accidentals_
.top().pass_done_
< 1)
470 process_grobs_first_pass ();
474 New_accidental_engraver::finalize ()
480 New_accidental_engraver::stop_translation_timestep ()
482 merge_property_on_family(daddy_trans_
, "localKeySignatureChanges", "localKeySignature");
483 if (accidentals_
.size () && accidentals_
.top().pass_done_
< 2)
484 process_grobs_second_pass ();
486 for (int i
= 0; i
< accidentals_
.size(); i
++)
488 Grob
*a
= accidentals_
[i
].accidental_
;
495 if (accidental_placement_
)
496 typeset_grob(accidental_placement_
);
497 accidental_placement_
= 00;
499 set_property_on_family(daddy_trans_
, "localKeySignatureChanges", SCM_EOL
);
500 accidentals_
.clear();
506 New_accidental_engraver::acknowledge_grob (Grob_info info
)
508 Music
* note
= info
.music_cause ();
511 && note
->is_mus_type ("note-event")
512 && Rhythmic_head::has_interface (info
.grob_
))
514 New_accidental_entry entry
;
515 entry
.head_
= info
.grob_
;
516 entry
.origin_
= info
.origin_trans_
->daddy_trans_
;
517 entry
.melodic_
= note
;
519 accidentals_
.push (entry
);
521 else if (Tie::has_interface (info
.grob_
))
523 ties_
.push (info
.grob_
);
525 else if (Arpeggio::has_interface (info
.grob_
))
527 arpeggios_
.push (info
.grob_
);
533 New_accidental_engraver::process_music ()
535 SCM sig
= get_property ("keySignature");
537 /* Detect key sig changes.
538 Update all parents and children
540 if (last_keysig_
!= sig
)
542 set_property_on_family(daddy_trans_
, "localKeySignature", sig
);
543 set_property_on_family(daddy_trans_
, "localKeySignatureChanges", SCM_EOL
); //This souldn't be neccesary
552 ENTER_DESCRIPTION (New_accidental_engraver
,
553 "Make accidentals. Catches note heads, ties and notices key-change "
554 "events. Due to interaction with ties (which don't come together "
555 "with note heads), this needs to be in a context higher than Tie_engraver.",
559 "rhythmic-head-interface tie-interface arpeggio-interface",
560 "localKeySignature localKeySignatureChanges extraNatural autoAccidentals autoCautionaries",
561 "localKeySignature localKeySignatureChanges");