* stepmake/stepmake/metafont-rules.make: backport 1.7 fixes.
[lilypond.git] / lily / accidental-engraver.cc
blob6a566d042341cd3b83b433518399dadea0bfc72d
1 /*
2 accidental-engraver.cc -- implement accidental_engraver
4 (c) 1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 Modified 2001-2002 by Rune Zedeler <rz@daimi.au.dk>
6 */
8 #include "musical-request.hh"
9 #include "command-request.hh"
10 #include "item.hh"
11 #include "tie.hh"
12 #include "rhythmic-head.hh"
13 #include "engraver-group-engraver.hh"
14 #include "accidental-placement.hh"
15 #include "side-position-interface.hh"
16 #include "engraver.hh"
17 #include "arpeggio.hh"
18 #include "warn.hh"
20 #include "translator-group.hh"
22 /**
25 FIXME: should not compute vertical positioning of accidentals, but
26 get them from the noteheads
28 The algorithm for accidentals should be documented, and made
29 tweakable.
33 struct Accidental_entry {
34 bool done_;
35 Note_req * melodic_;
36 Grob * accidental_;
37 Translator_group *origin_;
38 Grob* head_;
39 Accidental_entry();
42 Accidental_entry::Accidental_entry()
44 done_ = false;
45 melodic_ =0;
46 accidental_ = 0;
47 origin_ = 0;
48 head_ = 0;
51 struct Accidental_engraver : Engraver {
52 protected:
53 TRANSLATOR_DECLARATIONS (Accidental_engraver);
54 virtual void process_music ();
55 virtual void acknowledge_grob (Grob_info);
56 virtual void stop_translation_timestep ();
57 virtual void initialize ();
58 virtual void process_acknowledged_grobs ();
59 virtual void finalize ();
60 public:
63 TODO -> property.
65 This is not a property, and it is not protected. This poses a
66 very small risk of the value being GC'd from under us.
68 SCM last_keysig_;
71 Urgh. Since the accidentals depend on lots of variables, we have to
72 store all information before we can really create the accidentals.
74 Link_array<Grob> arpeggios_;
76 Grob * accidental_placement_;
80 The next
82 Array<Accidental_entry> accidentals_;
84 Link_array<Grob> ties_;
90 static void set_property_on_children (Translator_group * trans, const char * sym, SCM val)
92 trans->set_property (sym, val);
93 for (SCM p = trans -> trans_group_list_; gh_pair_p (p); p = ly_cdr(p)) {
94 Translator_group *trg = dynamic_cast<Translator_group*> (unsmob_translator (ly_car (p)));
95 set_property_on_children(trg,sym,ly_deep_copy(val));
99 Accidental_engraver::Accidental_engraver ()
101 accidental_placement_ = 0;
102 last_keysig_ = SCM_EOL;
105 void
106 Accidental_engraver::initialize ()
108 last_keysig_ = get_property ("keySignature");
110 Translator_group * trans_ = daddy_trans_;
111 while (trans_)
113 trans_ -> set_property ("localKeySignature", ly_deep_copy (last_keysig_));
114 trans_ = trans_->daddy_trans_;
116 set_property_on_children (daddy_trans_,"localKeySignature", last_keysig_);
121 calculates the number of accidentals on basis of the current local key sig
122 (passed as argument)
123 Returns number of accidentals (0, 1 or 2).
124 Negative (-1 or -2) if accidental has changed.
127 static int
128 number_accidentals (SCM sig, Note_req * note, Pitch *pitch, SCM curbarnum, SCM lazyness,
129 bool ignore_octave_b)
131 int n = pitch->notename_;
132 int o = pitch->octave_;
133 int a = pitch->alteration_;
134 int curbarnum_i = gh_scm2int (curbarnum);
135 int accbarnum_i = 0;
137 SCM prev;
138 if (ignore_octave_b)
139 prev = ly_assoc_cdr (scm_int2num (n), sig);
140 else
141 prev = gh_assoc (gh_cons (scm_int2num (o), scm_int2num (n)), sig);
143 /* should really be true unless prev == SCM_BOOL_F */
144 if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
146 accbarnum_i = gh_scm2int (ly_cddr (prev));
147 prev = gh_cons (ly_car (prev), ly_cadr (prev));
150 /* If an accidental was not found or the accidental was too old */
151 if (prev == SCM_BOOL_F ||
152 (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
153 prev = gh_assoc (scm_int2num (n), sig);
156 SCM prev_acc = (prev == SCM_BOOL_F) ? scm_int2num (0) : ly_cdr (prev);
158 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
160 int num;
161 if (a == p
162 && !to_boolean (note->get_mus_property ("force-accidental"))
163 && gh_number_p (prev_acc))
164 num = 0;
165 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
166 num = 2;
167 else
168 num = 1;
170 return a == p ? num : -num;
173 static int
174 number_accidentals (Note_req * note, Pitch *pitch, Translator_group * origin,
175 SCM accidentals, SCM curbarnum)
177 int number = 0;
179 bool diff = false;
180 if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
181 warning (_f ("Accidental typesetting list must begin with context-name: %s",
182 ly_scm2string (ly_car (accidentals)).to_str0 ()));
184 while (gh_pair_p (accidentals) && origin)
186 // If pair then it is a new accidentals typesetting rule to be checked
187 if (gh_pair_p (ly_car (accidentals)))
189 SCM type = gh_caar (accidentals);
190 SCM lazyness = gh_cdar (accidentals);
191 SCM localsig = origin->get_property ("localKeySignature");
193 bool same_octave_b =
194 gh_eq_p (ly_symbol2scm ("same-octave"), type);
195 bool any_octave_b =
196 gh_eq_p (ly_symbol2scm ("any-octave"), type);
198 if (same_octave_b || any_octave_b)
200 int n = number_accidentals
201 (localsig, note, pitch, curbarnum, lazyness, any_octave_b);
202 diff = diff || (n < 0);
203 number = max (number, abs (n));
205 else
206 warning (_f ("unknown accidental typesetting: %s. Ignored",
207 ly_symbol2string (type).to_str0 ()));
212 if symbol then it is a context name. Scan parent contexts to find it.
214 else if (gh_symbol_p (ly_car (accidentals)))
216 String context = ly_symbol2string (ly_car (accidentals));
218 while (origin && !origin->is_alias_b (context))
219 origin = origin->daddy_trans_;
221 if (!origin)
222 warning (_f ("Symbol is not a parent context: %s. Ignored",
223 context.to_str0 ()));
225 else warning (_f ("Accidental typesetting must be pair or context-name: %s",
226 ly_scm2string (ly_car (accidentals)).to_str0 ()));
228 accidentals = ly_cdr (accidentals);
230 return diff ? -number : number;
233 void
234 Accidental_engraver::process_acknowledged_grobs ()
236 if (accidentals_.size () && !accidentals_.top().done_)
238 //SCM localsig = get_property ("localKeySignature");
239 SCM accidentals = get_property ("autoAccidentals");
240 SCM cautionaries = get_property ("autoCautionaries");
241 SCM barnum = get_property ("currentBarNumber");
242 SCM smp = get_property("measurePosition");
243 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
244 if(mp<0 && gh_number_p(barnum)) barnum = scm_int2num(gh_scm2int(barnum)-1);
245 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
246 for (int i = 0; i < accidentals_.size (); i++)
248 if (accidentals_[i].done_ )
249 continue;
250 accidentals_[i].done_ = true;
251 Grob * support = accidentals_[i].head_;
252 Note_req * note = accidentals_[i].melodic_;
253 Translator_group * origin = accidentals_[i].origin_;
255 Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
256 int num = number_accidentals (note, pitch, origin, accidentals, barnum);
257 int num_caut = number_accidentals (note, pitch, origin, cautionaries, barnum);
258 bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
260 if (abs (num_caut) > abs (num))
262 num = num_caut;
263 cautionary = true;
266 bool different = num < 0;
267 num = abs (num);
269 /* see if there's a tie that "changes" the accidental */
270 /* works because if there's a tie, the note to the left
271 is of the same pitch as the actual note */
273 Grob *tie_break_reminder = 0;
274 bool tie_changes = false;
275 for (int j = 0; j < ties_.size (); j++)
276 if (support == Tie::head (ties_[j], RIGHT))
278 tie_changes = different;
280 /* Enable accidentals for broken tie
282 We only want an accidental on a broken tie,
283 if the tie changes the accidental.
285 Maybe check property noTieBreakForceAccidental? */
286 if (different)
287 tie_break_reminder = ties_[j];
288 break;
291 if (num)
293 Grob * a = new Item (get_property ("Accidental"));
294 a->set_parent (support, Y_AXIS);
296 if (!accidental_placement_)
298 accidental_placement_ = new Item (get_property ("AccidentalPlacement"));
299 announce_grob (accidental_placement_, a->self_scm());
302 Accidental_placement::add_accidental (accidental_placement_, a);
303 announce_grob (a, SCM_EOL);
306 SCM accs = gh_cons (scm_int2num (pitch->alteration_), SCM_EOL);
307 if (num == 2 && extra_natural_b)
308 accs = gh_cons (scm_int2num (0), accs);
310 /* TODO:
312 add cautionary option in accidental.
315 if (cautionary)
317 a->set_grob_property ("cautionary", SCM_BOOL_T);
320 if (tie_break_reminder)
322 // TODO.
323 a->set_grob_property ("tie", tie_break_reminder->self_scm());
327 support->set_grob_property ("accidental-grob", a->self_scm ());
329 a->set_grob_property ("accidentals", accs);
330 accidentals_[i].accidental_ = a;
332 We add the accidentals to the support of the arpeggio, so it is put left of the
333 accidentals.
336 for (int i = 0; i < arpeggios_.size (); i++)
337 Side_position_interface::add_support (arpeggios_[i], a);
342 We should not record the accidental if it is the first
343 note and it is tied from the previous measure.
345 Checking whether it is tied also works mostly, but will it
346 always do the correct thing?
350 int n = pitch->notename_;
351 int o = pitch->octave_;
352 int a = pitch->alteration_;
353 SCM on_s = gh_cons (scm_int2num (o), scm_int2num (n));
356 TODO: Speed this up!
358 Perhaps only check translators mentioned in the auto-accidentals?
361 TODO: profile this.
363 I'd be surprised if the impact of this would be
364 measurable. Anyway, it seems localsig doesn't change
365 every time-step, but a set_property() is done every
366 time. We could save on that, probably.
368 --hwn.
373 while (origin)
375 SCM localsig = origin->get_property ("localKeySignature");
376 if (tie_changes)
379 Remember an alteration that is different both from
380 that of the tied note and of the key signature.
382 localsig = ly_assoc_front_x
383 (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
385 else
388 not really really correct if there are more than one
389 noteheads with the same notename.
391 localsig = ly_assoc_front_x
392 (localsig, on_s, gh_cons (scm_int2num (a), barnum));
394 origin->set_property ("localKeySignature", localsig);
395 origin = origin->daddy_trans_;
401 void
402 Accidental_engraver::finalize ()
407 void
408 Accidental_engraver::stop_translation_timestep ()
410 for (int i = 0; i < accidentals_.size(); i++)
412 Grob *a = accidentals_[i].accidental_;
413 if (a)
415 typeset_grob (a);
419 if (accidental_placement_)
420 typeset_grob(accidental_placement_);
421 accidental_placement_ = 00;
423 accidentals_.clear();
424 arpeggios_.clear ();
425 ties_.clear ();
428 void
429 Accidental_engraver::acknowledge_grob (Grob_info info)
431 Note_req * note = dynamic_cast <Note_req *> (info.music_cause ());
433 if (note && Rhythmic_head::has_interface (info.grob_))
435 Accidental_entry entry ;
436 entry.head_ = info.grob_;
437 entry.origin_ = info.origin_trans_->daddy_trans_;
438 entry.melodic_ = note;
440 accidentals_.push (entry);
442 else if (Tie::has_interface (info.grob_))
444 ties_.push (info.grob_);
446 else if (Arpeggio::has_interface (info.grob_))
448 arpeggios_.push (info.grob_);
453 void
454 Accidental_engraver::process_music ()
456 SCM sig = get_property ("keySignature");
458 /* Detect key sig changes.
459 Update all parents and children
461 if (last_keysig_ != sig)
463 Translator_group * trans_ = daddy_trans_;
464 while (trans_)
466 trans_ -> set_property ("localKeySignature", ly_deep_copy (sig));
467 trans_ = trans_->daddy_trans_;
469 set_property_on_children(daddy_trans_,"localKeySignature", sig);
471 last_keysig_ = sig;
479 ENTER_DESCRIPTION (Accidental_engraver,
480 "Make accidentals. Catches note heads, ties and notices key-change
481 events. Due to interaction with ties (which don't come together
482 with note heads), this needs to be in a context higher than Tie_engraver.",
484 "Accidental",
485 "rhythmic-head-interface tie-interface arpeggio-interface",
486 "localKeySignature extraNatural autoAccidentals autoCautionaries",
487 "localKeySignature");