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