]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-engraver.cc
*** empty log message ***
[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@cs.uu.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   PRECOMPUTED_VIRTUAL void process_music ();
59   virtual void acknowledge_grob (Grob_info);
60   PRECOMPUTED_VIRTUAL void stop_translation_timestep ();
61   virtual void initialize ();
62   PRECOMPUTED_VIRTUAL void process_acknowledged ();
63   virtual void finalize ();
64   virtual void derived_mark () const;
65
66 public:
67   SCM last_keysig_;     // ugh.
68
69   /*
70     Urgh. Since the accidentals depend on lots of variables, we have
71     to store all information before we can really create the
72     accidentals.
73   */
74   Link_array<Grob> left_objects_;
75   Link_array<Grob> right_objects_;
76
77   Grob *accidental_placement_;
78
79   Array<Accidental_entry> accidentals_;
80   Link_array<Spanner> ties_;
81
82
83 };
84
85 /*
86   TODO:
87
88   ugh, it is not clear what properties are mutable and which
89   aren't. eg. localKeySignature is changed at runtime, which means
90   that references in grobs should always store ly_deep_copy ()s of
91   those.
92 */
93
94 static void
95 set_property_on_children (Context *trans, char const *sym, SCM val)
96 {
97   trans->set_property (sym, ly_deep_copy (val));
98   for (SCM p = trans->children_contexts (); scm_is_pair (p); p = scm_cdr (p))
99     {
100       Context *trg = unsmob_context (scm_car (p));
101       set_property_on_children (trg, sym, ly_deep_copy (val));
102     }
103 }
104
105 Accidental_engraver::Accidental_engraver ()
106 {
107   accidental_placement_ = 0;
108   last_keysig_ = SCM_EOL;
109 }
110
111 void
112 Accidental_engraver::derived_mark () const
113 {
114   scm_gc_mark (last_keysig_);
115 }
116
117 void
118 Accidental_engraver::update_local_key_signature ()
119 {
120   last_keysig_ = get_property ("keySignature");
121   set_property_on_children (context (), "localKeySignature", last_keysig_);
122
123   Context *trans = context ()->get_parent_context ();
124
125   /* Huh. Don't understand what this is good for. --hwn.  */
126   while (trans && trans->where_defined (ly_symbol2scm ("localKeySignature")))
127     {
128       trans->set_property ("localKeySignature", ly_deep_copy (last_keysig_));
129       trans = trans->get_parent_context ();
130     }
131 }
132
133 void
134 Accidental_engraver::initialize ()
135 {
136   update_local_key_signature ();
137 }
138
139 /** Calculate the number of accidentals on basis of the current local key
140     sig (passed as argument)
141
142     * First check step+octave (taking into account barnumbers if necessary).
143
144     * Then check the global signature (only step).
145
146     Return number of accidentals (0, 1 or 2).  */
147
148 static bool
149 recent_enough (int bar_number, SCM alteration_def, SCM laziness)
150 {
151   if (scm_is_number (alteration_def)
152       || laziness == SCM_BOOL_T)
153     return true;
154
155   return (bar_number <= scm_to_int (scm_cdr (alteration_def)) + scm_to_int (laziness));
156 }
157
158 static int
159 extract_alteration (SCM alteration_def)
160 {
161   if (scm_is_number (alteration_def))
162     return scm_to_int (alteration_def);
163   else if (scm_is_pair (alteration_def))
164     return scm_to_int (scm_car (alteration_def));
165   else if (alteration_def == SCM_BOOL_F)
166     return 0;
167   else
168     assert (0);
169   return 0;
170 }
171
172 bool
173 is_tied (SCM alteration_def)
174 {
175   return (alteration_def == SCM_BOOL_T)
176     || (scm_is_pair (alteration_def) && scm_car (alteration_def) == SCM_BOOL_T);
177 }
178
179 static int
180 number_accidentals_from_sig (bool *different, SCM sig, Pitch *pitch,
181                              int bar_number, SCM laziness, bool ignore_octave)
182 {
183   int n = pitch->get_notename ();
184   int o = pitch->get_octave ();
185
186   SCM previous_alteration = SCM_BOOL_F;
187
188   SCM from_same_octave = ly_assoc_get (scm_cons (scm_int2num (o),
189                                                  scm_int2num (n)), sig, SCM_BOOL_F);
190   SCM from_key_signature = ly_assoc_get (scm_int2num (n), sig, SCM_BOOL_F);
191   SCM from_other_octaves = SCM_BOOL_F;
192   for (SCM s = sig; scm_is_pair (s); s = scm_cdr (s))
193     {
194       SCM entry = scm_car (s);
195       if (scm_is_pair (scm_car (entry))
196           && scm_cdar (entry) == scm_int2num (n))
197         from_other_octaves = scm_cdr (entry);
198     }
199
200   if (from_same_octave != SCM_BOOL_F
201       && recent_enough (bar_number, from_same_octave, laziness))
202     {
203       previous_alteration = from_same_octave;
204     }
205   else if (ignore_octave
206            && from_other_octaves != SCM_BOOL_F
207            && recent_enough (bar_number, from_other_octaves, laziness))
208     {
209       previous_alteration = from_other_octaves;
210     }
211   else if (from_key_signature != SCM_BOOL_F)
212     {
213       previous_alteration = from_key_signature;
214     }
215
216   int num = 1;
217   if (is_tied (previous_alteration))
218     {
219       num = 1;
220       *different = true;
221     }
222   else
223     {
224       int prev = extract_alteration (previous_alteration);
225       int alter = pitch->get_alteration ();
226
227       if (alter == prev)
228         num = 0;
229       else if ((abs (alter) < abs (prev) || 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             {
359               create_accidental (&accidentals_[i], num > 1, cautionary);
360             }
361         }
362     }
363 }
364
365 void
366 Accidental_engraver::create_accidental (Accidental_entry *entry,
367                                         bool restore_natural,
368                                         bool cautionary)
369 {
370   Music *note = entry->melodic_;
371   Grob *support = entry->head_;
372   Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
373   
374   
375   bool as_suggestion = get_property ("suggestAccidentals");
376   Grob *a = 0;
377   if (as_suggestion)
378     a = make_suggested_accidental (note, support, entry->origin_engraver_);
379   else
380     a = make_standard_accidental (note, support, entry->origin_engraver_);
381  
382   SCM accs = scm_cons (scm_int2num (pitch->get_alteration ()),
383                        SCM_EOL);
384   if (restore_natural)
385     {
386       if (to_boolean (get_property ("extraNatural")))
387         accs = scm_cons (scm_int2num (0), accs);
388     }
389   
390   /* TODO: add cautionary option in accidental. */
391   if (cautionary)
392     a->set_property ("cautionary", SCM_BOOL_T);
393
394
395   a->set_property ("accidentals", accs);
396   entry->accidental_ = a;
397 }
398
399 Grob *
400 Accidental_engraver::make_standard_accidental (Music *note,
401                                                Grob *support,
402                                                Engraver *trans)
403 {
404   
405   /*
406     We construct the accidentals at the originating Voice
407     level, so that we get the property settings for
408     Accidental from the respective Voice.
409   */
410   Grob *a
411     = make_grob_from_properties (trans,
412                                  ly_symbol2scm ("Accidental"),
413                                  note->self_scm (),
414                                  "Accidental");
415
416   /*
417     We add the accidentals to the support of the arpeggio,
418     so it is put left of the accidentals.
419   */
420   for (int i = 0; i < left_objects_.size (); i++)
421     Side_position_interface::add_support (left_objects_[i], a);
422   for (int i = 0; i < right_objects_.size (); i++)
423     Side_position_interface::add_support (a, right_objects_[i]);
424
425   a->set_parent (support, Y_AXIS);
426
427   if (!accidental_placement_)
428     accidental_placement_ = make_item ("AccidentalPlacement",
429                                        a->self_scm ());
430   Accidental_placement::add_accidental (accidental_placement_, a);
431   
432   support->set_object ("accidental-grob", a->self_scm ());
433
434   return a;
435 }
436
437
438 Grob *
439 Accidental_engraver::make_suggested_accidental (Music *note,
440                                                    Grob *note_head, Engraver *trans)
441 {
442   
443   Grob *a
444     = make_grob_from_properties (trans,
445                                  ly_symbol2scm ("AccidentalSuggestion"),
446                                  note->self_scm (),
447                                  "AccidentalSuggestion");
448
449   
450   Side_position_interface::add_support (a, note_head);
451   if (Grob *stem = unsmob_grob (a->get_object ("stem")))
452     {
453       Side_position_interface::add_support (a, stem);
454     }
455
456   a->set_parent (note_head, X_AXIS);
457   return a;
458 }
459
460
461 void
462 Accidental_engraver::finalize ()
463 {
464   last_keysig_ = SCM_EOL;
465 }
466
467 void
468 Accidental_engraver::stop_translation_timestep ()
469 {
470   for (int j = ties_.size (); j--;)
471     {
472       Grob *r = Tie::head (ties_[j], RIGHT);
473       for (int i = accidentals_.size (); i--;)
474         if (accidentals_[i].head_ == r)
475           {
476             if (Grob *g = accidentals_[i].accidental_)
477               {
478                 g->set_object ("tie", ties_[j]->self_scm ());
479                 accidentals_[i].tied_ = true;
480               }
481             ties_.del (j);
482             break;
483           }
484     }
485
486   for (int i = accidentals_.size (); i--;)
487     {
488       int barnum = get_bar_number ();
489
490       Music *note = accidentals_[i].melodic_;
491       Context *origin = accidentals_[i].origin_;
492
493       Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
494       if (!pitch)
495         continue;
496
497       int n = pitch->get_notename ();
498       int o = pitch->get_octave ();
499       int a = pitch->get_alteration ();
500       SCM key = scm_cons (scm_int2num (o), scm_int2num (n));
501
502       while (origin
503              && origin->where_defined (ly_symbol2scm ("localKeySignature")))
504         {
505           /*
506             huh? we set props all the way to the top?
507           */
508           SCM localsig = origin->get_property ("localKeySignature");
509           bool change = false;
510           if (accidentals_[i].tied_)
511             {
512               /*
513                 Remember an alteration that is different both from
514                 that of the tied note and of the key signature.
515               */
516               localsig = ly_assoc_front_x
517                 (localsig, key, scm_cons (SCM_BOOL_T, scm_int2num (barnum)));
518
519               change = true;
520             }
521           else
522             {
523               /*
524                 not really really correct if there are more than one
525                 noteheads with the same notename.
526               */
527               localsig = ly_assoc_front_x (localsig, key,
528                                            scm_cons (scm_int2num (a),
529                                                      scm_int2num (barnum)));
530               change = true;
531             }
532
533           if (change)
534             origin->set_property ("localKeySignature", localsig);
535
536           origin = origin->get_parent_context ();
537         }
538     }
539
540   accidental_placement_ = 0;
541   accidentals_.clear ();
542   left_objects_.clear ();
543   right_objects_.clear ();
544 }
545
546 void
547 Accidental_engraver::acknowledge_grob (Grob_info info)
548 {
549   Music *note = info.music_cause ();
550
551   if (note
552       && note->is_mus_type ("note-event")
553       && Rhythmic_head::has_interface (info.grob ()))
554     {
555       /*
556         String harmonics usually don't have accidentals.
557        */
558       if (to_boolean (get_property ("harmonicAccidentals"))
559           || !ly_is_equal (info.grob ()->get_property ("style"),
560                             ly_symbol2scm ("harmonic")))
561         {
562           Accidental_entry entry;
563           entry.head_ = info.grob ();
564           entry.origin_engraver_ = dynamic_cast<Engraver *> (info.origin_translator ());
565           entry.origin_ = entry.origin_engraver_->context ();
566           entry.melodic_ = note;
567
568           accidentals_.push (entry);
569         }
570     }
571   else if (Tie::has_interface (info.grob ()))
572     ties_.push (dynamic_cast<Spanner *> (info.grob ()));
573   else if (Arpeggio::has_interface (info.grob ()))
574     left_objects_.push (info.grob ());
575   else if (info.grob ()
576            ->internal_has_interface (ly_symbol2scm ("finger-interface")))
577     left_objects_.push (info.grob ());
578 }
579
580 void
581 Accidental_engraver::process_music ()
582 {
583   SCM sig = get_property ("keySignature");
584   /* Detect key sig changes.
585      Update all parents and children.  */
586   if (last_keysig_ != sig)
587     update_local_key_signature ();
588 }
589
590 ADD_TRANSLATOR (Accidental_engraver,
591                 "Make accidentals.  "
592                 "Catch note heads, ties and notices key-change events.  "
593                 "This engraver usually lives at Staff level, but "
594                 "reads the settings for Accidental at @code{Voice} level, "
595                 "so you can @code{\\override} them at @code{Voice}. ",
596                 "Accidental AccidentalSuggestion",
597
598                 "",
599                 
600                 /* acks */
601                 "arpeggio-interface "
602                 "finger-interface "
603                 "rhythmic-head-interface "
604                 "tie-interface ",
605
606                 "autoAccidentals "
607                 "autoCautionaries "
608                 "extraNatural "
609                 "harmonicAccidentals "
610                 "localKeySignature",
611                 "localKeySignature");