]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-engraver.cc
*** empty log message ***
[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 "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 Accidental_entry {
34   bool done_;
35   Note_req * 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, Note_req * 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
162       && !to_boolean (note->get_mus_property ("force-accidental"))
163       && gh_number_p (prev_acc))
164     num = 0;
165   else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
166     num = 2;
167   else
168     num = 1;
169   
170   return a == p ? num : -num;
171 }
172
173 static int
174 number_accidentals (Note_req * note, Pitch *pitch, Translator_group * origin, 
175                     SCM accidentals, SCM curbarnum)
176 {
177   int number = 0;
178
179   bool diff = false;
180   if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
181     warning (_f ("Accidental typesetting list must begin with context-name: %s", 
182                  ly_scm2string (ly_car (accidentals)).to_str0 ()));
183   
184   while (gh_pair_p (accidentals) && origin)
185     {
186       // If pair then it is a new accidentals typesetting rule to be checked
187       if (gh_pair_p (ly_car (accidentals)))
188         {
189           SCM type = gh_caar (accidentals);
190           SCM lazyness = gh_cdar (accidentals);
191           SCM localsig = origin->get_property ("localKeySignature");
192           
193           bool same_octave_b = 
194             gh_eq_p (ly_symbol2scm ("same-octave"), type);
195           bool any_octave_b = 
196             gh_eq_p (ly_symbol2scm ("any-octave"), type);
197
198           if (same_octave_b || any_octave_b)
199             {
200               int n = number_accidentals
201                 (localsig, note, pitch, curbarnum, lazyness, any_octave_b);
202               diff = diff || (n < 0);
203               number = max (number, abs (n));     
204             }
205           else
206             warning (_f ("unknown accidental typesetting: %s. Ignored", 
207                          ly_symbol2string (type).to_str0 ()));
208         }
209       
210
211       /*
212         if symbol then it is a context name. Scan parent contexts to find it.
213       */
214       else if (gh_symbol_p (ly_car (accidentals)))
215         {
216           String context = ly_symbol2string (ly_car (accidentals));
217           
218           while (origin && !origin->is_alias_b (context))
219             origin = origin->daddy_trans_;
220       
221           if (!origin)
222             warning (_f ("Symbol is not a parent context: %s. Ignored", 
223                          context.to_str0 ()));
224         }
225       else warning (_f ("Accidental typesetting must be pair or context-name: %s", 
226                         ly_scm2string (ly_car (accidentals)).to_str0 ()));
227       
228       accidentals = ly_cdr (accidentals);
229     }
230   return diff ? -number : number;
231 }
232
233 void
234 Accidental_engraver::process_acknowledged_grobs ()
235 {
236   if (accidentals_.size () && !accidentals_.top().done_)
237     {
238       //SCM localsig = get_property ("localKeySignature");
239       SCM accidentals =  get_property ("autoAccidentals");
240       SCM cautionaries =  get_property ("autoCautionaries");
241       SCM barnum = get_property ("currentBarNumber");
242       SCM smp = get_property("measurePosition");
243       Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
244       if(mp<0 && gh_number_p(barnum)) barnum = scm_int2num(gh_scm2int(barnum)-1);
245       bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
246       for (int i = 0; i  < accidentals_.size (); i++) 
247         {
248           if (accidentals_[i].done_ )
249             continue;
250           accidentals_[i].done_  = true;
251           Grob * support = accidentals_[i].head_;
252           Note_req * note = accidentals_[i].melodic_;
253           Translator_group * origin = accidentals_[i].origin_;
254
255           Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
256           int num = number_accidentals (note, pitch, origin, accidentals, barnum);
257           int num_caut = number_accidentals (note, pitch, origin, cautionaries, barnum);
258           bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
259           
260           if (abs (num_caut) > abs (num))
261             {
262               num = num_caut;
263               cautionary = true;
264             }
265           
266           bool different = num < 0;
267           num = abs (num);
268
269           /* see if there's a tie that "changes" the accidental */
270           /* works because if there's a tie, the note to the left
271              is of the same pitch as the actual note */
272
273           Grob *tie_break_reminder = 0;
274           bool tie_changes = false;
275           for (int j = 0; j < ties_.size (); j++)
276             if (support == Tie::head (ties_[j], RIGHT))
277               {
278                 tie_changes = different;
279
280                 /* Enable accidentals for broken tie
281
282                 We only want an accidental on a broken tie, 
283                 if the tie changes the accidental.
284                    
285                 Maybe check property noTieBreakForceAccidental? */
286                 if (different)
287                   tie_break_reminder = ties_[j];
288                 break;
289               }
290
291           if (num)
292             {
293               Grob * a = new Item (get_property ("Accidental"));
294               a->set_parent (support, Y_AXIS);
295
296               if (!accidental_placement_)
297                 {
298                   accidental_placement_ = new Item (get_property ("AccidentalPlacement"));
299                   announce_grob (accidental_placement_, a->self_scm());
300                 }
301               
302               Accidental_placement::add_accidental (accidental_placement_, a);
303               announce_grob (a, SCM_EOL);
304
305               
306               SCM accs = gh_cons (scm_int2num (pitch->alteration_), SCM_EOL);
307               if (num == 2 && extra_natural_b)
308                 accs = gh_cons (scm_int2num (0), accs);
309
310               /* TODO:
311
312               add cautionary option in accidental.
313                */
314
315               if (cautionary)
316                 {
317                   a->set_grob_property ("cautionary", SCM_BOOL_T);
318                 }
319               
320               if (tie_break_reminder)
321                 {
322                   // TODO.
323                   a->set_grob_property ("tie", tie_break_reminder->self_scm());
324                 }
325               
326               
327               support->set_grob_property ("accidental-grob", a->self_scm ());
328
329               a->set_grob_property ("accidentals", accs);
330               accidentals_[i].accidental_ = a;
331  /*
332         We add the accidentals to the support of the arpeggio, so it is put left of the
333         accidentals. 
334         
335       */
336               for (int i = 0;  i < arpeggios_.size ();  i++)
337                 Side_position_interface::add_support (arpeggios_[i], a);
338             }
339           
340
341           /*
342             We should not record the accidental if it is the first
343             note and it is tied from the previous measure.
344
345             Checking whether it is tied also works mostly, but will it
346             always do the correct thing?
347           */
348           
349
350           int n = pitch->notename_;
351           int o = pitch->octave_;
352           int a = pitch->alteration_;
353           SCM on_s = gh_cons (scm_int2num (o), scm_int2num (n));
354
355           /*
356             TODO: Speed this up!
357             
358             Perhaps only check translators mentioned in the auto-accidentals?
359             -rz
360
361             TODO: profile this.
362             
363             I'd be surprised if the impact of this would be
364             measurable.  Anyway, it seems localsig doesn't change
365             every time-step, but a set_property() is done every
366             time. We could save on that, probably.
367
368             --hwn.
369             
370             
371           */
372
373           while (origin)
374             {
375               SCM localsig = origin->get_property ("localKeySignature");
376               if (tie_changes)
377                 {
378                   /*
379                     Remember an alteration that is different both from
380                     that of the tied note and of the key signature.
381                   */
382                   localsig = ly_assoc_front_x
383                     (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
384                 }
385               else
386                 {
387                   /*
388                     not really really correct if there are more than one
389                     noteheads with the same notename.
390                   */
391                   localsig = ly_assoc_front_x
392                     (localsig, on_s, gh_cons (scm_int2num (a), barnum)); 
393                 }
394               origin->set_property ("localKeySignature",  localsig);
395               origin = origin->daddy_trans_;
396             }
397         }
398     }
399 }
400
401 void
402 Accidental_engraver::finalize ()
403 {
404
405 }
406
407 void
408 Accidental_engraver::stop_translation_timestep ()
409 {
410   for (int i = 0; i < accidentals_.size(); i++)
411     {
412       Grob *a = accidentals_[i].accidental_;
413       if (a)
414         {
415           typeset_grob (a);
416         }
417     }
418
419   if (accidental_placement_)
420     typeset_grob(accidental_placement_);
421   accidental_placement_ = 00;
422   
423   accidentals_.clear();
424   arpeggios_.clear ();
425   ties_.clear ();
426 }
427
428 void
429 Accidental_engraver::acknowledge_grob (Grob_info info)
430 {
431   Note_req * note =  dynamic_cast <Note_req *> (info.music_cause ());
432
433   if (note && Rhythmic_head::has_interface (info.grob_))
434     {
435       Accidental_entry entry ;
436       entry.head_ = info.grob_;
437       entry.origin_ = info.origin_trans_->daddy_trans_;
438       entry.melodic_ = note;
439
440       accidentals_.push (entry);
441     }
442   else if (Tie::has_interface (info.grob_))
443     {
444       ties_.push (info.grob_);
445     }
446   else if (Arpeggio::has_interface (info.grob_))
447     {
448       arpeggios_.push (info.grob_); 
449     }
450   
451 }
452
453 void
454 Accidental_engraver::process_music ()
455 {
456   SCM sig = get_property ("keySignature");
457
458   /* Detect key sig changes.
459      Update all parents and children
460   */
461   if (last_keysig_ != sig)
462     {
463       Translator_group * trans_ = daddy_trans_;
464       while (trans_)
465         {
466           trans_ -> set_property ("localKeySignature",  ly_deep_copy (sig));
467           trans_ = trans_->daddy_trans_;
468         }
469       set_property_on_children(daddy_trans_,"localKeySignature", sig);
470
471       last_keysig_ = sig;
472     }
473 }
474
475
476
477
478
479 ENTER_DESCRIPTION (Accidental_engraver,
480 "Make accidentals.  Catches note heads, ties and notices key-change
481 events.  Due to interaction with ties (which don't come together
482 with note heads), this needs to be in a context higher than Tie_engraver.",
483                    
484                "Accidental",
485                "rhythmic-head-interface tie-interface arpeggio-interface",
486                "localKeySignature extraNatural autoAccidentals autoCautionaries",
487                    "localKeySignature");