lilypond-1.5.10
[lilypond.git] / lily / local-key-engraver.cc
blobed71c533773a215e1f84ec607fced5b9577bc72a
1 /*
2 local-key-engraver.cc -- implement Local_key_engraver
4 (c) 1997--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 */
7 #include "musical-request.hh"
8 #include "command-request.hh"
9 #include "local-key-item.hh"
10 #include "item.hh"
11 #include "tie.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"
21 /**
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
28 tweakable.
33 struct Local_key_engraver : Engraver {
34 Item *key_item_p_;
35 protected:
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 ();
43 public:
45 // todo -> property
46 SCM last_keysig_;
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 ()
63 key_item_p_ =0;
65 last_keysig_ = SCM_EOL;
68 void
69 Local_key_engraver::initialize ()
71 last_keysig_ = get_property ("keySignature");
72 daddy_trans_l_->set_property ("localKeySignature", last_keysig_);
75 void
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? */
114 if (different)
115 tie_break_reminder = tie_l_arr_[i];
116 break;
119 /* When do we want accidentals:
121 1. when property force-accidental is set, and not
122 tie_changes
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"))
127 || different)
128 && !tie_changes)
129 || tie_break_reminder)
131 if (!key_item_p_)
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);
146 bool extra_natural =
147 sign (p) * (p - a) == 1
148 && abs (p) == 2;
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")),
152 extra_natural,
153 tie_break_reminder);
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"));
166 if (tie_changes)
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),
174 gh_int2scm (n)),
175 SCM_BOOL_T);
178 else if (!forget)
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),
185 gh_int2scm (n)),
186 gh_int2scm (a));
194 daddy_trans_l_->set_property ("localKeySignature", localsig);
198 if (key_item_p_)
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_);
210 arpeggios_.clear ();
214 void
215 Local_key_engraver::finalize ()
220 void
221 Local_key_engraver::stop_translation_timestep ()
223 if (key_item_p_)
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_);
229 key_item_p_ =0;
233 mel_l_arr_.clear ();
234 arpeggios_.clear ();
235 tie_l_arr_.clear ();
236 support_l_arr_.clear ();
237 forced_l_arr_.clear ();
240 void
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.
264 void
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));
278 last_keysig_ = 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);