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