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_);
141 announce_grob (key_item_p_, 0);
146 sign (p) * (p - a) == 1
149 Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
150 to_boolean (note_l->get_mus_property ("cautionary")),
153 Side_position_interface::add_support (key_item_p_,support_l);
157 We should not record the accidental if it is the first
158 note and it is tied from the previous measure.
160 Checking whether it is tied also works mostly, but will it
161 always do the correct thing?
164 bool forget = to_boolean (get_property ("forgetAccidentals"));
168 Remember an alteration that is different both from
169 that of the tied note and of the key signature.
172 localsig = scm_assoc_set_x (localsig, gh_cons (gh_int2scm (o),
180 not really really correct if there are more than one
181 noteheads with the same notename.
183 localsig = scm_assoc_set_x (localsig, gh_cons (gh_int2scm (o),
193 daddy_trans_l_->set_property ("localKeySignature", localsig);
196 if (key_item_p_ && grace_align_l_)
198 Side_position_interface::add_support (grace_align_l_,key_item_p_);
205 Hmm. Which one has to be on the left?
207 On which left, code or paper?
209 (Arpeggios are engraved left of accidentals, of course.)
211 for (int i=0; i < arpeggios_.size (); i++)
212 Side_position_interface::add_support (arpeggios_[i], key_item_p_);
219 Local_key_engraver::finalize ()
221 // TODO: if grace ? signal accidentals to Local_key_engraver the
225 Local_key_engraver::stop_translation_timestep ()
229 for (int i=0; i < support_l_arr_.size (); i++)
230 Side_position_interface::add_support (key_item_p_,support_l_arr_[i]);
232 typeset_grob (key_item_p_);
240 support_l_arr_.clear ();
241 forced_l_arr_.clear ();
245 Local_key_engraver::acknowledge_grob (Grob_info info)
247 SCM wg= get_property ("weAreGraceContext");
249 bool selfgr = gh_boolean_p (wg) &&gh_scm2bool (wg);
250 bool he_gr = to_boolean (info.elem_l_->get_grob_property ("grace"));
252 Item * item = dynamic_cast<Item*> (info.elem_l_);
253 if (he_gr && !selfgr && item && Grace_align_item::has_interface (item))
255 grace_align_l_ = item;
260 Note_req * note_l = dynamic_cast <Note_req *> (info.req_l_);
262 if (note_l && Rhythmic_head::has_interface (info.elem_l_))
264 mel_l_arr_.push (note_l);
265 support_l_arr_.push (info.elem_l_);
267 else if (Tie::has_interface (info.elem_l_))
269 tie_l_arr_.push (info.elem_l_);
271 else if (Arpeggio::has_interface (info.elem_l_))
273 arpeggios_.push (info.elem_l_);
279 ugh. repeated deep_copy generates lots of garbage.
282 Local_key_engraver::process_music ()
284 SCM smp = get_property ("measurePosition");
285 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
287 SCM sig = get_property ("keySignature");
290 Detect key sig changes. If we haven't found any, check if at start
291 of measure, and set localKeySignature anyhow. */
292 if (last_keysig_ != sig)
294 daddy_trans_l_->set_property ("localKeySignature", ly_deep_copy (sig));
299 if (!to_boolean (get_property ("noResetKey")))
300 daddy_trans_l_->set_property ("localKeySignature", ly_deep_copy (sig));
306 ADD_THIS_TRANSLATOR (Local_key_engraver);