2 new-accidental-engraver.cc -- implement new_accidental_engraver
4 (c) 1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 Modified 2001--2002 by Rune Zedeler <rz@daimi.au.dk>
7 This is an experimental file - producing correct accidentals but
8 unfortunately ruining the spacing. -rz
11 #include "musical-request.hh"
12 #include "command-request.hh"
15 #include "rhythmic-head.hh"
16 #include "engraver-group-engraver.hh"
17 #include "accidental-placement.hh"
18 #include "side-position-interface.hh"
19 #include "engraver.hh"
20 #include "arpeggio.hh"
23 #include "translator-group.hh"
28 FIXME: should not compute vertical positioning of accidentals, but
29 get them from the noteheads
31 The algorithm for accidentals should be documented, and made
36 struct New_accidental_entry
{
38 int number_accidentals_
;
39 int number_cautionaries_
;
43 Translator_group
*origin_
;
45 New_accidental_entry();
48 New_accidental_entry::New_accidental_entry()
51 number_accidentals_
= 0;
52 number_cautionaries_
= 0;
60 struct New_accidental_engraver
: Engraver
{
62 TRANSLATOR_DECLARATIONS (New_accidental_engraver
);
63 virtual void process_music ();
64 virtual void acknowledge_grob (Grob_info
);
65 virtual void stop_translation_timestep ();
66 virtual void initialize ();
67 virtual void process_acknowledged_grobs ();
68 virtual void finalize ();
69 virtual void process_grobs_first_pass ();
70 virtual void process_grobs_second_pass ();
77 This is not a property, and it is not protected. This poses a
78 very small risk of the value being GC'd from under us.
83 Urgh. Since the accidentals depend on lots of variables, we have to
84 store all information before we can really create the accidentals.
86 Link_array
<Grob
> arpeggios_
;
88 Grob
* accidental_placement_
;
94 Array
<New_accidental_entry
> accidentals_
;
96 Link_array
<Grob
> ties_
;
102 New_accidental_engraver::New_accidental_engraver ()
104 accidental_placement_
= 0;
105 last_keysig_
= SCM_EOL
;
108 /* inserts the source alist into the destination alist, erasing old entries.
109 result is: dest = merged
111 static SCM
merge_alists_front_x (SCM src
, SCM dest
) {
113 dest
= merge_alists_front_x(ly_cdr(src
),dest
);
114 dest
= ly_assoc_front_x(dest
, ly_caar(src
), ly_cdar(src
));
119 static void merge_property_on_children (Translator_group
* trans
,
120 const char * from_sym
, const char * to_sym
)
122 SCM from
= trans
->get_property(from_sym
);
123 SCM to
= trans
->get_property(to_sym
);
124 to
= merge_alists_front_x(from
, to
);
125 trans
->set_property (to_sym
, to
);
126 trans
->set_property (from_sym
, SCM_EOL
);
127 for (SCM p
= trans
-> trans_group_list_
; gh_pair_p (p
); p
= ly_cdr(p
)) {
128 Translator_group
*trg
= dynamic_cast<Translator_group
*> (unsmob_translator (ly_car (p
)));
129 merge_property_on_children(trg
, from_sym
, to_sym
);
133 static void merge_property_on_family (Translator_group
* trans
,
134 const char * from_sym
, const char * to_sym
)
136 merge_property_on_children (trans
, from_sym
, to_sym
);
137 trans
= trans
->daddy_trans_
;
140 SCM from
= trans
->get_property(from_sym
);
141 SCM to
= trans
->get_property(to_sym
);
142 to
= merge_alists_front_x(from
, to
);
143 trans
->set_property (to_sym
, to
);
144 trans
->set_property (from_sym
, SCM_EOL
);
145 trans
= trans
->daddy_trans_
;
149 static void set_property_on_children (Translator_group
* trans
, const char * sym
, SCM val
)
151 trans
->set_property (sym
, val
);
152 for (SCM p
= trans
-> trans_group_list_
; gh_pair_p (p
); p
= ly_cdr(p
)) {
153 Translator_group
*trg
= dynamic_cast<Translator_group
*> (unsmob_translator (ly_car (p
)));
154 set_property_on_children(trg
,sym
,ly_deep_copy(val
));
158 static void set_property_on_family(Translator_group
* trans
, const char * sym
, SCM val
)
160 set_property_on_children (trans
, sym
, val
);
161 trans
= trans
->daddy_trans_
;
164 trans
-> set_property (sym
, ly_deep_copy (val
));
165 trans
= trans
->daddy_trans_
;
170 New_accidental_engraver::initialize ()
172 // to ensure that process_music will initialize last_keysig_
173 last_keysig_
= SCM_BOOL_F
;
177 calculates the number of accidentals on basis of the current local key sig
179 Returns number of accidentals (0, 1 or 2).
180 Negative (-1 or -2) if accidental has changed.
184 number_accidentals (SCM sig
, Note_req
* note
, Pitch
*pitch
, SCM curbarnum
, SCM lazyness
,
185 bool ignore_octave_b
)
187 int n
= pitch
->notename_
;
188 int o
= pitch
->octave_
;
189 int a
= pitch
->alteration_
;
190 int curbarnum_i
= gh_scm2int (curbarnum
);
195 prev
= ly_assoc_cdr (gh_int2scm (n
), sig
);
197 prev
= gh_assoc (gh_cons (gh_int2scm (o
), gh_int2scm (n
)), sig
);
199 /* should really be true unless prev == SCM_BOOL_F */
200 if (gh_pair_p (prev
) && gh_pair_p (ly_cdr (prev
)))
202 accbarnum_i
= gh_scm2int (ly_cddr (prev
));
203 prev
= gh_cons (ly_car (prev
), ly_cadr (prev
));
206 /* If an accidental was not found or the accidental was too old */
207 if (prev
== SCM_BOOL_F
||
208 (gh_number_p (lazyness
) && curbarnum_i
> accbarnum_i
+ gh_scm2int (lazyness
)))
209 prev
= gh_assoc (gh_int2scm (n
), sig
);
212 SCM prev_acc
= (prev
== SCM_BOOL_F
) ? gh_int2scm (0) : ly_cdr (prev
);
214 int p
= gh_number_p (prev_acc
) ? gh_scm2int (prev_acc
) : 0;
218 && !to_boolean (note
->get_mus_property ("force-accidental"))
219 && gh_number_p (prev_acc
))
221 else if ( (abs (a
)<abs (p
) || p
*a
<0) && a
!= 0 )
226 return a
== p
? num
: -num
;
230 number_accidentals (Note_req
* note
, Pitch
*pitch
, Translator_group
* origin
,
231 SCM accidentals
, SCM curbarnum
)
236 if (gh_pair_p (accidentals
) && !gh_symbol_p (ly_car (accidentals
)))
237 warning (_f ("Accidental typesetting list must begin with context-name: %s",
238 ly_scm2string (ly_car (accidentals
)).to_str0 ()));
240 while (gh_pair_p (accidentals
) && origin
)
242 // If pair then it is a new accidentals typesetting rule to be checked
243 if (gh_pair_p (ly_car (accidentals
)))
245 SCM type
= gh_caar (accidentals
);
246 SCM lazyness
= gh_cdar (accidentals
);
247 SCM localsig
= origin
->get_property ("localKeySignature");
250 gh_eq_p (ly_symbol2scm ("same-octave"), type
);
252 gh_eq_p (ly_symbol2scm ("any-octave"), type
);
254 if (same_octave_b
|| any_octave_b
)
256 int n
= number_accidentals
257 (localsig
, note
, pitch
, curbarnum
, lazyness
, any_octave_b
);
258 diff
= diff
|| (n
< 0);
259 number
= max (number
, abs (n
));
262 warning (_f ("unknown accidental typesetting: %s. Ignored",
263 ly_symbol2string (type
).to_str0 ()));
268 if symbol then it is a context name. Scan parent contexts to find it.
270 else if (gh_symbol_p (ly_car (accidentals
)))
272 String context
= ly_symbol2string (ly_car (accidentals
));
274 while (origin
&& !origin
->is_alias_b (context
))
275 origin
= origin
->daddy_trans_
;
278 warning (_f ("Symbol is not a parent context: %s. Ignored",
279 context
.to_str0 ()));
281 else warning (_f ("Accidental typesetting must be pair or context-name: %s",
282 ly_scm2string (ly_car (accidentals
)).to_str0 ()));
284 accidentals
= ly_cdr (accidentals
);
286 return diff
? -number
: number
;
291 Perhaps one should join the two functions into one function taking an
293 OTOH even though code would be smaller, spaghetti-level would increase.
296 New_accidental_engraver::process_grobs_first_pass ()
298 SCM accidentals
= get_property ("autoAccidentals");
299 SCM cautionaries
= get_property ("autoCautionaries");
300 SCM barnum
= get_property ("currentBarNumber");
302 for (int i
= 0; i
< accidentals_
.size (); i
++)
304 if (accidentals_
[i
].pass_done_
>= 1)
306 accidentals_
[i
].pass_done_
= 1;
308 Grob
* support
= accidentals_
[i
].head_
;
309 Note_req
* note
= accidentals_
[i
].melodic_
;
310 Translator_group
* origin
= accidentals_
[i
].origin_
;
311 Pitch
* pitch
= unsmob_pitch (note
->get_mus_property ("pitch"));
314 num
= number_accidentals (note
, pitch
, origin
, accidentals
, barnum
);
315 accidentals_
[i
].number_accidentals_
= abs(num
);
316 accidentals_
[i
].different_
= num
<0;
318 num
= number_accidentals (note
, pitch
, origin
, cautionaries
, barnum
);
319 accidentals_
[i
].number_cautionaries_
= abs(num
);
320 accidentals_
[i
].different_
= accidentals_
[i
].different_
|| num
<0;
322 bool tie_changes
= false;
323 for (int j
= 0; j
< ties_
.size (); j
++)
324 if (support
== Tie::head (ties_
[j
], RIGHT
))
325 tie_changes
= accidentals_
[i
].different_
;
326 int n
= pitch
->notename_
;
327 int o
= pitch
->octave_
;
328 int a
= pitch
->alteration_
;
329 SCM o_s
= gh_int2scm (o
);
330 SCM n_s
= gh_int2scm (n
);
331 SCM on_s
= gh_cons (o_s
,n_s
);
335 SCM sigch
= origin
->get_property ("localKeySignatureChanges");
339 Remember an alteration that is different both from
340 that of the tied note and of the key signature.
344 alt
= gh_int2scm (a
);
345 bool other_alt_same_oct
= false;
346 bool other_alt_any_oct
= false;
347 for (SCM j
= sigch
; gh_pair_p(j
); j
= ly_cdr(j
)) {
348 SCM entry
= ly_car(j
);
349 /* if same notename has a different alt already recorded: */
350 if(gh_equal_p(ly_cdar(entry
),n_s
) && !gh_equal_p(ly_cadr(entry
),alt
))
352 /* if it is also in same octave */
353 if(gh_equal_p(ly_caar(entry
),o_s
))
354 other_alt_same_oct
= true;
356 other_alt_any_oct
= true;
359 if(other_alt_same_oct
)
361 sigch
= ly_assoc_front_x (sigch
, on_s
, gh_cons(alt
,barnum
));
362 if(other_alt_any_oct
&& !other_alt_same_oct
) {
363 sigch
= ly_assoc_front_x (sigch
, on_s
, gh_cons(SCM_BOOL_T
,barnum
));
365 origin
->set_property ("localKeySignatureChanges", sigch
);
366 origin
= origin
->daddy_trans_
;
372 New_accidental_engraver::process_grobs_second_pass ()
374 SCM accidentals
= get_property ("autoAccidentals");
375 SCM cautionaries
= get_property ("autoCautionaries");
376 SCM barnum
= get_property ("currentBarNumber");
378 bool extra_natural_b
= get_property ("extraNatural") == SCM_BOOL_T
;
379 for (int i
= 0; i
< accidentals_
.size (); i
++)
381 if (accidentals_
[i
].pass_done_
>= 2)
383 accidentals_
[i
].pass_done_
= 2;
384 Grob
* support
= accidentals_
[i
].head_
;
385 Note_req
* note
= accidentals_
[i
].melodic_
;
386 Translator_group
* origin
= accidentals_
[i
].origin_
;
388 Pitch
* pitch
= unsmob_pitch (note
->get_mus_property ("pitch"));
391 num
= number_accidentals (note
, pitch
, origin
, accidentals
, barnum
);
392 accidentals_
[i
].number_accidentals_
=
393 max (accidentals_
[i
].number_accidentals_
, abs(num
));
394 accidentals_
[i
].different_
= accidentals_
[i
].different_
|| num
<0;
396 num
= number_accidentals (note
, pitch
, origin
, cautionaries
, barnum
);
397 accidentals_
[i
].number_cautionaries_
=
398 max (accidentals_
[i
].number_cautionaries_
, abs(num
));
399 accidentals_
[i
].different_
= accidentals_
[i
].different_
|| num
<0;
402 bool cautionary
= to_boolean (note
->get_mus_property ("cautionary"));
404 if (accidentals_
[i
].number_cautionaries_
>accidentals_
[i
].number_accidentals_
)
406 num
= accidentals_
[i
].number_cautionaries_
;
410 num
= accidentals_
[i
].number_accidentals_
;
412 bool tie_changes
= false;
413 Grob
*tie_break_reminder
= 0;
414 for (int j
= 0; j
< ties_
.size (); j
++)
415 if (support
== Tie::head (ties_
[j
], RIGHT
))
417 tie_changes
= accidentals_
[i
].different_
;
418 tie_break_reminder
= ties_
[j
];
423 Grob
* a
= new Item (get_property ("Accidental"));
424 a
->set_parent (support
, Y_AXIS
);
426 if (!accidental_placement_
)
428 accidental_placement_
= new Item (get_property ("AccidentalPlacement"));
429 announce_grob (accidental_placement_
, a
->self_scm());
432 Accidental_placement::add_accidental (accidental_placement_
, a
);
433 announce_grob (a
, SCM_EOL
);
436 SCM accs
= gh_cons (gh_int2scm (pitch
->alteration_
), SCM_EOL
);
437 if (num
== 2 && extra_natural_b
)
438 accs
= gh_cons (gh_int2scm (0), accs
);
442 a
->set_grob_property ("cautionary", SCM_BOOL_T
);
445 if (tie_break_reminder
)
447 a
->set_grob_property ("tie", tie_break_reminder
->self_scm());
451 support
->set_grob_property ("accidental-grob", a
->self_scm ());
453 a
->set_grob_property ("accidentals", accs
);
454 accidentals_
[i
].accidental_
= a
;
456 We add the accidentals to the support of the arpeggio, so it is
457 put left of the accidentals.
459 for (int i
= 0; i
< arpeggios_
.size (); i
++)
460 Side_position_interface::add_support (arpeggios_
[i
], a
);
466 New_accidental_engraver::process_acknowledged_grobs ()
468 if (accidentals_
.size () && accidentals_
.top().pass_done_
< 1)
469 process_grobs_first_pass ();
473 New_accidental_engraver::finalize ()
479 New_accidental_engraver::stop_translation_timestep ()
481 merge_property_on_family(daddy_trans_
, "localKeySignatureChanges", "localKeySignature");
482 if (accidentals_
.size () && accidentals_
.top().pass_done_
< 2)
483 process_grobs_second_pass ();
485 for (int i
= 0; i
< accidentals_
.size(); i
++)
487 Grob
*a
= accidentals_
[i
].accidental_
;
494 if (accidental_placement_
)
495 typeset_grob(accidental_placement_
);
496 accidental_placement_
= 00;
498 set_property_on_family(daddy_trans_
, "localKeySignatureChanges", SCM_EOL
);
499 accidentals_
.clear();
505 New_accidental_engraver::acknowledge_grob (Grob_info info
)
507 Note_req
* note
= dynamic_cast <Note_req
*> (info
.music_cause ());
509 if (note
&& Rhythmic_head::has_interface (info
.grob_
))
511 New_accidental_entry entry
;
512 entry
.head_
= info
.grob_
;
513 entry
.origin_
= info
.origin_trans_
->daddy_trans_
;
514 entry
.melodic_
= note
;
516 accidentals_
.push (entry
);
518 else if (Tie::has_interface (info
.grob_
))
520 ties_
.push (info
.grob_
);
522 else if (Arpeggio::has_interface (info
.grob_
))
524 arpeggios_
.push (info
.grob_
);
530 New_accidental_engraver::process_music ()
532 SCM sig
= get_property ("keySignature");
534 /* Detect key sig changes.
535 Update all parents and children
537 if (last_keysig_
!= sig
)
539 set_property_on_family(daddy_trans_
, "localKeySignature", sig
);
540 set_property_on_family(daddy_trans_
, "localKeySignatureChanges", SCM_EOL
); //This souldn't be neccesary
549 ENTER_DESCRIPTION (New_accidental_engraver
,
550 "Make accidentals. Catches note heads, ties and notices key-change
551 events. Due to interaction with ties (which don't come together
552 with note heads), this needs to be in a context higher than Tie_engraver.",
555 "rhythmic-head-interface tie-interface arpeggio-interface",
556 "localKeySignature localKeySignatureChanges extraNatural autoAccidentals autoCautionaries",
557 "localKeySignature localKeySignatureChanges");