]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-engraver.cc
edb22455c56953d8704ba4276c02fa021c54a54f
[lilypond.git] / lily / accidental-engraver.cc
1 /*
2   accidental-engraver.cc -- implement accidental_engraver
3
4   (c)  1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5   Modified 2001-2002 by Rune Zedeler <rz@daimi.au.dk>
6 */
7
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"
19
20 #include "translator-group.hh"
21
22 /**
23
24
25 FIXME: should not compute vertical positioning of accidentals, but
26 get them from the noteheads
27
28 The algorithm for accidentals should be documented, and made
29 tweakable.
30
31 */
32
33 struct Accidental_entry {
34   bool done_;
35   Note_req * melodic_;
36   Grob * accidental_;
37   Translator_group *origin_;
38   Grob*  head_;
39   Accidental_entry();
40 };
41
42 Accidental_entry::Accidental_entry()
43 {
44   done_ = false;
45   melodic_ =0;
46   accidental_ = 0;
47   origin_ = 0;
48   head_ = 0;
49 }
50
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:
61
62   /*
63     TODO -> property.
64     
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.
67   */
68   SCM last_keysig_;
69
70   /*
71     Urgh. Since the accidentals depend on lots of variables, we have to
72     store all information before we can really create the accidentals.
73   */
74   Link_array<Grob> arpeggios_;
75
76   Grob * accidental_placement_;
77   
78
79   /*
80     The next 
81    */
82   Array<Accidental_entry> accidental_arr_;
83   
84   Link_array<Grob> tie_arr_;
85
86
87 };
88
89
90 static void set_property_on_children (Translator_group * trans, const char * sym, SCM val)
91 {
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));
96   }
97 }
98
99 Accidental_engraver::Accidental_engraver ()
100 {
101   accidental_placement_ = 0;
102   last_keysig_ = SCM_EOL;
103 }
104
105 void
106 Accidental_engraver::initialize ()
107 {
108   last_keysig_ = get_property ("keySignature");
109
110   Translator_group * trans_ = daddy_trans_l_;
111   while (trans_)
112     {
113       trans_ -> set_property ("localKeySignature",  ly_deep_copy (last_keysig_));
114       trans_ = trans_->daddy_trans_l_;
115     }
116   set_property_on_children (daddy_trans_l_,"localKeySignature", last_keysig_);
117 }
118
119 /*
120
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.
125
126 */
127 static int
128 number_accidentals (SCM sig, Note_req * note_l, Pitch *pitch, SCM curbarnum, SCM lazyness, 
129                     bool ignore_octave_b)
130 {
131   int n = pitch->notename_i_;
132   int o = pitch->octave_i_;
133   int a = pitch->alteration_i_;
134   int curbarnum_i = gh_scm2int (curbarnum);
135   int accbarnum_i = 0;
136
137   SCM prev;
138   if (ignore_octave_b)
139     prev = ly_assoc_cdr (gh_int2scm (n), sig);
140   else
141     prev = gh_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), sig);
142
143   /* should really be true unless prev == SCM_BOOL_F */
144   if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
145     {
146       accbarnum_i = gh_scm2int (ly_cddr (prev));
147       prev = gh_cons (ly_car (prev), ly_cadr (prev));
148     }
149   
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 (gh_int2scm (n), sig);
154
155
156   SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
157
158   int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
159
160   int num;
161   if (a == p
162       && !to_boolean (note_l->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;
169   
170   return a == p ? num : -num;
171 }
172
173 static int
174 number_accidentals (Note_req * note_l, Pitch *pitch, Translator_group * origin_l, 
175                     SCM accidentals, SCM curbarnum)
176 {
177   int number = 0;
178
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)).ch_C ()));
183   
184   while (gh_pair_p (accidentals) && origin_l)
185     {
186       // If pair then it is a new accidentals typesetting rule to be checked
187       if (gh_pair_p (ly_car (accidentals)))
188         {
189           SCM type = gh_caar (accidentals);
190           SCM lazyness = gh_cdar (accidentals);
191           SCM localsig = origin_l->get_property ("localKeySignature");
192           
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);
197
198           if (same_octave_b || any_octave_b)
199             {
200               int n = number_accidentals
201                 (localsig, note_l, pitch, curbarnum, lazyness, any_octave_b);
202               diff = diff || (n < 0);
203               number = max (number, abs (n));     
204             }
205           else
206             warning (_f ("unknown accidental typesetting: %s. Ignored", 
207                          ly_symbol2string (type).ch_C ()));
208         }
209       
210
211       /*
212         if symbol then it is a context name. Scan parent contexts to find it.
213       */
214       else if (gh_symbol_p (ly_car (accidentals)))
215         {
216           String context = ly_symbol2string (ly_car (accidentals));
217           
218           while (origin_l && !origin_l->is_alias_b (context))
219             origin_l = origin_l->daddy_trans_l_;
220       
221           if (!origin_l)
222             warning (_f ("Symbol is not a parent context: %s. Ignored", 
223                          context.ch_C ()));
224         }
225       else warning (_f ("Accidental typesetting must be pair or context-name: %s", 
226                         ly_scm2string (ly_car (accidentals)).ch_C ()));
227       
228       accidentals = ly_cdr (accidentals);
229     }
230   return diff ? -number : number;
231 }
232
233 void
234 Accidental_engraver::process_acknowledged_grobs ()
235 {
236   if (accidental_arr_.size () && !accidental_arr_.top().done_)
237     {
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
243       bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
244       for (int i = 0; i  < accidental_arr_.size (); i++) 
245         {
246           if (accidental_arr_[i].done_ )
247             continue;
248           accidental_arr_[i].done_  = true;
249           Grob * support_l = accidental_arr_[i].head_;
250           Note_req * note_l = accidental_arr_[i].melodic_;
251           Translator_group * origin_l = accidental_arr_[i].origin_;
252
253           Pitch * pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
254           int num = number_accidentals (note_l, pitch, origin_l, accidentals, barnum);
255           int num_caut = number_accidentals (note_l, pitch, origin_l, cautionaries, barnum);
256           bool cautionary = to_boolean (note_l->get_mus_property ("cautionary"));
257           
258           if (abs (num_caut) > abs (num))
259             {
260               num = num_caut;
261               cautionary = true;
262             }
263           
264           bool different = num < 0;
265           num = abs (num);
266
267           /* see if there's a tie that "changes" the accidental */
268           /* works because if there's a tie, the note to the left
269              is of the same pitch as the actual note */
270
271           Grob *tie_break_reminder = 0;
272           bool tie_changes = false;
273           for (int j = 0; j < tie_arr_.size (); j++)
274             if (support_l == Tie::head (tie_arr_[j], RIGHT))
275               {
276                 tie_changes = different;
277
278                 /* Enable accidentals for broken tie
279
280                 We only want an accidental on a broken tie, 
281                 if the tie changes the accidental.
282                    
283                 Maybe check property noTieBreakForceAccidental? */
284                 if (different)
285                   tie_break_reminder = tie_arr_[j];
286                 break;
287               }
288
289           if (num)
290             {
291               Grob * a = new Item (get_property ("Accidental"));
292               a->set_parent (support_l, Y_AXIS);
293
294               if (!accidental_placement_)
295                 {
296                   accidental_placement_ = new Item (get_property ("AccidentalPlacement"));
297                   announce_grob (accidental_placement_, a->self_scm());
298                 }
299               
300               Accidental_placement::add_accidental (accidental_placement_, a);
301               announce_grob (a, SCM_EOL);
302
303               
304               SCM accs = gh_cons (gh_int2scm (pitch->alteration_i_), SCM_EOL);
305               if (num == 2 && extra_natural_b)
306                 accs = gh_cons (gh_int2scm (0), accs);
307
308               /* TODO:
309
310               add cautionary option in accidental.
311                */
312
313               if (cautionary)
314                 {
315                   a->set_grob_property ("cautionary", SCM_BOOL_T);
316                 }
317               
318               if (tie_break_reminder)
319                 {
320                   // TODO.
321                   a->set_grob_property ("tie", tie_break_reminder->self_scm());
322                 }
323               
324               
325               support_l->set_grob_property ("accidental-grob", a->self_scm ());
326
327               a->set_grob_property ("accidentals", accs);
328               accidental_arr_[i].accidental_ = a;
329  /*
330         We add the accidentals to the support of the arpeggio, so it is put left of the
331         accidentals. 
332         
333       */
334               for (int i = 0;  i < arpeggios_.size ();  i++)
335                 Side_position_interface::add_support (arpeggios_[i], a);
336             }
337           
338
339           /*
340             We should not record the accidental if it is the first
341             note and it is tied from the previous measure.
342
343             Checking whether it is tied also works mostly, but will it
344             always do the correct thing?
345           */
346           
347
348           int n = pitch->notename_i_;
349           int o = pitch->octave_i_;
350           int a = pitch->alteration_i_;
351           SCM on_s = gh_cons (gh_int2scm (o), gh_int2scm (n));
352
353           /*
354             TODO: Speed this up!
355             
356             Perhaps only check translators mentioned in the auto-accidentals?
357             -rz
358
359             TODO: profile this.
360             
361             I'd be surprised if the impact of this would be
362             measurable.  Anyway, it seems localsig doesn't change
363             every time-step, but a set_property() is done every
364             time. We could save on that, probably.
365
366             --hwn.
367             
368             
369           */
370
371           while (origin_l)
372             {
373               SCM localsig = origin_l->get_property ("localKeySignature");
374               if (tie_changes)
375                 {
376                   /*
377                     Remember an alteration that is different both from
378                     that of the tied note and of the key signature.
379                   */
380                   localsig = ly_assoc_front_x
381                     (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
382                 }
383               else
384                 {
385                   /*
386                     not really really correct if there are more than one
387                     noteheads with the same notename.
388                   */
389                   localsig = ly_assoc_front_x
390                     (localsig, on_s, gh_cons (gh_int2scm (a), barnum)); 
391                 }
392               origin_l->set_property ("localKeySignature",  localsig);
393               origin_l = origin_l->daddy_trans_l_;
394             }
395         }
396     }
397 }
398
399 void
400 Accidental_engraver::finalize ()
401 {
402
403 }
404
405 void
406 Accidental_engraver::stop_translation_timestep ()
407 {
408   for (int i = 0; i < accidental_arr_.size(); i++)
409     {
410       Grob *a = accidental_arr_[i].accidental_;
411       if (a)
412         {
413           typeset_grob (a);
414         }
415     }
416
417   if (accidental_placement_)
418     typeset_grob(accidental_placement_);
419   accidental_placement_ = 00;
420   
421   accidental_arr_.clear();
422   arpeggios_.clear ();
423   tie_arr_.clear ();
424 }
425
426 void
427 Accidental_engraver::acknowledge_grob (Grob_info info)
428 {
429   Note_req * note_l =  dynamic_cast <Note_req *> (info.music_cause ());
430
431   if (note_l && Rhythmic_head::has_interface (info.grob_l_))
432     {
433       Accidental_entry entry ;
434       entry.head_ = info.grob_l_;
435       entry.origin_ = info.origin_trans_l_->daddy_trans_l_;
436       entry.melodic_ = note_l;
437
438       accidental_arr_.push (entry);
439     }
440   else if (Tie::has_interface (info.grob_l_))
441     {
442       tie_arr_.push (info.grob_l_);
443     }
444   else if (Arpeggio::has_interface (info.grob_l_))
445     {
446       arpeggios_.push (info.grob_l_); 
447     }
448   
449 }
450
451 void
452 Accidental_engraver::process_music ()
453 {
454   SCM sig = get_property ("keySignature");
455
456   /* Detect key sig changes.
457      Update all parents and children
458   */
459   if (last_keysig_ != sig)
460     {
461       Translator_group * trans_ = daddy_trans_l_;
462       while (trans_)
463         {
464           trans_ -> set_property ("localKeySignature",  ly_deep_copy (sig));
465           trans_ = trans_->daddy_trans_l_;
466         }
467       set_property_on_children(daddy_trans_l_,"localKeySignature", sig);
468
469       last_keysig_ = sig;
470     }
471 }
472
473
474
475
476
477 ENTER_DESCRIPTION (Accidental_engraver,
478 "Make accidentals.  Catches note heads, ties and notices key-change
479 events.  Due to interaction with ties (which don't come together
480 with note heads), this needs to be in a context higher than Tie_engraver.",
481                    
482                "Accidental",
483                "rhythmic-head-interface tie-interface arpeggio-interface",
484                "localKeySignature extraNatural autoAccidentals autoCautionaries",
485                    "localKeySignature");