]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-engraver.cc
release commit
[lilypond.git] / lily / 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 "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
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 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   /*
63     TODO -> property.
64     
65     This is not a property, and it is not protected.  This poses a
66     very small risk of the value being GC'd from under us.
67   */
68   SCM last_keysig_;
69
70   /*
71     Urgh. Since the accidentals depend on lots of variables, we have to
72     store all information before we can really create the accidentals.
73   */
74   Link_array<Grob> arpeggios_;
75
76   Grob * accidental_placement_;
77   
78
79   /*
80     The next 
81    */
82   Array<Accidental_entry> accidentals_;
83   
84   Link_array<Grob> ties_;
85
86
87 };
88
89
90 static void set_property_on_children (Translator_group * trans, const char * sym, SCM val)
91 {
92   trans->set_property (sym, val);
93   for (SCM p = trans -> trans_group_list_; gh_pair_p (p); p = ly_cdr(p)) {
94     Translator_group *trg =  dynamic_cast<Translator_group*> (unsmob_translator (ly_car (p)));
95     set_property_on_children(trg,sym,ly_deep_copy(val));
96   }
97 }
98
99 Accidental_engraver::Accidental_engraver ()
100 {
101   accidental_placement_ = 0;
102   last_keysig_ = SCM_EOL;
103 }
104
105 void
106 Accidental_engraver::initialize ()
107 {
108   last_keysig_ = get_property ("keySignature");
109
110   Translator_group * trans_ = daddy_trans_;
111   while (trans_)
112     {
113       trans_ -> set_property ("localKeySignature",  ly_deep_copy (last_keysig_));
114       trans_ = trans_->daddy_trans_;
115     }
116   set_property_on_children (daddy_trans_,"localKeySignature", last_keysig_);
117 }
118
119 /*
120
121 calculates the number of accidentals on basis of the current local key sig
122   (passed as argument)
123   Returns number of accidentals (0, 1 or 2).
124     Negative (-1 or -2) if accidental has changed.
125
126 */
127 static int
128 number_accidentals (SCM sig, Music * note, Pitch *pitch, SCM curbarnum, SCM lazyness, 
129                     bool ignore_octave_b)
130 {
131   int n = pitch->notename_;
132   int o = pitch->octave_;
133   int a = pitch->alteration_;
134   int curbarnum_i = gh_scm2int (curbarnum);
135   int accbarnum_i = 0;
136
137   SCM prev;
138   if (ignore_octave_b)
139     prev = ly_assoc_cdr (scm_int2num (n), sig);
140   else
141     prev = gh_assoc (gh_cons (scm_int2num (o), scm_int2num (n)), sig);
142
143   /* should really be true unless prev == SCM_BOOL_F */
144   if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
145     {
146       accbarnum_i = gh_scm2int (ly_cddr (prev));
147       prev = gh_cons (ly_car (prev), ly_cadr (prev));
148     }
149   
150   /* If an accidental was not found or the accidental was too old */
151   if (prev == SCM_BOOL_F ||
152       (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
153     prev = gh_assoc (scm_int2num (n), sig);
154
155
156   SCM prev_acc = (prev == SCM_BOOL_F) ? scm_int2num (0) : ly_cdr (prev);
157
158   int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
159
160   int num;
161   if (a == p && gh_number_p (prev_acc))
162     num = 0;
163   else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
164     num = 2;
165   else
166     num = 1;
167   
168   return a == p ? num : -num;
169 }
170
171 static int
172 number_accidentals (Music * note, Pitch *pitch, Translator_group * origin, 
173                     SCM accidentals, SCM curbarnum)
174 {
175   int number = 0;
176
177   bool diff = false;
178   if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
179     warning (_f ("Accidental typesetting list must begin with context-name: %s", 
180                  ly_scm2string (ly_car (accidentals)).to_str0 ()));
181   
182   while (gh_pair_p (accidentals) && origin)
183     {
184       // If pair then it is a new accidentals typesetting rule to be checked
185       if (gh_pair_p (ly_car (accidentals)))
186         {
187           SCM type = gh_caar (accidentals);
188           SCM lazyness = gh_cdar (accidentals);
189           SCM localsig = origin->get_property ("localKeySignature");
190           
191           bool same_octave_b = 
192             gh_eq_p (ly_symbol2scm ("same-octave"), type);
193           bool any_octave_b = 
194             gh_eq_p (ly_symbol2scm ("any-octave"), type);
195
196           if (same_octave_b || any_octave_b)
197             {
198               int n = number_accidentals
199                 (localsig, note, pitch, curbarnum, lazyness, any_octave_b);
200               diff = diff || (n < 0);
201               number = max (number, abs (n));     
202             }
203           else
204             warning (_f ("unknown accidental typesetting: %s. Ignored", 
205                          ly_symbol2string (type).to_str0 ()));
206         }
207       
208
209       /*
210         if symbol then it is a context name. Scan parent contexts to find it.
211       */
212       else if (gh_symbol_p (ly_car (accidentals)))
213         {
214           String context = ly_symbol2string (ly_car (accidentals));
215           
216           while (origin && !origin->is_alias_b (context))
217             origin = origin->daddy_trans_;
218       
219           if (!origin)
220             warning (_f ("Symbol is not a parent context: %s. Ignored", 
221                          context.to_str0 ()));
222         }
223       else warning (_f ("Accidental typesetting must be pair or context-name: %s", 
224                         ly_scm2string (ly_car (accidentals)).to_str0 ()));
225       
226       accidentals = ly_cdr (accidentals);
227     }
228   return diff ? -number : number;
229 }
230
231 void
232 Accidental_engraver::process_acknowledged_grobs ()
233 {
234   if (accidentals_.size () && !accidentals_.top().done_)
235     {
236       //SCM localsig = get_property ("localKeySignature");
237       SCM accidentals =  get_property ("autoAccidentals");
238       SCM cautionaries =  get_property ("autoCautionaries");
239       SCM barnum = get_property ("currentBarNumber");
240       SCM smp = get_property("measurePosition");
241       Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
242       if(mp.main_part_<Rational(0) && gh_number_p(barnum)) barnum = scm_int2num(gh_scm2int(barnum)-1);
243       bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
244       for (int i = 0; i  < accidentals_.size (); i++) 
245         {
246           if (accidentals_[i].done_ )
247             continue;
248           accidentals_[i].done_  = true;
249           Grob * support = accidentals_[i].head_;
250           Music * note = accidentals_[i].melodic_;
251           Translator_group * origin = accidentals_[i].origin_;
252
253           Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
254           int num = number_accidentals (note, pitch, origin, accidentals, barnum);
255           int num_caut = number_accidentals (note, pitch, origin, cautionaries, barnum);
256           bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
257           
258           if (abs (num_caut) > abs (num))
259             {
260               num = num_caut;
261               cautionary = true;
262             }
263
264           if(num==0 && to_boolean (note->get_mus_property ("force-accidental")))
265              num=1;
266           
267           bool different = num < 0;
268           num = abs (num);
269
270           /* see if there's a tie that "changes" the accidental */
271           /* works because if there's a tie, the note to the left
272              is of the same pitch as the actual note */
273
274           Grob *tie_break_reminder = 0;
275           bool tie_changes = false;
276           for (int j = 0; j < ties_.size (); j++)
277             if (support == Tie::head (ties_[j], RIGHT))
278               {
279                 tie_changes = different;
280
281                 /* Enable accidentals for broken tie
282
283                 We only want an accidental on a broken tie, 
284                 if the tie changes the accidental.
285                    
286                 Maybe check property noTieBreakForceAccidental? */
287                 if (different)
288                   tie_break_reminder = ties_[j];
289                 break;
290               }
291
292           if (num)
293             {
294               Grob * a = new Item (get_property ("Accidental"));
295               a->set_parent (support, Y_AXIS);
296
297               if (!accidental_placement_)
298                 {
299                   accidental_placement_ = new Item (get_property ("AccidentalPlacement"));
300                   announce_grob (accidental_placement_, a->self_scm());
301                 }
302               
303               Accidental_placement::add_accidental (accidental_placement_, a);
304               announce_grob (a, SCM_EOL);
305
306               
307               SCM accs = gh_cons (scm_int2num (pitch->alteration_), SCM_EOL);
308               if (num == 2 && extra_natural_b)
309                 accs = gh_cons (scm_int2num (0), accs);
310
311               /* TODO:
312
313               add cautionary option in accidental.
314                */
315
316               if (cautionary)
317                 {
318                   a->set_grob_property ("cautionary", SCM_BOOL_T);
319                 }
320               
321               if (tie_break_reminder)
322                 {
323                   // TODO.
324                   a->set_grob_property ("tie", tie_break_reminder->self_scm());
325                 }
326               
327               
328               support->set_grob_property ("accidental-grob", a->self_scm ());
329
330               a->set_grob_property ("accidentals", accs);
331               accidentals_[i].accidental_ = a;
332  /*
333         We add the accidentals to the support of the arpeggio, so it is put left of the
334         accidentals. 
335         
336       */
337               for (int i = 0;  i < arpeggios_.size ();  i++)
338                 Side_position_interface::add_support (arpeggios_[i], a);
339             }
340           
341
342           /*
343             We should not record the accidental if it is the first
344             note and it is tied from the previous measure.
345
346             Checking whether it is tied also works mostly, but will it
347             always do the correct thing?
348           */
349           
350
351           int n = pitch->notename_;
352           int o = pitch->octave_;
353           int a = pitch->alteration_;
354           SCM on_s = gh_cons (scm_int2num (o), scm_int2num (n));
355
356           /*
357             TODO: Speed this up!
358             
359             Perhaps only check translators mentioned in the auto-accidentals?
360             -rz
361
362             TODO: profile this.
363             
364             I'd be surprised if the impact of this would be
365             measurable.  Anyway, it seems localsig doesn't change
366             every time-step, but a set_property() is done every
367             time. We could save on that, probably.
368
369             --hwn.
370             
371             
372           */
373
374           while (origin)
375             {
376               SCM localsig = origin->get_property ("localKeySignature");
377               if (tie_changes)
378                 {
379                   /*
380                     Remember an alteration that is different both from
381                     that of the tied note and of the key signature.
382                   */
383                   localsig = ly_assoc_front_x
384                     (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
385                 }
386               else
387                 {
388                   /*
389                     not really really correct if there are more than one
390                     noteheads with the same notename.
391                   */
392                   localsig = ly_assoc_front_x
393                     (localsig, on_s, gh_cons (scm_int2num (a), barnum)); 
394                 }
395               origin->set_property ("localKeySignature",  localsig);
396               origin = origin->daddy_trans_;
397             }
398         }
399     }
400 }
401
402 void
403 Accidental_engraver::finalize ()
404 {
405
406 }
407
408 void
409 Accidental_engraver::stop_translation_timestep ()
410 {
411   for (int i = 0; i < accidentals_.size(); i++)
412     {
413       Grob *a = accidentals_[i].accidental_;
414       if (a)
415         {
416           typeset_grob (a);
417         }
418     }
419
420   if (accidental_placement_)
421     typeset_grob(accidental_placement_);
422   accidental_placement_ = 00;
423   
424   accidentals_.clear();
425   arpeggios_.clear ();
426   ties_.clear ();
427 }
428
429 void
430 Accidental_engraver::acknowledge_grob (Grob_info info)
431 {
432   Music * note =  info.music_cause ();
433
434   if (note
435       && note->is_mus_type("note-event")
436       && Rhythmic_head::has_interface (info.grob_))
437     {
438       Accidental_entry entry ;
439       entry.head_ = info.grob_;
440       entry.origin_ = info.origin_trans_->daddy_trans_;
441       entry.melodic_ = note;
442
443       accidentals_.push (entry);
444     }
445   else if (Tie::has_interface (info.grob_))
446     {
447       ties_.push (info.grob_);
448     }
449   else if (Arpeggio::has_interface (info.grob_))
450     {
451       arpeggios_.push (info.grob_); 
452     }
453   
454 }
455
456 void
457 Accidental_engraver::process_music ()
458 {
459   SCM sig = get_property ("keySignature");
460
461   /* Detect key sig changes.
462      Update all parents and children
463   */
464   if (last_keysig_ != sig)
465     {
466       Translator_group * trans_ = daddy_trans_;
467       while (trans_)
468         {
469           trans_ -> set_property ("localKeySignature",  ly_deep_copy (sig));
470           trans_ = trans_->daddy_trans_;
471         }
472       set_property_on_children(daddy_trans_,"localKeySignature", sig);
473
474       last_keysig_ = sig;
475     }
476 }
477
478
479
480
481
482 ENTER_DESCRIPTION (Accidental_engraver,
483 "Make accidentals.  Catches note heads, ties and notices key-change "
484 " events.  Due to interaction with ties (which don't come together "
485 " with note heads), this needs to be in a context higher than Tie_engraver.",
486                "Accidental",
487 /* accepts */     "",
488                "rhythmic-head-interface tie-interface arpeggio-interface",
489                "localKeySignature extraNatural autoAccidentals autoCautionaries",
490                    "localKeySignature");