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