]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-engraver.cc
(main): don't identify binary.
[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           SCM context =ly_car (accidentals);
207           while (origin && !origin->is_alias_b (context))
208             origin = origin->daddy_trans_;
209       
210           if (!origin)
211             warning (_f ("Symbol is not a parent context: %s. Ignored", 
212                          ly_symbol2string (context).to_str0 ()));
213         }
214       else warning (_f ("Accidental typesetting must be pair or context-name: %s", 
215                         ly_scm2string (ly_car (accidentals)).to_str0 ()));
216       
217       accidentals = ly_cdr (accidentals);
218     }
219   return diff ? -number : number;
220 }
221
222 void
223 Accidental_engraver::process_acknowledged_grobs ()
224 {
225   if (accidentals_.size () && !accidentals_.top().done_)
226     {
227       //SCM localsig = get_property ("localKeySignature");
228       SCM accidentals =  get_property ("autoAccidentals");
229       SCM cautionaries =  get_property ("autoCautionaries");
230       SCM barnum = get_property ("currentBarNumber");
231       SCM smp = get_property("measurePosition");
232       Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
233       if(mp.main_part_<Rational(0) && gh_number_p(barnum)) barnum = scm_int2num(gh_scm2int(barnum)-1);
234       bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
235       for (int i = 0; i  < accidentals_.size (); i++) 
236         {
237           if (accidentals_[i].done_ )
238             continue;
239           accidentals_[i].done_  = true;
240           Grob * support = accidentals_[i].head_;
241           Music * note = accidentals_[i].melodic_;
242           Translator_group * origin = accidentals_[i].origin_;
243
244           Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
245           int num = number_accidentals (note, pitch, origin, accidentals, barnum);
246           int num_caut = number_accidentals (note, pitch, origin, cautionaries, barnum);
247           bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
248           
249           if (abs (num_caut) > abs (num))
250             {
251               num = num_caut;
252               cautionary = true;
253             }
254
255           if(num==0 && to_boolean (note->get_mus_property ("force-accidental")))
256              num=1;
257           
258           bool different = num < 0;
259           num = abs (num);
260
261           /* see if there's a tie that "changes" the accidental */
262           /* works because if there's a tie, the note to the left
263              is of the same pitch as the actual note */
264
265           Grob *tie_break_reminder = 0;
266           bool tie_changes = false;
267           for (int j = 0; j < ties_.size (); j++)
268             if (support == Tie::head (ties_[j], RIGHT))
269               {
270                 tie_changes = different;
271
272                 /* Enable accidentals for broken tie
273
274                 We only want an accidental on a broken tie, 
275                 if the tie changes the accidental.
276                    
277                 Maybe check property noTieBreakForceAccidental? */
278                 if (different)
279                   tie_break_reminder = ties_[j];
280                 break;
281               }
282
283           if (num)
284             {
285               Grob * a = new Item (get_property ("Accidental"));
286               a->set_parent (support, Y_AXIS);
287
288               if (!accidental_placement_)
289                 {
290                   accidental_placement_ = new Item (get_property ("AccidentalPlacement"));
291                   announce_grob (accidental_placement_, a->self_scm());
292                 }
293               
294               Accidental_placement::add_accidental (accidental_placement_, a);
295               announce_grob (a, SCM_EOL);
296
297               
298               SCM accs = gh_cons (scm_int2num (pitch->get_alteration ()), SCM_EOL);
299               if (num == 2 && extra_natural_b)
300                 accs = gh_cons (scm_int2num (0), accs);
301
302               /* TODO:
303
304               add cautionary option in accidental.
305                */
306
307               if (cautionary)
308                 {
309                   a->set_grob_property ("cautionary", SCM_BOOL_T);
310                 }
311               
312               if (tie_break_reminder)
313                 {
314                   // TODO.
315                   a->set_grob_property ("tie", tie_break_reminder->self_scm());
316                 }
317               
318               
319               support->set_grob_property ("accidental-grob", a->self_scm ());
320
321               a->set_grob_property ("accidentals", accs);
322               accidentals_[i].accidental_ = a;
323  /*
324         We add the accidentals to the support of the arpeggio, so it is put left of the
325         accidentals. 
326         
327       */
328               for (int i = 0;  i < left_objects_.size ();  i++)
329                 Side_position_interface::add_support (left_objects_[i], a);
330               for (int i = 0;  i < right_objects_.size ();  i++)
331                 Side_position_interface::add_support (a, right_objects_[i]);
332             }
333           
334
335           /*
336             We should not record the accidental if it is the first
337             note and it is tied from the previous measure.
338
339             Checking whether it is tied also works mostly, but will it
340             always do the correct thing?
341           */
342           
343
344           int n = pitch->get_notename ();
345           int o = pitch->get_octave ();
346           int a = pitch->get_alteration ();
347           SCM on_s = gh_cons (scm_int2num (o), scm_int2num (n));
348
349           /*
350             TODO: Speed this up!
351             
352             Perhaps only check translators mentioned in the auto-accidentals?
353             -rz
354
355             TODO: profile this.
356             
357             I'd be surprised if the impact of this would be
358             measurable.  Anyway, it seems localsig doesn't change
359             every time-step, but a set_property() is done every
360             time. We could save on that, probably.
361
362             --hwn.
363             
364             
365           */
366
367           while (origin)
368             {
369               SCM localsig = origin->get_property ("localKeySignature");
370               if (tie_changes)
371                 {
372                   /*
373                     Remember an alteration that is different both from
374                     that of the tied note and of the key signature.
375                   */
376                   localsig = ly_assoc_front_x
377                     (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
378                 }
379               else
380                 {
381                   /*
382                     not really really correct if there are more than one
383                     noteheads with the same notename.
384                   */
385                   localsig = ly_assoc_front_x
386                     (localsig, on_s, gh_cons (scm_int2num (a), barnum)); 
387                 }
388               origin->set_property ("localKeySignature",  localsig);
389               origin = origin->daddy_trans_;
390             }
391         }
392     }
393 }
394
395 void
396 Accidental_engraver::finalize ()
397 {
398
399 }
400
401 void
402 Accidental_engraver::stop_translation_timestep ()
403 {
404   for (int i = 0; i < accidentals_.size(); i++)
405     {
406       Grob *a = accidentals_[i].accidental_;
407       if (a)
408         {
409           typeset_grob (a);
410         }
411     }
412
413   if (accidental_placement_)
414     typeset_grob(accidental_placement_);
415   accidental_placement_ = 00;
416   
417   accidentals_.clear();
418   left_objects_.clear ();
419   right_objects_.clear ();
420   ties_.clear ();
421 }
422
423 void
424 Accidental_engraver::acknowledge_grob (Grob_info info)
425 {
426   Music * note =  info.music_cause ();
427
428   if (note
429       && note->is_mus_type("note-event")
430       && Rhythmic_head::has_interface (info.grob_))
431     {
432       Accidental_entry entry ;
433       entry.head_ = info.grob_;
434       entry.origin_ = info.origin_trans_->daddy_trans_;
435       entry.melodic_ = note;
436
437       accidentals_.push (entry);
438     }
439   else if (Tie::has_interface (info.grob_))
440     {
441       ties_.push (info.grob_);
442     }
443   else if (Arpeggio::has_interface (info.grob_))
444     {
445       left_objects_.push (info.grob_); 
446     }
447   else if (info.grob_->internal_has_interface (ly_symbol2scm("finger-interface")))
448     {
449       left_objects_.push (info.grob_); 
450     }
451 }
452
453 void
454 Accidental_engraver::process_music ()
455 {
456   SCM sig = get_property ("keySignature");
457
458   /* Detect key sig changes.
459      Update all parents and children
460   */
461   if (last_keysig_ != sig)
462     {
463       Translator_group * trans_ = daddy_trans_;
464       while (trans_)
465         {
466           trans_ -> set_property ("localKeySignature",  ly_deep_copy (sig));
467           trans_ = trans_->daddy_trans_;
468         }
469       set_property_on_children(daddy_trans_,"localKeySignature", sig);
470
471       last_keysig_ = sig;
472     }
473 }
474
475
476
477
478
479 ENTER_DESCRIPTION (Accidental_engraver,
480 "Make accidentals.  Catches note heads, ties and notices key-change "
481 " events.  Due to interaction with ties (which don't come together "
482 " with note heads), this needs to be in a context higher than Tie_engraver.",
483                "Accidental",
484 /* accepts */     "",
485                "finger-interface rhythmic-head-interface tie-interface arpeggio-interface",
486                "localKeySignature extraNatural autoAccidentals autoCautionaries",
487                    "localKeySignature");