2 local-key-engraver.cc -- implement Local_key_engraver
4 (c) 1997--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 #include "musical-request.hh"
8 #include "command-request.hh"
9 #include "local-key-item.hh"
12 #include "rhythmic-head.hh"
13 #include "timing-translator.hh"
14 #include "engraver-group-engraver.hh"
15 #include "grace-align-item.hh"
16 #include "staff-symbol-referencer.hh"
17 #include "side-position-interface.hh"
18 #include "engraver.hh"
19 #include "arpeggio.hh"
24 FIXME: should not compute vertical positioning of accidentals, but
25 get them from the noteheads
27 The algorithm for accidentals should be documented, and made
33 struct Local_key_engraver
: Engraver
{
36 VIRTUAL_COPY_CONS (Translator
);
37 virtual void process_music ();
38 virtual void acknowledge_grob (Grob_info
);
39 virtual void stop_translation_timestep ();
40 virtual void initialize ();
41 virtual void create_grobs ();
42 virtual void finalize ();
49 Urgh. Since the accidentals depend on lots of variables, we have to
50 store all information before we can really create the accidentals.
52 Link_array
<Grob
> arpeggios_
;
54 Link_array
<Note_req
> mel_l_arr_
;
55 Link_array
<Grob
> support_l_arr_
;
56 Link_array
<Item
> forced_l_arr_
;
57 Link_array
<Grob
> tie_l_arr_
;
58 Local_key_engraver ();
60 Item
* grace_align_l_
;
63 Local_key_engraver::Local_key_engraver ()
67 last_keysig_
= SCM_EOL
;
71 Local_key_engraver::initialize ()
73 last_keysig_
= get_property ("keySignature");
74 daddy_trans_l_
->set_property ("localKeySignature", last_keysig_
);
78 Local_key_engraver::create_grobs ()
80 if (!key_item_p_
&& mel_l_arr_
.size ())
82 SCM localsig
= get_property ("localKeySignature");
84 for (int i
=0; i
< mel_l_arr_
.size (); i
++)
86 Grob
* support_l
= support_l_arr_
[i
];
87 Note_req
* note_l
= mel_l_arr_
[i
];
89 int n
= unsmob_pitch (note_l
->get_mus_property ("pitch"))->notename_i_
;
90 int o
= unsmob_pitch (note_l
->get_mus_property ("pitch"))->octave_i () ;
91 int a
= unsmob_pitch (note_l
->get_mus_property ("pitch"))->alteration_i_
;
93 /* see if there's a tie that "changes" the accidental */
94 /* works because if there's a tie, the note to the left
95 is of the same pitch as the actual note */
97 SCM prev
= scm_assoc (gh_cons (gh_int2scm (o
), gh_int2scm (n
)), localsig
);
98 if (prev
== SCM_BOOL_F
)
99 prev
= scm_assoc (gh_int2scm (n
), localsig
);
100 SCM prev_acc
= (prev
== SCM_BOOL_F
) ? gh_int2scm (0) : gh_cdr (prev
);
101 bool different
= !gh_equal_p (prev_acc
, gh_int2scm (a
));
102 int p
= gh_number_p (prev_acc
) ? gh_scm2int (prev_acc
) : 0;
104 Grob
*tie_break_reminder
= 0;
105 bool tie_changes
= false;
106 for (int i
=0; i
< tie_l_arr_
.size (); i
++)
107 if (support_l
== Tie::head (tie_l_arr_
[i
], RIGHT
))
109 tie_changes
= different
;
110 /* Enable accidentals for broken tie
112 We only want an accidental on a broken tie,
113 if the tie changes the accidental.
115 Maybe check property noTieBreakForceAccidental? */
117 tie_break_reminder
= tie_l_arr_
[i
];
121 /* When do we want accidentals:
123 1. when property force-accidental is set, and not
125 2. when different and not tie-changes
126 3. maybe when at end of a tie: we must later see if
127 we're after a line break */
128 if (( (to_boolean (note_l
->get_mus_property ("force-accidental"))
131 || tie_break_reminder
)
135 key_item_p_
= new Item (get_property ("Accidentals"));
136 Local_key_item::set_interface (key_item_p_
);
139 Staff_symbol_referencer::set_interface (key_item_p_
);
140 SCM c0
= get_property ("centralCPosition");
141 if (gh_number_p (c0
))
142 Staff_symbol_referencer::set_position (key_item_p_
, gh_scm2int (c0
));
144 announce_grob (key_item_p_
, 0);
149 sign (p
) * (p
- a
) == 1
152 Local_key_item::add_pitch (key_item_p_
, *unsmob_pitch (note_l
->get_mus_property ("pitch")),
153 to_boolean (note_l
->get_mus_property ("cautionary")),
156 Side_position_interface::add_support (key_item_p_
,support_l
);
160 We should not record the accidental if it is the first
161 note and it is tied from the previous measure.
163 Checking whether it is tied also works mostly, but will it
164 always do the correct thing?
167 bool forget
= to_boolean (get_property ("forgetAccidentals"));
171 Remember an alteration that is different both from
172 that of the tied note and of the key signature.
175 localsig
= scm_assoc_set_x (localsig
, gh_cons (gh_int2scm (o
),
183 not really really correct if there are more than one
184 noteheads with the same notename.
186 localsig
= scm_assoc_set_x (localsig
, gh_cons (gh_int2scm (o
),
196 daddy_trans_l_
->set_property ("localKeySignature", localsig
);
199 if (key_item_p_
&& grace_align_l_
)
201 Side_position_interface::add_support (grace_align_l_
,key_item_p_
);
208 Hmm. Which one has to be on the left?
210 On which left, code or paper?
212 (Arpeggios are engraved left of accidentals, of course.)
214 for (int i
=0; i
< arpeggios_
.size (); i
++)
215 Side_position_interface::add_support (arpeggios_
[i
], key_item_p_
);
222 Local_key_engraver::finalize ()
224 // TODO: if grace ? signal accidentals to Local_key_engraver the
228 Local_key_engraver::stop_translation_timestep ()
232 for (int i
=0; i
< support_l_arr_
.size (); i
++)
233 Side_position_interface::add_support (key_item_p_
,support_l_arr_
[i
]);
235 typeset_grob (key_item_p_
);
243 support_l_arr_
.clear ();
244 forced_l_arr_
.clear ();
248 Local_key_engraver::acknowledge_grob (Grob_info info
)
250 SCM wg
= get_property ("weAreGraceContext");
252 bool selfgr
= gh_boolean_p (wg
) &&gh_scm2bool (wg
);
253 bool he_gr
= to_boolean (info
.elem_l_
->get_grob_property ("grace"));
255 Item
* item
= dynamic_cast<Item
*> (info
.elem_l_
);
256 if (he_gr
&& !selfgr
&& item
&& Grace_align_item::has_interface (item
))
258 grace_align_l_
= item
;
263 Note_req
* note_l
= dynamic_cast <Note_req
*> (info
.req_l_
);
265 if (note_l
&& Rhythmic_head::has_interface (info
.elem_l_
))
267 mel_l_arr_
.push (note_l
);
268 support_l_arr_
.push (info
.elem_l_
);
270 else if (Tie::has_interface (info
.elem_l_
))
272 tie_l_arr_
.push (info
.elem_l_
);
274 else if (Arpeggio::has_interface (info
.elem_l_
))
276 arpeggios_
.push (info
.elem_l_
);
282 ugh. repeated deep_copy generates lots of garbage.
285 Local_key_engraver::process_music ()
287 SCM smp
= get_property ("measurePosition");
288 Moment mp
= (unsmob_moment (smp
)) ? *unsmob_moment (smp
) : Moment (0);
290 SCM sig
= get_property ("keySignature");
293 Detect key sig changes. If we haven't found any, check if at start
294 of measure, and set localKeySignature anyhow. */
295 if (last_keysig_
!= sig
)
297 daddy_trans_l_
->set_property ("localKeySignature", ly_deep_copy (sig
));
302 if (!to_boolean (get_property ("noResetKey")))
303 daddy_trans_l_
->set_property ("localKeySignature", ly_deep_copy (sig
));
309 ADD_THIS_TRANSLATOR (Local_key_engraver
);