]> git.donarmstrong.com Git - lilypond.git/blob - lily/new-accidental-engraver.cc
''
[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 "local-key-item.hh"
11 #include "item.hh"
12 #include "tie.hh"
13 #include "rhythmic-head.hh"
14 #include "engraver-group-engraver.hh"
15 #include "accidental-placement.hh"
16 #include "side-position-interface.hh"
17 #include "engraver.hh"
18 #include "arpeggio.hh"
19 #include "warn.hh"
20
21 #include "translator-group.hh"
22
23 /**
24
25
26 FIXME: should not compute vertical positioning of accidentals, but
27 get them from the noteheads
28
29 The algorithm for accidentals should be documented, and made
30 tweakable.
31
32 */
33
34 struct Accidental_entry {
35   bool done_;
36   Note_req * melodic_;
37   Grob * accidental_;
38   Translator_group *origin_;
39   Grob*  head_;
40   Accidental_entry();
41 };
42
43 Accidental_entry::Accidental_entry()
44 {
45   done_ = false;
46   melodic_ =0;
47   accidental_ = 0;
48   origin_ = 0;
49   head_ = 0;
50 }
51
52 struct New_accidental_engraver : Engraver {
53 protected:
54   TRANSLATOR_DECLARATIONS (New_accidental_engraver);
55   virtual void process_music ();
56   virtual void acknowledge_grob (Grob_info);
57   virtual void stop_translation_timestep ();
58   virtual void initialize ();
59   virtual void create_grobs ();
60   virtual void finalize ();
61 public:
62
63   /*
64     TODO -> property.
65     
66     This is not a property, and it is not protected.  This poses a
67     very small risk of the value being GC'd from under us.
68
69     Oh well.
70   */
71   SCM last_keysig_;
72
73   /*
74     Urgh. Since the accidentals depend on lots of variables, we have to
75     store all information before we can really create the accidentals.
76   */
77   Link_array<Grob> arpeggios_;
78
79   Grob * accidental_placement_;
80   
81
82   /*
83     The next 
84    */
85   Array<Accidental_entry> accidental_arr_;
86   
87   Link_array<Grob> tie_arr_;
88
89
90 };
91
92
93 New_accidental_engraver::New_accidental_engraver ()
94 {
95   accidental_placement_ = 0;
96   last_keysig_ = SCM_EOL;
97 }
98
99 void
100 New_accidental_engraver::initialize ()
101 {
102   last_keysig_ = get_property ("keySignature");
103
104   Translator_group * trans_ = daddy_trans_l_;
105   while (trans_)
106     {
107       trans_ -> set_property ("localKeySignature",  ly_deep_copy (last_keysig_));
108       trans_ = trans_->daddy_trans_l_;
109     }
110   daddy_trans_l_->set_children_property ("localKeySignature", last_keysig_);
111 }
112
113 /*
114
115 calculates the number of accidentals on basis of the current local key sig
116   (passed as argument)
117   Returns number of accidentals (0, 1 or 2).
118     Negative (-1 or -2) if accidental has changed.
119
120 */
121 static int
122 number_accidentals (SCM sig, Note_req * note_l, Pitch *pitch, SCM curbarnum, SCM lazyness, 
123                     bool ignore_octave_b)
124 {
125   int n = pitch->notename_i_;
126   int o = pitch->octave_i_;
127   int a = pitch->alteration_i_;
128   int curbarnum_i = gh_scm2int (curbarnum);
129   int accbarnum_i = 0;
130
131   SCM prev;
132   if (ignore_octave_b)
133     prev = ly_assoc_cdr (gh_int2scm (n), sig);
134   else
135     prev = gh_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), sig);
136
137   /* should really be true unless prev == SCM_BOOL_F */
138   if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
139     {
140       accbarnum_i = gh_scm2int (ly_cddr (prev));
141       prev = gh_cons (ly_car (prev), ly_cadr (prev));
142     }
143   
144   /* If an accidental was not found or the accidental was too old */
145   if (prev == SCM_BOOL_F ||
146       (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
147     prev = gh_assoc (gh_int2scm (n), sig);
148
149
150   SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
151
152   int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
153
154   int num;
155   if (a == p
156       && !to_boolean (note_l->get_mus_property ("force-accidental"))
157       && 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 (Note_req * note_l, Pitch *pitch, Translator_group * origin_l, 
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)).ch_C ()));
177   
178   while (gh_pair_p (accidentals) && origin_l)
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_l->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_l, 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).ch_C ()));
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_l && !origin_l->is_alias_b (context))
213             origin_l = origin_l->daddy_trans_l_;
214       
215           if (!origin_l)
216             warning (_f ("Symbol is not a parent context: %s. Ignored", 
217                          context.ch_C ()));
218         }
219       else warning (_f ("Accidental typesetting must be pair or context-name: %s", 
220                         ly_scm2string (ly_car (accidentals)).ch_C ()));
221       
222       accidentals = ly_cdr (accidentals);
223     }
224   return diff ? -number : number;
225 }
226
227 void
228 New_accidental_engraver::create_grobs ()
229 {
230   if (accidental_arr_.size () && !accidental_arr_.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
237       bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
238       for (int i = 0; i  < accidental_arr_.size (); i++) 
239         {
240           if (accidental_arr_[i].done_ )
241             continue;
242           accidental_arr_[i].done_  = true;
243           Grob * support_l = accidental_arr_[i].head_;
244           Note_req * note_l = accidental_arr_[i].melodic_;
245           Translator_group * origin_l = accidental_arr_[i].origin_;
246
247           Pitch * pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
248           int num = number_accidentals (note_l, pitch, origin_l, accidentals, barnum);
249           int num_caut = number_accidentals (note_l, pitch, origin_l, cautionaries, barnum);
250           bool cautionary = to_boolean (note_l->get_mus_property ("cautionary"));
251           
252           if (abs (num_caut) > abs (num))
253             {
254               num = num_caut;
255               cautionary = true;
256             }
257           
258           bool different = num < 0;
259           num = abs (num);
260
261           /* see if there's a tie that "changes" the accidental */
262           /* works because if there's a tie, the note to the left
263              is of the same pitch as the actual note */
264
265           Grob *tie_break_reminder = 0;
266           bool tie_changes = false;
267           for (int j = 0; j < tie_arr_.size (); j++)
268             if (support_l == Tie::head (tie_arr_[j], RIGHT))
269               {
270                 tie_changes = different;
271
272                 /* Enable accidentals for broken tie
273
274                 We only want an accidental on a broken tie, 
275                 if the tie changes the accidental.
276                    
277                 Maybe check property noTieBreakForceAccidental? */
278                 if (different)
279                   tie_break_reminder = tie_arr_[j];
280                 break;
281               }
282
283           if (num)
284             {
285               Grob * a = new Item (get_property ("Accidental"));
286               a->set_parent (support_l, Y_AXIS);
287
288               if (!accidental_placement_)
289                 {
290                   accidental_placement_ = new Item (get_property ("AccidentalPlacement"));
291                   announce_grob (accidental_placement_, a->self_scm());
292                 }
293               
294               Accidental_placement::add_accidental (accidental_placement_, a);
295               announce_grob (a, SCM_EOL);
296
297               
298               SCM accs = gh_cons (gh_int2scm (pitch->alteration_i_), SCM_EOL);
299               if (num == 2 && extra_natural_b)
300                 accs = gh_cons (gh_int2scm (0), accs);
301
302               /* TODO:
303
304               add cautionary option in accidental.
305                */
306               
307               if (tie_break_reminder)
308                 ;               // TODO.
309               
310               support_l->set_grob_property ("accidental-grob", a->self_scm ());
311
312               a->set_grob_property ("accidentals", accs);
313               accidental_arr_[i].accidental_ = a;
314  /*
315         We add the accidentals to the support of the arpeggio, so it is put left of the
316         accidentals. 
317         
318       */
319               for (int i = 0;  i < arpeggios_.size ();  i++)
320                 Side_position_interface::add_support (arpeggios_[i], a);
321             }
322           
323
324           /*
325             We should not record the accidental if it is the first
326             note and it is tied from the previous measure.
327
328             Checking whether it is tied also works mostly, but will it
329             always do the correct thing?
330           */
331           
332
333           int n = pitch->notename_i_;
334           int o = pitch->octave_i_;
335           int a = pitch->alteration_i_;
336           SCM on_s = gh_cons (gh_int2scm (o), gh_int2scm (n));
337
338           /*
339             TODO: Speed this up!
340             
341             Perhaps only check translators mentioned in the auto-accidentals?
342             -rz
343
344             TODO: profile this.
345             
346             I'd be surprised if the impact of this would be
347             measurable.  Anyway, it seems localsig doesn't change
348             every time-step, but a set_property() is done every
349             time. We could save on that, probably.
350
351             --hwn.
352             
353             
354           */
355
356           while (origin_l)
357             {
358               SCM localsig = origin_l->get_property ("localKeySignature");
359               if (tie_changes)
360                 {
361                   /*
362                     Remember an alteration that is different both from
363                     that of the tied note and of the key signature.
364                   */
365                   localsig = ly_assoc_front_x
366                     (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
367                 }
368               else
369                 {
370                   /*
371                     not really really correct if there are more than one
372                     noteheads with the same notename.
373                   */
374                   localsig = ly_assoc_front_x
375                     (localsig, on_s, gh_cons (gh_int2scm (a), barnum)); 
376                 }
377               origin_l->set_property ("localKeySignature",  localsig);
378               origin_l = origin_l->daddy_trans_l_;
379             }
380         }
381     }
382 }
383
384 void
385 New_accidental_engraver::finalize ()
386 {
387
388 }
389
390 void
391 New_accidental_engraver::stop_translation_timestep ()
392 {
393   for (int i = 0; i < accidental_arr_.size(); i++)
394     {
395       Grob *a = accidental_arr_[i].accidental_;
396       if (a)
397         {
398           typeset_grob (a);
399         }
400     }
401
402   if (accidental_placement_)
403     typeset_grob(accidental_placement_);
404   accidental_placement_ = 00;
405   
406   accidental_arr_.clear();
407   arpeggios_.clear ();
408   tie_arr_.clear ();
409 }
410
411 void
412 New_accidental_engraver::acknowledge_grob (Grob_info info)
413 {
414   Note_req * note_l =  dynamic_cast <Note_req *> (info.music_cause ());
415
416   if (note_l && Rhythmic_head::has_interface (info.grob_l_))
417     {
418       Accidental_entry entry ;
419       entry.head_ = info.grob_l_;
420       entry.origin_ = info.origin_trans_l_->daddy_trans_l_;
421       entry.melodic_ = note_l;
422
423       accidental_arr_.push (entry);
424     }
425   else if (Tie::has_interface (info.grob_l_))
426     {
427       tie_arr_.push (info.grob_l_);
428     }
429   else if (Arpeggio::has_interface (info.grob_l_))
430     {
431       arpeggios_.push (info.grob_l_); 
432     }
433   
434 }
435
436 void
437 New_accidental_engraver::process_music ()
438 {
439   SCM sig = get_property ("keySignature");
440
441   /* Detect key sig changes.
442      Update all parents and children
443   */
444   if (last_keysig_ != sig)
445     {
446       Translator_group * trans_ = daddy_trans_l_;
447       while (trans_)
448         {
449           trans_ -> set_property ("localKeySignature",  ly_deep_copy (sig));
450           trans_ = trans_->daddy_trans_l_;
451         }
452       daddy_trans_l_->set_children_property ("localKeySignature", sig);
453
454       last_keysig_ = sig;
455     }
456 }
457
458
459
460
461
462 ENTER_DESCRIPTION (New_accidental_engraver,
463 "Make accidentals.  Catches note heads, ties and notices key-change
464 events.  Due to interaction with ties (which don't come together
465 with note heads), this needs to be in a context higher than Tie_engraver.",
466                    
467                "Accidental",
468                "rhythmic-head-interface tie-interface arpeggio-interface",
469                "localKeySignature extraNatural autoAccidentals autoCautionaries",
470                    "localKeySignature");