]> git.donarmstrong.com Git - lilypond.git/blob - lily/new-accidental-engraver.cc
262c3060acf61fa1f00d6168f60de18212deaaa2
[lilypond.git] / lily / new-accidental-engraver.cc
1 /*
2   accidental-engraver.cc -- implement accidental_engraver
3
4   (c)  1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5   Modified 2001--2002 by Rune Zedeler <rz@daimi.au.dk>
6 */
7
8 #include "musical-request.hh"
9 #include "command-request.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
20 #include "translator-group.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 New_accidental_entry {
34   int pass_done_;
35   int number_accidentals_;
36   int number_cautionaries_;
37   bool different_;
38   Note_req * melodic_;
39   Grob * accidental_;
40   Translator_group *origin_;
41   Grob*  head_;
42   New_accidental_entry();
43 };
44
45 New_accidental_entry::New_accidental_entry()
46 {
47   pass_done_ = 0;
48   number_accidentals_ = 0;
49   number_cautionaries_ = 0;
50   different_ = false;
51   melodic_ = 0;
52   accidental_ = 0;
53   origin_ = 0;
54   head_ = 0;
55 }
56
57 struct New_accidental_engraver : Engraver {
58 protected:
59   TRANSLATOR_DECLARATIONS (New_accidental_engraver);
60   virtual void process_music ();
61   virtual void acknowledge_grob (Grob_info);
62   virtual void stop_translation_timestep ();
63   virtual void initialize ();
64   virtual void process_acknowledged_grobs ();
65   virtual void finalize ();
66   virtual void process_grobs_first_pass ();
67   virtual void process_grobs_second_pass ();
68
69   public:
70
71   /*
72     TODO -> property.
73     
74     This is not a property, and it is not protected.  This poses a
75     very small risk of the value being GC'd from under us.
76   */
77   SCM last_keysig_;
78
79   /*
80     Urgh. Since the accidentals depend on lots of variables, we have to
81     store all information before we can really create the accidentals.
82   */
83   Link_array<Grob> arpeggios_;
84
85   Grob * accidental_placement_;
86   
87
88   /*
89     The next 
90    */
91   Array<New_accidental_entry> accidental_arr_;
92   
93   Link_array<Grob> tie_arr_;
94
95
96 };
97
98
99 New_accidental_engraver::New_accidental_engraver ()
100 {
101   accidental_placement_ = 0;
102   last_keysig_ = SCM_EOL;
103 }
104
105 /* inserts the source alist into the destination alist, erasing old entries.
106    result is: dest = merged
107 */ 
108 static SCM merge_alists_front_x (SCM src, SCM dest) {
109   if(gh_pair_p(src)) {
110     dest = merge_alists_front_x(ly_cdr(src),dest);
111     dest = ly_assoc_front_x(dest, ly_caar(src), ly_cdar(src));
112   }
113   return dest;
114 }
115
116 static void merge_property_on_children (Translator_group * trans,
117                                        const char * from_sym, const char * to_sym)
118 {
119   SCM from = trans->get_property(from_sym);
120   SCM to = trans->get_property(to_sym);
121   to = merge_alists_front_x(from, to);
122   trans->set_property (to_sym,  to);
123   trans->set_property (from_sym, SCM_EOL);
124   for (SCM p = trans -> trans_group_list_; gh_pair_p (p); p = ly_cdr(p)) {
125     Translator_group *trg =  dynamic_cast<Translator_group*> (unsmob_translator (ly_car (p)));
126     merge_property_on_children(trg, from_sym, to_sym);
127   }
128 }
129
130 static void merge_property_on_family (Translator_group * trans,
131                                       const char * from_sym, const char * to_sym)
132 {
133   merge_property_on_children (trans, from_sym, to_sym);
134   trans = trans->daddy_trans_l_;
135   while (trans)
136     {
137       SCM from = trans->get_property(from_sym);
138       SCM to = trans->get_property(to_sym);
139       to = merge_alists_front_x(from, to);
140       trans->set_property (to_sym,  to);
141       trans->set_property (from_sym, SCM_EOL);
142       trans = trans->daddy_trans_l_;
143     }
144 }
145
146 static void set_property_on_children (Translator_group * trans, const char * sym, SCM val)
147 {
148   trans->set_property (sym, val);
149   for (SCM p = trans -> trans_group_list_; gh_pair_p (p); p = ly_cdr(p)) {
150     Translator_group *trg =  dynamic_cast<Translator_group*> (unsmob_translator (ly_car (p)));
151     set_property_on_children(trg,sym,ly_deep_copy(val));
152   }
153 }
154
155 static void set_property_on_family(Translator_group * trans, const char * sym, SCM val)
156 {
157   set_property_on_children (trans, sym, val);
158   trans = trans->daddy_trans_l_;
159   while (trans)
160     {
161       trans -> set_property (sym,  ly_deep_copy (val));
162       trans = trans->daddy_trans_l_;
163     }
164 }
165
166 void
167 New_accidental_engraver::initialize ()
168 {
169   // to ensure that process_music will initialize last_keysig_
170   last_keysig_ = SCM_BOOL_F;
171 }
172
173 /*
174 calculates the number of accidentals on basis of the current local key sig
175   (passed as argument)
176   Returns number of accidentals (0, 1 or 2).
177     Negative (-1 or -2) if accidental has changed.
178
179 */
180 static int
181 number_accidentals (SCM sig, Note_req * note_l, Pitch *pitch, SCM curbarnum, SCM lazyness, 
182                     bool ignore_octave_b)
183 {
184   int n = pitch->notename_i_;
185   int o = pitch->octave_i_;
186   int a = pitch->alteration_i_;
187   int curbarnum_i = gh_scm2int (curbarnum);
188   int accbarnum_i = 0;
189
190   SCM prev;
191   if (ignore_octave_b)
192     prev = ly_assoc_cdr (gh_int2scm (n), sig);
193   else
194     prev = gh_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), sig);
195
196   /* should really be true unless prev == SCM_BOOL_F */
197   if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
198     {
199       accbarnum_i = gh_scm2int (ly_cddr (prev));
200       prev = gh_cons (ly_car (prev), ly_cadr (prev));
201     }
202   
203   /* If an accidental was not found or the accidental was too old */
204   if (prev == SCM_BOOL_F ||
205       (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
206     prev = gh_assoc (gh_int2scm (n), sig);
207
208
209   SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
210
211   int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
212
213   int num;
214   if (a == p
215       && !to_boolean (note_l->get_mus_property ("force-accidental"))
216       && gh_number_p (prev_acc))
217     num = 0;
218   else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
219     num = 2;
220   else
221     num = 1;
222   
223   return a == p ? num : -num;
224 }
225
226 static int
227 number_accidentals (Note_req * note_l, Pitch *pitch, Translator_group * origin_l, 
228                     SCM accidentals, SCM curbarnum)
229 {
230   int number = 0;
231
232   bool diff = false;
233   if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
234     warning (_f ("Accidental typesetting list must begin with context-name: %s", 
235                  ly_scm2string (ly_car (accidentals)).ch_C ()));
236   
237   while (gh_pair_p (accidentals) && origin_l)
238     {
239       // If pair then it is a new accidentals typesetting rule to be checked
240       if (gh_pair_p (ly_car (accidentals)))
241         {
242           SCM type = gh_caar (accidentals);
243           SCM lazyness = gh_cdar (accidentals);
244           SCM localsig = origin_l->get_property ("localKeySignature");
245           
246           bool same_octave_b = 
247             gh_eq_p (ly_symbol2scm ("same-octave"), type);
248           bool any_octave_b = 
249             gh_eq_p (ly_symbol2scm ("any-octave"), type);
250
251           if (same_octave_b || any_octave_b)
252             {
253               int n = number_accidentals
254                 (localsig, note_l, pitch, curbarnum, lazyness, any_octave_b);
255               diff = diff || (n < 0);
256               number = max (number, abs (n));     
257             }
258           else
259             warning (_f ("unknown accidental typesetting: %s. Ignored", 
260                          ly_symbol2string (type).ch_C ()));
261         }
262       
263
264       /*
265         if symbol then it is a context name. Scan parent contexts to find it.
266       */
267       else if (gh_symbol_p (ly_car (accidentals)))
268         {
269           String context = ly_symbol2string (ly_car (accidentals));
270           
271           while (origin_l && !origin_l->is_alias_b (context))
272             origin_l = origin_l->daddy_trans_l_;
273       
274           if (!origin_l)
275             warning (_f ("Symbol is not a parent context: %s. Ignored", 
276                          context.ch_C ()));
277         }
278       else warning (_f ("Accidental typesetting must be pair or context-name: %s", 
279                         ly_scm2string (ly_car (accidentals)).ch_C ()));
280       
281       accidentals = ly_cdr (accidentals);
282     }
283   return diff ? -number : number;
284 }
285
286
287 /* 
288   Perhaps one should join the two functions into one function taking an
289   argument (pass).
290   OTOH even though code would be smaller, spaghetti-level would increase.
291 */
292 void
293 New_accidental_engraver::process_grobs_first_pass ()
294 {
295   SCM accidentals =  get_property ("autoAccidentals");
296   SCM cautionaries =  get_property ("autoCautionaries");
297   SCM barnum = get_property ("currentBarNumber");
298
299   for (int i = 0; i  < accidental_arr_.size (); i++) 
300     {
301       if (accidental_arr_[i].pass_done_ >= 1)
302         continue;
303       accidental_arr_[i].pass_done_  = 1;
304
305       Grob * support_l = accidental_arr_[i].head_;
306       Note_req * note_l = accidental_arr_[i].melodic_;
307       Translator_group * origin_l = accidental_arr_[i].origin_;
308       Pitch * pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
309
310       int num;
311       num = number_accidentals (note_l, pitch, origin_l, accidentals, barnum);
312       accidental_arr_[i].number_accidentals_ = abs(num);
313       accidental_arr_[i].different_ = num<0;
314
315       num = number_accidentals (note_l, pitch, origin_l, cautionaries, barnum);
316       accidental_arr_[i].number_cautionaries_ = abs(num);
317       accidental_arr_[i].different_ = accidental_arr_[i].different_ || num<0;
318
319       bool tie_changes = false;
320       for (int j = 0; j < tie_arr_.size (); j++)
321         if (support_l == Tie::head (tie_arr_[j], RIGHT))
322           tie_changes = accidental_arr_[i].different_;
323       int n = pitch->notename_i_;
324       int o = pitch->octave_i_;
325       int a = pitch->alteration_i_;
326       SCM o_s = gh_int2scm (o);
327       SCM n_s = gh_int2scm (n);
328       SCM on_s = gh_cons (o_s,n_s);
329       
330       while (origin_l)
331         {
332           SCM sigch = origin_l->get_property ("localKeySignatureChanges");
333           SCM alt;
334           if (tie_changes)
335             /*
336               Remember an alteration that is different both from
337               that of the tied note and of the key signature.
338             */
339             alt = SCM_BOOL_T;
340           else
341             alt = gh_int2scm (a);
342           bool other_alt_same_oct = false;
343           bool other_alt_any_oct = false;
344           for (SCM j = sigch; gh_pair_p(j); j = ly_cdr(j)) {
345             SCM entry = ly_car(j);
346             /* if same notename has a different alt already recorded: */
347             if(gh_equal_p(ly_cdar(entry),n_s) && !gh_equal_p(ly_cadr(entry),alt))
348               {
349                 /* if it is also in same octave */
350                 if(gh_equal_p(ly_caar(entry),o_s))
351                   other_alt_same_oct = true;
352                 else
353                   other_alt_any_oct = true;
354               }
355           }
356           if(other_alt_same_oct)
357             alt = SCM_BOOL_T;
358           sigch = ly_assoc_front_x (sigch, on_s, gh_cons(alt,barnum)); 
359           if(other_alt_any_oct && !other_alt_same_oct) {
360             sigch = ly_assoc_front_x (sigch, on_s, gh_cons(SCM_BOOL_T,barnum));
361           }
362           origin_l->set_property ("localKeySignatureChanges",  sigch);
363           origin_l = origin_l->daddy_trans_l_;  
364         }
365     }
366 }
367
368 void
369 New_accidental_engraver::process_grobs_second_pass ()
370 {
371   SCM accidentals =  get_property ("autoAccidentals");
372   SCM cautionaries =  get_property ("autoCautionaries");
373   SCM barnum = get_property ("currentBarNumber");
374   
375   bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
376   for (int i = 0; i  < accidental_arr_.size (); i++) 
377     {
378       if (accidental_arr_[i].pass_done_ >= 2)
379         continue;
380       accidental_arr_[i].pass_done_  = 2;
381       Grob * support_l = accidental_arr_[i].head_;
382       Note_req * note_l = accidental_arr_[i].melodic_;
383       Translator_group * origin_l = accidental_arr_[i].origin_;
384       
385       Pitch * pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
386
387       int num;
388       num = number_accidentals (note_l, pitch, origin_l, accidentals, barnum);
389       accidental_arr_[i].number_accidentals_ =
390         max (accidental_arr_[i].number_accidentals_, abs(num));
391       accidental_arr_[i].different_ = accidental_arr_[i].different_ || num<0;
392
393       num = number_accidentals (note_l, pitch, origin_l, cautionaries, barnum);
394       accidental_arr_[i].number_cautionaries_ =
395         max (accidental_arr_[i].number_cautionaries_, abs(num));
396       accidental_arr_[i].different_ = accidental_arr_[i].different_ || num<0;
397
398
399       bool cautionary = to_boolean (note_l->get_mus_property ("cautionary"));
400       
401       if (accidental_arr_[i].number_cautionaries_ >accidental_arr_[i].number_accidentals_ )
402         {
403           num = accidental_arr_[i].number_cautionaries_;
404           cautionary = true;
405         }
406       else
407           num = accidental_arr_[i].number_accidentals_;
408
409       bool tie_changes = false;
410       Grob *tie_break_reminder = 0;
411       for (int j = 0; j < tie_arr_.size (); j++)
412         if (support_l == Tie::head (tie_arr_[j], RIGHT))
413           {
414             tie_changes = accidental_arr_[i].different_;
415             tie_break_reminder = tie_arr_[j];
416           }
417       
418       if (num)
419         {
420           Grob * a = new Item (get_property ("Accidental"));
421           a->set_parent (support_l, Y_AXIS);
422           
423           if (!accidental_placement_)
424             {
425               accidental_placement_ = new Item (get_property ("AccidentalPlacement"));
426               announce_grob (accidental_placement_, a->self_scm());
427             }
428           
429           Accidental_placement::add_accidental (accidental_placement_, a);
430           announce_grob (a, SCM_EOL);
431           
432           
433           SCM accs = gh_cons (gh_int2scm (pitch->alteration_i_), SCM_EOL);
434           if (num == 2 && extra_natural_b)
435             accs = gh_cons (gh_int2scm (0), accs);
436           
437           if (cautionary)
438             {
439               a->set_grob_property ("cautionary", SCM_BOOL_T);
440             }
441           
442           if (tie_break_reminder)
443             {
444               a->set_grob_property ("tie", tie_break_reminder->self_scm());
445             }
446           
447           
448           support_l->set_grob_property ("accidental-grob", a->self_scm ());
449           
450           a->set_grob_property ("accidentals", accs);
451           accidental_arr_[i].accidental_ = a;
452           /*
453             We add the accidentals to the support of the arpeggio, so it is
454             put left of the accidentals. 
455           */
456           for (int i = 0;  i < arpeggios_.size ();  i++)
457             Side_position_interface::add_support (arpeggios_[i], a);
458         }       
459     }
460 }
461
462 void
463 New_accidental_engraver::process_acknowledged_grobs ()
464 {
465   if (accidental_arr_.size () && accidental_arr_.top().pass_done_ < 1)
466     process_grobs_first_pass ();
467 }
468
469 void
470 New_accidental_engraver::finalize ()
471 {
472
473 }
474
475 void
476 New_accidental_engraver::stop_translation_timestep ()
477 {
478   merge_property_on_family(daddy_trans_l_, "localKeySignatureChanges", "localKeySignature");
479   if (accidental_arr_.size () && accidental_arr_.top().pass_done_ < 2)
480     process_grobs_second_pass ();
481
482   for (int i = 0; i < accidental_arr_.size(); i++)
483     {
484       Grob *a = accidental_arr_[i].accidental_;
485       if (a)
486         {
487           typeset_grob (a);
488         }
489     }
490
491   if (accidental_placement_)
492     typeset_grob(accidental_placement_);
493   accidental_placement_ = 00;
494
495   set_property_on_family(daddy_trans_l_, "localKeySignatureChanges", SCM_EOL);  
496   accidental_arr_.clear();
497   arpeggios_.clear ();
498   tie_arr_.clear ();
499 }
500
501 void
502 New_accidental_engraver::acknowledge_grob (Grob_info info)
503 {
504   Note_req * note_l =  dynamic_cast <Note_req *> (info.music_cause ());
505
506   if (note_l && Rhythmic_head::has_interface (info.grob_l_))
507     {
508       New_accidental_entry entry ;
509       entry.head_ = info.grob_l_;
510       entry.origin_ = info.origin_trans_l_->daddy_trans_l_;
511       entry.melodic_ = note_l;
512
513       accidental_arr_.push (entry);
514     }
515   else if (Tie::has_interface (info.grob_l_))
516     {
517       tie_arr_.push (info.grob_l_);
518     }
519   else if (Arpeggio::has_interface (info.grob_l_))
520     {
521       arpeggios_.push (info.grob_l_); 
522     }
523   
524 }
525
526 void
527 New_accidental_engraver::process_music ()
528 {
529   SCM sig = get_property ("keySignature");
530
531   /* Detect key sig changes.
532      Update all parents and children
533   */
534   if (last_keysig_ != sig)
535     {
536       set_property_on_family(daddy_trans_l_, "localKeySignature", sig);
537       set_property_on_family(daddy_trans_l_, "localKeySignatureChanges", SCM_EOL); //This souldn't be neccesary
538       last_keysig_ = sig;
539     }
540 }
541
542
543
544
545
546 ENTER_DESCRIPTION (New_accidental_engraver,
547 "Make accidentals.  Catches note heads, ties and notices key-change
548 events.  Due to interaction with ties (which don't come together
549 with note heads), this needs to be in a context higher than Tie_engraver.",
550                    
551                "Accidental",
552                "rhythmic-head-interface tie-interface arpeggio-interface",
553                "localKeySignature localKeySignatureChanges extraNatural autoAccidentals autoCautionaries",
554                    "localKeySignature localKeySignatureChanges");