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