]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-engraver.cc
* lily/parser.yy (book_body): set default bookpaper.
[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   SCM prev_acc = scm_int2num (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           prev_acc = ly_cdr (prevs[i]);
180           break;
181         }
182     }
183
184   /*
185     UGH. prev_acc can be #t in case of ties. What is this for?
186     
187    */
188   int p = ly_c_number_p (prev_acc) ? ly_scm2int (prev_acc) : 0;
189
190
191   int num;
192   if (a == p && ly_c_number_p (prev_acc))
193     num = 0;
194   else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
195     num = 2;
196   else
197     num = 1;
198
199   *different = (a != p);
200   return num;
201 }
202
203 static int
204 number_accidentals (bool *different,
205                     Pitch *pitch, Context * origin, 
206                     SCM accidentals, int curbarnum)
207 {
208   int number = 0;
209
210   *different = false;
211   if (ly_c_pair_p (accidentals) && !ly_c_symbol_p (ly_car (accidentals)))
212     warning (_f ("Accidental typesetting list must begin with context-name: %s", 
213                  ly_scm2string (ly_car (accidentals)).to_str0 ()));
214   
215   for (; ly_c_pair_p (accidentals) && origin; accidentals = ly_cdr (accidentals))
216     {
217       // If pair then it is a new accidentals typesetting rule to be checked
218       SCM rule = ly_car (accidentals);
219       if (ly_c_pair_p (rule))
220         {
221           SCM type = ly_car (rule);
222           SCM lazyness = ly_cdr (rule);
223           SCM localsig = origin->get_property ("localKeySignature");
224           
225           bool same_octave_b = 
226             ly_c_eq_p (ly_symbol2scm ("same-octave"), type);
227           bool any_octave_b = 
228             ly_c_eq_p (ly_symbol2scm ("any-octave"), type);
229
230           if (same_octave_b || any_octave_b)
231             {
232               bool d = false;
233               int n = number_accidentals_from_sig
234                 (&d, localsig, pitch, curbarnum, lazyness, any_octave_b);
235               *different = *different || d;
236               number = max (number, n);     
237             }
238           else
239             warning (_f ("ignoring unknown accidental: %s", 
240                          ly_symbol2string (type).to_str0 ()));
241         }
242       
243
244       /*
245         if symbol then it is a context name. Scan parent contexts to find it.
246       */
247       else if (ly_c_symbol_p (rule))
248         {
249           Context * dad = origin;
250           while (dad && !dad->is_alias (rule))
251             dad = dad->get_parent_context ();
252       
253           if (dad)
254             origin = dad;
255         }
256       else warning (_f ("Accidental rule must be pair or context-name; Found %s", 
257                         ly_scm2string (rule).to_str0 ()));
258     }
259
260   return number;
261 }
262
263 int
264 Accidental_engraver::get_bar_number ()
265 {
266   SCM barnum = get_property ("currentBarNumber");
267   SCM smp = get_property ("measurePosition");
268
269   int bn = robust_scm2int (barnum, 0);
270   
271   Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
272   if (mp.main_part_ < Rational (0))
273     bn --;
274   
275   return bn;
276 }
277
278 void
279 Accidental_engraver::process_acknowledged_grobs ()
280 {
281   if (accidentals_.size () && !accidentals_.top ().done_)
282     {
283       SCM accidentals =  get_property ("autoAccidentals");
284       SCM cautionaries =  get_property ("autoCautionaries");
285       int barnum = get_bar_number ();
286       
287       bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
288       for (int i = 0; i  < accidentals_.size (); i++) 
289         {
290           if (accidentals_[i].done_ )
291             continue;
292           accidentals_[i].done_  = true;
293           Grob * support = accidentals_[i].head_;
294           Music * note = accidentals_[i].melodic_;
295           Context * origin = accidentals_[i].origin_;
296
297           Pitch * pitch = unsmob_pitch (note->get_property ("pitch"));
298           if (!pitch)
299             continue;
300
301           bool different = false;
302           bool different_caut = false;
303           
304           int num = number_accidentals (&different,
305                                         pitch, origin,
306                                         accidentals, barnum);
307           int num_caut = number_accidentals (&different_caut,
308                                              pitch, origin,
309                                              cautionaries, barnum);
310
311           bool cautionary = to_boolean (note->get_property ("cautionary"));
312           
313           if (num_caut > num)
314             {
315               num = num_caut;
316               different = different_caut;
317               cautionary = true;
318             }
319
320           if (num == 0 && to_boolean (note->get_property ("force-accidental")))
321             num = 1;
322           
323
324           /*
325             Can not look for ties: it's not guaranteed that they reach
326             us before the notes
327            */
328         
329           if (num)
330             {
331               /*
332                 We construct the accidentals at the originating Voice
333                 level, so that we get the property settings for
334                 Accidental from the respective Voice.
335                */
336               Grob * a = make_item_from_properties (origin->implementation (),
337                                                     ly_symbol2scm ("Accidental"),
338                                                     note->self_scm ()
339                                                     );
340               a->set_parent (support, Y_AXIS);
341
342               if (!accidental_placement_)
343                 {
344                   accidental_placement_ = make_item ("AccidentalPlacement", a->self_scm ());
345                 }
346               
347               Accidental_placement::add_accidental (accidental_placement_, a);
348
349               
350               SCM accs = scm_cons (scm_int2num (pitch->get_alteration ()), SCM_EOL);
351               if (num == 2 && extra_natural_b)
352                 accs = scm_cons (scm_int2num (0), accs);
353
354               /* TODO:
355
356               add cautionary option in accidental.
357                */
358
359               if (cautionary)
360                 {
361                   a->set_property ("cautionary", SCM_BOOL_T);
362                 }
363               
364               
365               support->set_property ("accidental-grob", a->self_scm ());
366
367               a->set_property ("accidentals", accs);
368               accidentals_[i].accidental_ = a;
369
370               /*
371                 We add the accidentals to the support of the arpeggio,
372                 so it is put left of the accidentals.
373               */
374               for (int i = 0;  i < left_objects_.size ();  i++)
375                 Side_position_interface::add_support (left_objects_[i], a);
376               for (int i = 0;  i < right_objects_.size ();  i++)
377                 Side_position_interface::add_support (a, right_objects_[i]);
378             }
379         }
380     }
381 }
382
383
384
385
386 void
387 Accidental_engraver::finalize ()
388 {
389   last_keysig_ = SCM_EOL;
390 }
391
392 void
393 Accidental_engraver::stop_translation_timestep ()
394 {
395   for (int j  = ties_.size (); j --; )
396     {
397       Grob * r = Tie::head (ties_[j], RIGHT);
398       for (int i = accidentals_.size ();  i--;)
399         if (accidentals_[i].head_ == r)
400           {
401             if (Grob * g = accidentals_[i].accidental_)
402               {
403                 g->set_property ("tie", ties_[j]->self_scm ());
404                 accidentals_[i].tied_   = true;
405               }
406             
407             ties_.del (j);
408             break;
409           }
410     }
411
412   for (int i = accidentals_.size (); i--;) 
413     {
414       int barnum = get_bar_number ();
415
416       Music * note = accidentals_[i].melodic_;
417       Context * origin = accidentals_[i].origin_;
418
419       Pitch * pitch = unsmob_pitch (note->get_property ("pitch"));
420       if (!pitch)
421         continue;
422       
423       int n = pitch->get_notename ();
424       int o = pitch->get_octave ();
425       int a = pitch->get_alteration ();
426       SCM key = scm_cons (scm_int2num (o), scm_int2num (n));
427
428       while (origin && origin->where_defined (ly_symbol2scm ("localKeySignature")))
429         {
430           /*
431             huh? we set props all the way to the top? 
432           */
433           SCM localsig = origin->get_property ("localKeySignature");
434           bool change = false;
435           if (accidentals_[i].tied_)
436             {
437               /*
438                 Remember an alteration that is different both from
439                 that of the tied note and of the key signature.
440               */
441               localsig = ly_assoc_front_x
442                 (localsig, key, scm_cons (SCM_BOOL_T, scm_int2num (barnum)));
443
444               change = true;
445             }
446           else
447             {
448               /*
449                 not really really correct if there are more than one
450                 noteheads with the same notename.
451               */
452               localsig = ly_assoc_front_x
453                 (localsig, key, scm_cons (scm_int2num (a), scm_int2num (barnum)));
454
455               change = true;
456             }
457
458           if (change)
459             origin->set_property ("localKeySignature",  localsig);
460           
461           origin = origin->get_parent_context ();
462         }
463     }
464   
465
466   accidental_placement_ = 0;
467   
468   accidentals_.clear ();
469   left_objects_.clear ();
470   right_objects_.clear ();
471 }
472
473 void
474 Accidental_engraver::acknowledge_grob (Grob_info info)
475 {
476   Music * note =  info.music_cause ();
477
478   if (note
479       && note->is_mus_type ("note-event")
480       && Rhythmic_head::has_interface (info.grob_))
481     {
482       if (to_boolean ( get_property ("harmonicAccidentals"))
483           || !ly_c_equal_p (info.grob_->get_property ("style"),
484                           ly_symbol2scm ("harmonic")))
485         {
486           
487           Accidental_entry entry ;
488           entry.head_ = info.grob_;
489           entry.origin_ = info.origin_trans_->context ();
490           entry.melodic_ = note;
491
492           accidentals_.push (entry);
493         }
494     }
495   else if (Tie::has_interface (info.grob_))
496     {
497       ties_.push (dynamic_cast<Spanner*> (info.grob_));
498     }
499   else if (Arpeggio::has_interface (info.grob_))
500     {
501       left_objects_.push (info.grob_); 
502     }
503   else if (info.grob_->internal_has_interface (ly_symbol2scm ("finger-interface")))
504     {
505       left_objects_.push (info.grob_); 
506     }
507 }
508
509 void
510 Accidental_engraver::process_music ()
511 {
512   SCM sig = get_property ("keySignature");
513
514   /* Detect key sig changes.
515      Update all parents and children
516   */
517   if (last_keysig_ != sig)
518     {
519       update_local_key_signature ();
520     }
521 }
522
523
524
525
526
527 ENTER_DESCRIPTION (Accidental_engraver,
528                    "Make accidentals.  Catches note heads, ties and notices key-change "
529                    "events.  This engraver usually lives at Staff level, but "
530                    "reads the settings for Accidental at @code{Voice} level, " 
531                    "so you can @code{\\override} them at @code{Voice}. "
532                    ,
533                    
534                    "Accidental",
535                    "",
536                "finger-interface rhythmic-head-interface tie-interface arpeggio-interface",
537                "localKeySignature harmonicAccidentals extraNatural autoAccidentals autoCautionaries",
538                    "localKeySignature");