]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-engraver.cc
*** empty log message ***
[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 #include "spanner.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 #include "context.hh"
20 #include "protected-scm.hh"
21
22
23 struct Accidental_entry {
24   bool done_;
25   Music * melodic_;
26   Grob * accidental_;
27   Context *origin_;
28   Grob*  head_;
29   bool tied_;
30   Accidental_entry ();
31 };
32
33 Accidental_entry::Accidental_entry ()
34 {
35   tied_ = false;
36   done_ = false;
37   melodic_ =0;
38   accidental_ = 0;
39   origin_ = 0;
40   head_ = 0;
41 }
42
43 struct Accidental_engraver : Engraver {
44
45
46   int get_bar_number ();
47   void update_local_key_signature ();
48
49 protected:
50   TRANSLATOR_DECLARATIONS (Accidental_engraver);
51   virtual void process_music ();
52   virtual void acknowledge_grob (Grob_info);
53   virtual void stop_translation_timestep ();
54   virtual void initialize ();
55   virtual void process_acknowledged_grobs ();
56   virtual void finalize ();
57 public:
58
59   Protected_scm last_keysig_;   // ugh.
60   
61   /*
62     Urgh. Since the accidentals depend on lots of variables, we have to
63     store all information before we can really create the accidentals.
64   */
65   Link_array<Grob> left_objects_;
66   Link_array<Grob> right_objects_;
67
68   Grob * accidental_placement_;
69
70   Array<Accidental_entry> accidentals_;
71   Link_array<Spanner> ties_;
72 };
73
74
75 /*
76   TODO:
77
78   ugh, it is not clear what properties are mutable and which
79   aren't. eg. localKeySignature is changed at runtime, which means
80   that references in grobs should always store ly_deep_copy ()s of
81   those.
82  */
83
84 static void
85 set_property_on_children (Context * trans, const char * sym, SCM val)
86 {
87   trans->set_property (sym, ly_deep_copy (val));
88   for (SCM p = trans->children_contexts (); ly_c_pair_p (p); p = ly_cdr (p))
89     {
90       Context *trg =  unsmob_context (ly_car (p));
91       set_property_on_children (trg, sym, ly_deep_copy (val));
92     }
93 }
94
95 Accidental_engraver::Accidental_engraver ()
96 {
97   accidental_placement_ = 0;
98   last_keysig_ = SCM_EOL;
99 }
100
101
102 void
103 Accidental_engraver::update_local_key_signature ()
104 {
105   last_keysig_ = get_property ("keySignature");
106   set_property_on_children (context (), "localKeySignature", last_keysig_);
107
108   Context * trans = context ()->get_parent_context ();
109
110   /*
111     Huh. Don't understand what this is good for. --hwn.
112    */
113   while (trans && trans->where_defined (ly_symbol2scm ("localKeySignature")))
114     {
115       trans->set_property ("localKeySignature",
116                             ly_deep_copy (last_keysig_));
117       trans = trans->get_parent_context ();
118     }
119 }
120
121 void
122 Accidental_engraver::initialize ()
123 {
124   update_local_key_signature (); 
125 }
126
127 /*
128
129   calculates the number of accidentals on basis of the current local key
130   sig (passed as argument)
131
132   Returns number of accidentals (0, 1 or 2).
133
134 */
135 static int
136 number_accidentals_from_sig (bool *different,
137                              SCM sig, Pitch *pitch, int curbarnum, SCM lazyness, 
138                              bool ignore_octave)
139 {
140   int n = pitch->get_notename ();
141   int o = pitch->get_octave ();
142   int a = pitch->get_alteration ();
143   int accbarnum = -1;
144
145   SCM prevs[3];
146   int bar_nums[3] = {-1,-1,-1};
147   int prev_idx = 0;
148   
149   if (!ignore_octave)
150     {
151       prevs[prev_idx] = scm_assoc (scm_cons (scm_int2num (o), scm_int2num (n)), sig);
152
153       if (ly_c_pair_p (prevs[prev_idx]))
154         prev_idx++;
155     }
156   
157   prevs[prev_idx] = scm_assoc (scm_int2num (n), sig);
158   if (ly_c_pair_p (prevs[prev_idx]))
159     prev_idx++;
160
161   for (int i= 0; i < prev_idx; i++)
162     {
163       if (ly_c_pair_p (prevs[i])
164           && ly_c_pair_p (ly_cdr (prevs[i])))
165         {
166           bar_nums[i]  = ly_scm2int (ly_cddr (prevs[i]));
167           prevs[i] = scm_cons (ly_car (prevs[i]), ly_cadr (prevs[i]));
168         }
169     }
170   
171
172   int p = 0; 
173   for (int i= 0; i < prev_idx; i++)
174     {
175       if (accbarnum < 0
176           || (ly_c_number_p (lazyness)
177               && curbarnum > accbarnum + ly_scm2int (lazyness)))
178         {
179           p = ly_scm2int (ly_cdr (prevs[i]));
180           break;
181         }
182     }
183
184   int num;
185   if (a == p)
186     num = 0;
187   else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
188     num = 2;
189   else
190     num = 1;
191
192   *different = (a != p);
193   return num;
194 }
195
196 static int
197 number_accidentals (bool *different,
198                     Pitch *pitch, Context * origin, 
199                     SCM accidentals, int curbarnum)
200 {
201   int number = 0;
202
203   *different = false;
204   if (ly_c_pair_p (accidentals) && !ly_c_symbol_p (ly_car (accidentals)))
205     warning (_f ("Accidental typesetting list must begin with context-name: %s", 
206                  ly_scm2string (ly_car (accidentals)).to_str0 ()));
207   
208   for (; ly_c_pair_p (accidentals) && origin; accidentals = ly_cdr (accidentals))
209     {
210       // If pair then it is a new accidentals typesetting rule to be checked
211       SCM rule = ly_car (accidentals);
212       if (ly_c_pair_p (rule))
213         {
214           SCM type = ly_car (rule);
215           SCM lazyness = ly_cdr (rule);
216           SCM localsig = origin->get_property ("localKeySignature");
217           
218           bool same_octave_b = 
219             ly_c_eq_p (ly_symbol2scm ("same-octave"), type);
220           bool any_octave_b = 
221             ly_c_eq_p (ly_symbol2scm ("any-octave"), type);
222
223           if (same_octave_b || any_octave_b)
224             {
225               bool d = false;
226               int n = number_accidentals_from_sig
227                 (&d, localsig, pitch, curbarnum, lazyness, any_octave_b);
228               *different = *different || d;
229               number = max (number, n);     
230             }
231           else
232             warning (_f ("ignoring unknown accidental: %s", 
233                          ly_symbol2string (type).to_str0 ()));
234         }
235       
236
237       /*
238         if symbol then it is a context name. Scan parent contexts to find it.
239       */
240       else if (ly_c_symbol_p (rule))
241         {
242           Context * dad = origin;
243           while (dad && !dad->is_alias (rule))
244             dad = dad->get_parent_context ();
245       
246           if (dad)
247             origin = dad;
248         }
249       else warning (_f ("Accidental rule must be pair or context-name; Found %s", 
250                         ly_scm2string (rule).to_str0 ()));
251     }
252
253   return number;
254 }
255
256 int
257 Accidental_engraver::get_bar_number ()
258 {
259   SCM barnum = get_property ("currentBarNumber");
260   SCM smp = get_property ("measurePosition");
261
262   int bn = robust_scm2int (barnum, 0);
263   
264   Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
265   if (mp.main_part_ < Rational (0))
266     bn --;
267   
268   return bn;
269 }
270
271 void
272 Accidental_engraver::process_acknowledged_grobs ()
273 {
274   if (accidentals_.size () && !accidentals_.top ().done_)
275     {
276       SCM accidentals =  get_property ("autoAccidentals");
277       SCM cautionaries =  get_property ("autoCautionaries");
278       int barnum = get_bar_number ();
279       
280       bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
281       for (int i = 0; i  < accidentals_.size (); i++) 
282         {
283           if (accidentals_[i].done_ )
284             continue;
285           accidentals_[i].done_  = true;
286           Grob * support = accidentals_[i].head_;
287           Music * note = accidentals_[i].melodic_;
288           Context * origin = accidentals_[i].origin_;
289
290           Pitch * pitch = unsmob_pitch (note->get_property ("pitch"));
291           if (!pitch)
292             continue;
293
294           bool different = false;
295           bool different_caut = false;
296           
297           int num = number_accidentals (&different,
298                                         pitch, origin,
299                                         accidentals, barnum);
300           int num_caut = number_accidentals (&different_caut,
301                                              pitch, origin,
302                                              cautionaries, barnum);
303
304           bool cautionary = to_boolean (note->get_property ("cautionary"));
305           
306           if (num_caut > num)
307             {
308               num = num_caut;
309               different = different_caut;
310               cautionary = true;
311             }
312
313           if (num == 0 && to_boolean (note->get_property ("force-accidental")))
314             num = 1;
315           
316
317           /*
318             Can not look for ties: it's not guaranteed that they reach
319             us before the notes
320            */
321         
322           if (num)
323             {
324               /*
325                 We construct the accidentals at the originating Voice
326                 level, so that we get the property settings for
327                 Accidental from the respective Voice.
328                */
329               Grob * a = make_item_from_properties (origin->implementation (),
330                                                     ly_symbol2scm ("Accidental"),
331                                                     note->self_scm ()
332                                                     );
333               a->set_parent (support, Y_AXIS);
334
335               if (!accidental_placement_)
336                 {
337                   accidental_placement_ = make_item ("AccidentalPlacement", a->self_scm ());
338                 }
339               
340               Accidental_placement::add_accidental (accidental_placement_, a);
341
342               
343               SCM accs = scm_cons (scm_int2num (pitch->get_alteration ()), SCM_EOL);
344               if (num == 2 && extra_natural_b)
345                 accs = scm_cons (scm_int2num (0), accs);
346
347               /* TODO:
348
349               add cautionary option in accidental.
350                */
351
352               if (cautionary)
353                 {
354                   a->set_property ("cautionary", SCM_BOOL_T);
355                 }
356               
357               
358               support->set_property ("accidental-grob", a->self_scm ());
359
360               a->set_property ("accidentals", accs);
361               accidentals_[i].accidental_ = a;
362
363               /*
364                 We add the accidentals to the support of the arpeggio,
365                 so it is put left of the accidentals.
366               */
367               for (int i = 0;  i < left_objects_.size ();  i++)
368                 Side_position_interface::add_support (left_objects_[i], a);
369               for (int i = 0;  i < right_objects_.size ();  i++)
370                 Side_position_interface::add_support (a, right_objects_[i]);
371             }
372         }
373     }
374 }
375
376
377
378
379 void
380 Accidental_engraver::finalize ()
381 {
382   last_keysig_ = SCM_EOL;
383 }
384
385 void
386 Accidental_engraver::stop_translation_timestep ()
387 {
388   for (int j  = ties_.size (); j --; )
389     {
390       Grob * r = Tie::head (ties_[j], RIGHT);
391       for (int i = accidentals_.size ();  i--;)
392         if (accidentals_[i].head_ == r)
393           {
394             if (Grob * g = accidentals_[i].accidental_)
395               {
396                 g->set_property ("tie", ties_[j]->self_scm ());
397                 accidentals_[i].tied_   = true;
398               }
399             
400             ties_.del (j);
401             break;
402           }
403     }
404
405   for (int i = accidentals_.size (); i--;) 
406     {
407       int barnum = get_bar_number ();
408
409       Music * note = accidentals_[i].melodic_;
410       Context * origin = accidentals_[i].origin_;
411
412       Pitch * pitch = unsmob_pitch (note->get_property ("pitch"));
413       if (!pitch)
414         continue;
415       
416       int n = pitch->get_notename ();
417       int o = pitch->get_octave ();
418       int a = pitch->get_alteration ();
419       SCM on_s = scm_cons (scm_int2num (o), scm_int2num (n));
420
421       while (origin && origin->where_defined (ly_symbol2scm ("localKeySignature")))
422         {
423           /*
424             huh? we set props all the way to the top? 
425           */
426           SCM localsig = origin->get_property ("localKeySignature");
427           bool change = false;
428           if (accidentals_[i].tied_)
429             {
430               /*
431                 Remember an alteration that is different both from
432                 that of the tied note and of the key signature.
433               */
434               localsig = ly_assoc_front_x
435                 (localsig, on_s, scm_cons (SCM_BOOL_T, scm_int2num (barnum)));
436
437               change = true;
438             }
439           else
440             {
441               /*
442                 not really really correct if there are more than one
443                 noteheads with the same notename.
444               */
445               localsig = ly_assoc_front_x
446                 (localsig, on_s, scm_cons (scm_int2num (a), scm_int2num (barnum)));
447
448               change = true;
449             }
450
451           if (change)
452             origin->set_property ("localKeySignature",  localsig);
453           
454           origin = origin->get_parent_context ();
455         }
456     }
457   
458
459   accidental_placement_ = 0;
460   
461   accidentals_.clear ();
462   left_objects_.clear ();
463   right_objects_.clear ();
464 }
465
466 void
467 Accidental_engraver::acknowledge_grob (Grob_info info)
468 {
469   Music * note =  info.music_cause ();
470
471   if (note
472       && note->is_mus_type ("note-event")
473       && Rhythmic_head::has_interface (info.grob_))
474     {
475       if (to_boolean ( get_property ("harmonicAccidentals"))
476           || !ly_c_equal_p (info.grob_->get_property ("style"),
477                           ly_symbol2scm ("harmonic")))
478         {
479           
480           Accidental_entry entry ;
481           entry.head_ = info.grob_;
482           entry.origin_ = info.origin_trans_->context ();
483           entry.melodic_ = note;
484
485           accidentals_.push (entry);
486         }
487     }
488   else if (Tie::has_interface (info.grob_))
489     {
490       ties_.push (dynamic_cast<Spanner*> (info.grob_));
491     }
492   else if (Arpeggio::has_interface (info.grob_))
493     {
494       left_objects_.push (info.grob_); 
495     }
496   else if (info.grob_->internal_has_interface (ly_symbol2scm ("finger-interface")))
497     {
498       left_objects_.push (info.grob_); 
499     }
500 }
501
502 void
503 Accidental_engraver::process_music ()
504 {
505   SCM sig = get_property ("keySignature");
506
507   /* Detect key sig changes.
508      Update all parents and children
509   */
510   if (last_keysig_ != sig)
511     {
512       update_local_key_signature ();
513     }
514 }
515
516
517
518
519
520 ENTER_DESCRIPTION (Accidental_engraver,
521                    "Make accidentals.  Catches note heads, ties and notices key-change "
522                    "events.  This engraver usually lives at Staff level, but "
523                    "reads the settings for Accidental at @code{Voice} level, " 
524                    "so you can @code{\\override} them at @code{Voice}. "
525                    ,
526                    
527                    "Accidental",
528                    "",
529                "finger-interface rhythmic-head-interface tie-interface arpeggio-interface",
530                "localKeySignature harmonicAccidentals extraNatural autoAccidentals autoCautionaries",
531                    "localKeySignature");