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