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"
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 ();
61 Local_key_engraver::Local_key_engraver ()
65 last_keysig_
= SCM_EOL
;
69 Local_key_engraver::initialize ()
71 last_keysig_
= get_property ("keySignature");
72 daddy_trans_l_
->set_property ("localKeySignature", last_keysig_
);
76 Local_key_engraver::create_grobs ()
78 if (!key_item_p_
&& mel_l_arr_
.size ())
80 SCM localsig
= get_property ("localKeySignature");
82 for (int i
=0; i
< mel_l_arr_
.size (); i
++)
84 Grob
* support_l
= support_l_arr_
[i
];
85 Note_req
* note_l
= mel_l_arr_
[i
];
87 int n
= unsmob_pitch (note_l
->get_mus_property ("pitch"))->notename_i_
;
88 int o
= unsmob_pitch (note_l
->get_mus_property ("pitch"))->octave_i () ;
89 int a
= unsmob_pitch (note_l
->get_mus_property ("pitch"))->alteration_i_
;
91 /* see if there's a tie that "changes" the accidental */
92 /* works because if there's a tie, the note to the left
93 is of the same pitch as the actual note */
95 SCM prev
= scm_assoc (gh_cons (gh_int2scm (o
), gh_int2scm (n
)), localsig
);
96 if (prev
== SCM_BOOL_F
)
97 prev
= scm_assoc (gh_int2scm (n
), localsig
);
98 SCM prev_acc
= (prev
== SCM_BOOL_F
) ? gh_int2scm (0) : gh_cdr (prev
);
99 bool different
= !gh_equal_p (prev_acc
, gh_int2scm (a
));
100 int p
= gh_number_p (prev_acc
) ? gh_scm2int (prev_acc
) : 0;
102 Grob
*tie_break_reminder
= 0;
103 bool tie_changes
= false;
104 for (int i
=0; i
< tie_l_arr_
.size (); i
++)
105 if (support_l
== Tie::head (tie_l_arr_
[i
], RIGHT
))
107 tie_changes
= different
;
108 /* Enable accidentals for broken tie
110 We only want an accidental on a broken tie,
111 if the tie changes the accidental.
113 Maybe check property noTieBreakForceAccidental? */
115 tie_break_reminder
= tie_l_arr_
[i
];
119 /* When do we want accidentals:
121 1. when property force-accidental is set, and not
123 2. when different and not tie-changes
124 3. maybe when at end of a tie: we must later see if
125 we're after a line break */
126 if (( (to_boolean (note_l
->get_mus_property ("force-accidental"))
129 || tie_break_reminder
)
133 key_item_p_
= new Item (get_property ("Accidentals"));
134 Local_key_item::set_interface (key_item_p_
);
137 Staff_symbol_referencer::set_interface (key_item_p_
);
138 SCM c0
= get_property ("centralCPosition");
139 if (gh_number_p (c0
))
140 Staff_symbol_referencer::set_position (key_item_p_
, gh_scm2int (c0
));
142 announce_grob (key_item_p_
, 0);
147 sign (p
) * (p
- a
) == 1
150 Local_key_item::add_pitch (key_item_p_
, *unsmob_pitch (note_l
->get_mus_property ("pitch")),
151 to_boolean (note_l
->get_mus_property ("cautionary")),
154 Side_position_interface::add_support (key_item_p_
,support_l
);
158 We should not record the accidental if it is the first
159 note and it is tied from the previous measure.
161 Checking whether it is tied also works mostly, but will it
162 always do the correct thing?
165 bool forget
= to_boolean (get_property ("forgetAccidentals"));
169 Remember an alteration that is different both from
170 that of the tied note and of the key signature.
173 localsig
= scm_assoc_set_x (localsig
, gh_cons (gh_int2scm (o
),
181 not really really correct if there are more than one
182 noteheads with the same notename.
184 localsig
= scm_assoc_set_x (localsig
, gh_cons (gh_int2scm (o
),
194 daddy_trans_l_
->set_property ("localKeySignature", localsig
);
201 Hmm. Which one has to be on the left?
203 On which left, code or paper?
205 (Arpeggios are engraved left of accidentals, of course.)
207 for (int i
=0; i
< arpeggios_
.size (); i
++)
208 Side_position_interface::add_support (arpeggios_
[i
], key_item_p_
);
215 Local_key_engraver::finalize ()
221 Local_key_engraver::stop_translation_timestep ()
225 for (int i
=0; i
< support_l_arr_
.size (); i
++)
226 Side_position_interface::add_support (key_item_p_
,support_l_arr_
[i
]);
228 typeset_grob (key_item_p_
);
236 support_l_arr_
.clear ();
237 forced_l_arr_
.clear ();
241 Local_key_engraver::acknowledge_grob (Grob_info info
)
243 Note_req
* note_l
= dynamic_cast <Note_req
*> (info
.req_l_
);
245 if (note_l
&& Rhythmic_head::has_interface (info
.elem_l_
))
247 mel_l_arr_
.push (note_l
);
248 support_l_arr_
.push (info
.elem_l_
);
250 else if (Tie::has_interface (info
.elem_l_
))
252 tie_l_arr_
.push (info
.elem_l_
);
254 else if (Arpeggio::has_interface (info
.elem_l_
))
256 arpeggios_
.push (info
.elem_l_
);
262 ugh. repeated deep_copy generates lots of garbage.
265 Local_key_engraver::process_music ()
267 SCM smp
= get_property ("measurePosition");
268 Moment mp
= (unsmob_moment (smp
)) ? *unsmob_moment (smp
) : Moment (0);
270 SCM sig
= get_property ("keySignature");
273 Detect key sig changes. If we haven't found any, check if at start
274 of measure, and set localKeySignature anyhow. */
275 if (last_keysig_
!= sig
)
277 daddy_trans_l_
->set_property ("localKeySignature", ly_deep_copy (sig
));
280 else if (!mp
.to_bool () )
282 if (!to_boolean (get_property ("noResetKey")))
283 daddy_trans_l_
->set_property ("localKeySignature", ly_deep_copy (sig
));
289 ADD_THIS_TRANSLATOR (Local_key_engraver
);