]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-engraver.cc
release commit
[lilypond.git] / lily / accidental-engraver.cc
1 /*
2   accidental-engraver.cc -- implement accidental_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2005 Han-Wen Nienhuys <hanwen@xs4all.nl>
7   Modified 2001--2002 by Rune Zedeler <rz@daimi.au.dk>
8 */
9
10 #include "accidental-placement.hh"
11 #include "arpeggio.hh"
12 #include "context.hh"
13 #include "engraver.hh"
14 #include "protected-scm.hh"
15 #include "rhythmic-head.hh"
16 #include "side-position-interface.hh"
17 #include "tie.hh"
18 #include "warn.hh"
19 #include "music.hh"
20 #include "pitch.hh"
21
22 #include "translator.icc"
23
24 class Accidental_entry
25 {
26 public:
27   bool done_;
28   Music *melodic_;
29   Grob *accidental_;
30   Context *origin_;
31   Engraver *origin_engraver_;
32   Grob *head_;
33   bool tied_;
34
35   Accidental_entry ();
36 };
37
38 Accidental_entry::Accidental_entry ()
39 {
40   tied_ = false;
41   done_ = false;
42   melodic_ = 0;
43   accidental_ = 0;
44   origin_ = 0;
45   head_ = 0;
46 }
47
48 class Accidental_engraver : public Engraver
49 {
50   int get_bar_number ();
51   void update_local_key_signature ();
52   void create_accidental (Accidental_entry *entry, bool, bool);
53   Grob *make_standard_accidental (Music *note, Grob *note_head, Engraver *trans);
54   Grob *make_suggested_accidental (Music *note, Grob *note_head, Engraver *trans);
55
56 protected:
57   TRANSLATOR_DECLARATIONS (Accidental_engraver);
58   void process_music ();
59
60   void acknowledge_tie (Grob_info);
61   void acknowledge_arpeggio (Grob_info);
62   void acknowledge_rhythmic_head (Grob_info);
63   void acknowledge_finger (Grob_info);
64
65   void stop_translation_timestep ();
66   virtual void initialize ();
67   void process_acknowledged ();
68   virtual void finalize ();
69   virtual void derived_mark () const;
70
71 public:
72   SCM last_keysig_;     // ugh.
73
74   /*
75     Urgh. Since the accidentals depend on lots of variables, we have
76     to store all information before we can really create the
77     accidentals.
78   */
79   Link_array<Grob> left_objects_;
80   Link_array<Grob> right_objects_;
81
82   Grob *accidental_placement_;
83
84   Array<Accidental_entry> accidentals_;
85   Link_array<Spanner> ties_;
86 };
87
88 /*
89   TODO:
90
91   ugh, it is not clear what properties are mutable and which
92   aren't. eg. localKeySignature is changed at runtime, which means
93   that references in grobs should always store ly_deep_copy ()s of
94   those.
95 */
96
97 static void
98 set_property_on_children (Context *trans, char const *sym, SCM val)
99 {
100   trans->set_property (sym, ly_deep_copy (val));
101   for (SCM p = trans->children_contexts (); scm_is_pair (p); p = scm_cdr (p))
102     {
103       Context *trg = unsmob_context (scm_car (p));
104       set_property_on_children (trg, sym, ly_deep_copy (val));
105     }
106 }
107
108 Accidental_engraver::Accidental_engraver ()
109 {
110   accidental_placement_ = 0;
111   last_keysig_ = SCM_EOL;
112 }
113
114 void
115 Accidental_engraver::derived_mark () const
116 {
117   scm_gc_mark (last_keysig_);
118 }
119
120 void
121 Accidental_engraver::update_local_key_signature ()
122 {
123   last_keysig_ = get_property ("keySignature");
124   set_property_on_children (context (), "localKeySignature", last_keysig_);
125
126   Context *trans = context ()->get_parent_context ();
127
128   /* Huh. Don't understand what this is good for. --hwn.  */
129
130   SCM val;
131   while (trans && trans->where_defined (ly_symbol2scm ("localKeySignature"), &val))
132     {
133       trans->set_property ("localKeySignature", ly_deep_copy (last_keysig_));
134       trans = trans->get_parent_context ();
135     }
136 }
137
138 void
139 Accidental_engraver::initialize ()
140 {
141   update_local_key_signature ();
142 }
143
144 /** Calculate the number of accidentals on basis of the current local key
145     sig (passed as argument)
146
147     * First check step+octave (taking into account barnumbers if necessary).
148
149     * Then check the global signature (only step).
150
151     Return number of accidentals (0, 1 or 2).  */
152
153 static bool
154 recent_enough (int bar_number, SCM alteration_def, SCM laziness)
155 {
156   if (scm_is_number (alteration_def)
157       || laziness == SCM_BOOL_T)
158     return true;
159
160   return (bar_number <= scm_to_int (scm_cdr (alteration_def)) + scm_to_int (laziness));
161 }
162
163 static int
164 extract_alteration (SCM alteration_def)
165 {
166   if (scm_is_number (alteration_def))
167     return scm_to_int (alteration_def);
168   else if (scm_is_pair (alteration_def))
169     return scm_to_int (scm_car (alteration_def));
170   else if (alteration_def == SCM_BOOL_F)
171     return 0;
172   else
173     assert (0);
174   return 0;
175 }
176
177 bool
178 is_tied (SCM alteration_def)
179 {
180   return (alteration_def == SCM_BOOL_T)
181     || (scm_is_pair (alteration_def) && scm_car (alteration_def) == SCM_BOOL_T);
182 }
183
184 static int
185 number_accidentals_from_sig (bool *different, SCM sig, Pitch *pitch,
186                              int bar_number, SCM laziness, bool ignore_octave)
187 {
188   int n = pitch->get_notename ();
189   int o = pitch->get_octave ();
190
191   SCM previous_alteration = SCM_BOOL_F;
192
193   SCM from_same_octave = ly_assoc_get (scm_cons (scm_from_int (o),
194                                                  scm_from_int (n)), sig, SCM_BOOL_F);
195   SCM from_key_signature = ly_assoc_get (scm_from_int (n), sig, SCM_BOOL_F);
196   SCM from_other_octaves = SCM_BOOL_F;
197   for (SCM s = sig; scm_is_pair (s); s = scm_cdr (s))
198     {
199       SCM entry = scm_car (s);
200       if (scm_is_pair (scm_car (entry))
201           && scm_cdar (entry) == scm_from_int (n))
202         from_other_octaves = scm_cdr (entry);
203     }
204
205   if (from_same_octave != SCM_BOOL_F
206       && recent_enough (bar_number, from_same_octave, laziness))
207     previous_alteration = from_same_octave;
208   else if (ignore_octave
209            && from_other_octaves != SCM_BOOL_F
210            && recent_enough (bar_number, from_other_octaves, laziness))
211     previous_alteration = from_other_octaves;
212   else if (from_key_signature != SCM_BOOL_F)
213     previous_alteration = from_key_signature;
214
215   int num = 1;
216   if (is_tied (previous_alteration))
217     {
218       num = 1;
219       *different = true;
220     }
221   else
222     {
223       int prev = extract_alteration (previous_alteration);
224       int alter = pitch->get_alteration ();
225
226       if (alter == prev)
227         num = 0;
228       else if ((abs (alter) < abs (prev)
229                 || prev * alter < 0) && alter != 0)
230         num = 2;
231       *different = (alter != prev);
232     }
233   return num;
234 }
235
236 static int
237 number_accidentals (bool *different,
238                     Pitch *pitch, Context *origin,
239                     SCM accidentals, int bar_number)
240 {
241   int number = 0;
242
243   *different = false;
244   if (scm_is_pair (accidentals) && !scm_is_symbol (scm_car (accidentals)))
245     warning (_f ("accidental typesetting list must begin with context-name: %s",
246                  ly_scm2string (scm_car (accidentals)).to_str0 ()));
247
248   for (; scm_is_pair (accidentals) && origin;
249        accidentals = scm_cdr (accidentals))
250     {
251       // If pair then it is a new accidentals typesetting rule to be checked
252       SCM rule = scm_car (accidentals);
253       if (scm_is_pair (rule))
254         {
255           SCM type = scm_car (rule);
256           SCM laziness = scm_cdr (rule);
257           SCM localsig = origin->get_property ("localKeySignature");
258
259           bool same_octave_b
260             = scm_is_eq (ly_symbol2scm ("same-octave"), type);
261           bool any_octave_b
262             = scm_is_eq (ly_symbol2scm ("any-octave"), type);
263
264           if (same_octave_b || any_octave_b)
265             {
266               bool d = false;
267               int n = number_accidentals_from_sig
268                 (&d, localsig, pitch, bar_number, laziness, any_octave_b);
269               *different = *different || d;
270               number = max (number, n);
271             }
272           else
273             warning (_f ("ignoring unknown accidental: %s",
274                          ly_symbol2string (type).to_str0 ()));
275         }
276
277       /* if symbol then it is a context name.  Scan parent contexts to
278          find it. */
279       else if (scm_is_symbol (rule))
280         {
281           Context *dad = origin;
282           while (dad && !dad->is_alias (rule))
283             dad = dad->get_parent_context ();
284
285           if (dad)
286             origin = dad;
287         }
288       else
289         warning (_f ("pair or context-name expected for accidental rule, found %s",
290                      ly_scm2string (rule).to_str0 ()));
291     }
292
293   return number;
294 }
295
296 int
297 Accidental_engraver::get_bar_number ()
298 {
299   SCM barnum = get_property ("currentBarNumber");
300   SCM smp = get_property ("measurePosition");
301
302   int bn = robust_scm2int (barnum, 0);
303
304   Moment mp = robust_scm2moment (smp, Moment (0));
305   if (mp.main_part_ < Rational (0))
306     bn--;
307
308   return bn;
309 }
310
311 void
312 Accidental_engraver::process_acknowledged ()
313 {
314   if (accidentals_.size () && !accidentals_.top ().done_)
315     {
316       SCM accidentals = get_property ("autoAccidentals");
317       SCM cautionaries = get_property ("autoCautionaries");
318       int barnum = get_bar_number ();
319
320       for (int i = 0; i < accidentals_.size (); i++)
321         {
322           if (accidentals_[i].done_)
323             continue;
324           accidentals_[i].done_ = true;
325
326           Music *note = accidentals_[i].melodic_;
327           Context *origin = accidentals_[i].origin_;
328
329           Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
330           if (!pitch)
331             continue;
332
333           bool different = false;
334           bool different_caut = false;
335
336           int num = number_accidentals (&different,
337                                         pitch, origin,
338                                         accidentals, barnum);
339           int num_caut = number_accidentals (&different_caut,
340                                              pitch, origin,
341                                              cautionaries, barnum);
342
343           bool cautionary = to_boolean (note->get_property ("cautionary"));
344
345           if (num_caut > num)
346             {
347               num = num_caut;
348               different = different_caut;
349               cautionary = true;
350             }
351
352           if (num == 0 && to_boolean (note->get_property ("force-accidental")))
353             num = 1;
354
355           /* Cannot look for ties: it's not guaranteed that they reach
356              us before the notes. */
357           if (num)
358             create_accidental (&accidentals_[i], num > 1, cautionary);
359         }
360     }
361 }
362
363 void
364 Accidental_engraver::create_accidental (Accidental_entry *entry,
365                                         bool restore_natural,
366                                         bool cautionary)
367 {
368   Music *note = entry->melodic_;
369   Grob *support = entry->head_;
370   Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
371
372   bool as_suggestion = to_boolean (entry->origin_->get_property ("suggestAccidentals"));
373   Grob *a = 0;
374   if (as_suggestion)
375     a = make_suggested_accidental (note, support, entry->origin_engraver_);
376   else
377     a = make_standard_accidental (note, support, entry->origin_engraver_);
378
379   SCM accs = scm_cons (scm_from_int (pitch->get_alteration ()),
380                        SCM_EOL);
381   if (restore_natural)
382     {
383       if (to_boolean (get_property ("extraNatural")))
384         accs = scm_cons (scm_from_int (0), accs);
385     }
386
387   /* TODO: add cautionary option in accidental. */
388   if (cautionary)
389     a->set_property ("cautionary", SCM_BOOL_T);
390
391   a->set_property ("accidentals", accs);
392   entry->accidental_ = a;
393 }
394
395 Grob *
396 Accidental_engraver::make_standard_accidental (Music *note,
397                                                Grob *support,
398                                                Engraver *trans)
399 {
400
401   /*
402     We construct the accidentals at the originating Voice
403     level, so that we get the property settings for
404     Accidental from the respective Voice.
405   */
406   Grob *a
407     = make_grob_from_properties (trans,
408                                  ly_symbol2scm ("Accidental"),
409                                  note->self_scm (),
410                                  "Accidental");
411
412   /*
413     We add the accidentals to the support of the arpeggio,
414     so it is put left of the accidentals.
415   */
416   for (int i = 0; i < left_objects_.size (); i++)
417     Side_position_interface::add_support (left_objects_[i], a);
418   for (int i = 0; i < right_objects_.size (); i++)
419     Side_position_interface::add_support (a, right_objects_[i]);
420
421   a->set_parent (support, Y_AXIS);
422
423   if (!accidental_placement_)
424     accidental_placement_ = make_item ("AccidentalPlacement",
425                                        a->self_scm ());
426   Accidental_placement::add_accidental (accidental_placement_, a);
427
428   support->set_object ("accidental-grob", a->self_scm ());
429
430   return a;
431 }
432
433 Grob *
434 Accidental_engraver::make_suggested_accidental (Music *note,
435                                                 Grob *note_head, Engraver *trans)
436 {
437
438   Grob *a
439     = make_grob_from_properties (trans,
440                                  ly_symbol2scm ("AccidentalSuggestion"),
441                                  note->self_scm (),
442                                  "AccidentalSuggestion");
443
444   Side_position_interface::add_support (a, note_head);
445   if (Grob *stem = unsmob_grob (a->get_object ("stem")))
446     Side_position_interface::add_support (a, stem);
447
448   a->set_parent (note_head, X_AXIS);
449   return a;
450 }
451
452 void
453 Accidental_engraver::finalize ()
454 {
455   last_keysig_ = SCM_EOL;
456 }
457
458 void
459 Accidental_engraver::stop_translation_timestep ()
460 {
461   for (int j = ties_.size (); j--;)
462     {
463       Grob *r = Tie::head (ties_[j], RIGHT);
464       for (int i = accidentals_.size (); i--;)
465         if (accidentals_[i].head_ == r)
466           {
467             if (Grob *g = accidentals_[i].accidental_)
468               {
469                 g->set_object ("tie", ties_[j]->self_scm ());
470                 accidentals_[i].tied_ = true;
471               }
472             ties_.del (j);
473             break;
474           }
475     }
476
477   for (int i = accidentals_.size (); i--;)
478     {
479       int barnum = get_bar_number ();
480
481       Music *note = accidentals_[i].melodic_;
482       Context *origin = accidentals_[i].origin_;
483
484       Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
485       if (!pitch)
486         continue;
487
488       int n = pitch->get_notename ();
489       int o = pitch->get_octave ();
490       int a = pitch->get_alteration ();
491       SCM key = scm_cons (scm_from_int (o), scm_from_int (n));
492
493       SCM localsig = SCM_EOL;
494       while (origin
495              && origin->where_defined (ly_symbol2scm ("localKeySignature"), &localsig))
496         {
497           bool change = false;
498           if (accidentals_[i].tied_)
499             {
500               /*
501                 Remember an alteration that is different both from
502                 that of the tied note and of the key signature.
503               */
504               localsig = ly_assoc_front_x
505                 (localsig, key, scm_cons (SCM_BOOL_T, scm_from_int (barnum)));
506
507               change = true;
508             }
509           else
510             {
511               /*
512                 not really really correct if there are more than one
513                 noteheads with the same notename.
514               */
515               localsig = ly_assoc_front_x (localsig, key,
516                                            scm_cons (scm_from_int (a),
517                                                      scm_from_int (barnum)));
518               change = true;
519             }
520
521           if (change)
522             origin->set_property ("localKeySignature", localsig);
523
524           origin = origin->get_parent_context ();
525         }
526     }
527
528   accidental_placement_ = 0;
529   accidentals_.clear ();
530   left_objects_.clear ();
531   right_objects_.clear ();
532 }
533
534 void
535 Accidental_engraver::acknowledge_rhythmic_head (Grob_info info)
536 {
537   Music *note = info.music_cause ();
538   if (note
539       && note->is_mus_type ("note-event"))
540     {
541       /*
542         String harmonics usually don't have accidentals.
543       */
544       if (to_boolean (get_property ("harmonicAccidentals"))
545           || !ly_is_equal (info.grob ()->get_property ("style"),
546                            ly_symbol2scm ("harmonic")))
547         {
548           Accidental_entry entry;
549           entry.head_ = info.grob ();
550           entry.origin_engraver_ = dynamic_cast<Engraver *> (info.origin_translator ());
551           entry.origin_ = entry.origin_engraver_->context ();
552           entry.melodic_ = note;
553
554           accidentals_.push (entry);
555         }
556     }
557 }
558
559 void
560 Accidental_engraver::acknowledge_tie (Grob_info info)
561 {
562   ties_.push (dynamic_cast<Spanner *> (info.grob ()));
563 }
564
565 void
566 Accidental_engraver::acknowledge_arpeggio (Grob_info info)
567 {
568   left_objects_.push (info.grob ());
569 }
570
571 void
572 Accidental_engraver::acknowledge_finger (Grob_info info)
573 {
574   left_objects_.push (info.grob ());
575 }
576
577 void
578 Accidental_engraver::process_music ()
579 {
580   SCM sig = get_property ("keySignature");
581   /* Detect key sig changes.
582      Update all parents and children.  */
583   if (last_keysig_ != sig)
584     update_local_key_signature ();
585 }
586
587 ADD_ACKNOWLEDGER (Accidental_engraver, arpeggio);
588 ADD_ACKNOWLEDGER (Accidental_engraver, finger);
589 ADD_ACKNOWLEDGER (Accidental_engraver, rhythmic_head);
590 ADD_ACKNOWLEDGER (Accidental_engraver, tie);
591
592 ADD_TRANSLATOR (Accidental_engraver,
593                 "Make accidentals.  "
594                 "Catch note heads, ties and notices key-change events.  "
595                 "This engraver usually lives at Staff level, but "
596                 "reads the settings for Accidental at @code{Voice} level, "
597                 "so you can @code{\\override} them at @code{Voice}. ",
598                 "Accidental AccidentalSuggestion",
599
600                 "",
601
602                 "autoAccidentals "
603                 "autoCautionaries "
604                 "extraNatural "
605                 "harmonicAccidentals "
606                 "localKeySignature",
607                 "localKeySignature");