]> git.donarmstrong.com Git - lilypond.git/blob - lily/new-accidental-engraver.cc
d192b230f9ee09bb47eaddff22c66cd14cee746d
[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                 {
309                   // TODO.
310                   a->set_grob_property ("tie", tie_break_reminder->self_scm());
311                 }
312               
313               
314               support_l->set_grob_property ("accidental-grob", a->self_scm ());
315
316               a->set_grob_property ("accidentals", accs);
317               accidental_arr_[i].accidental_ = a;
318  /*
319         We add the accidentals to the support of the arpeggio, so it is put left of the
320         accidentals. 
321         
322       */
323               for (int i = 0;  i < arpeggios_.size ();  i++)
324                 Side_position_interface::add_support (arpeggios_[i], a);
325             }
326           
327
328           /*
329             We should not record the accidental if it is the first
330             note and it is tied from the previous measure.
331
332             Checking whether it is tied also works mostly, but will it
333             always do the correct thing?
334           */
335           
336
337           int n = pitch->notename_i_;
338           int o = pitch->octave_i_;
339           int a = pitch->alteration_i_;
340           SCM on_s = gh_cons (gh_int2scm (o), gh_int2scm (n));
341
342           /*
343             TODO: Speed this up!
344             
345             Perhaps only check translators mentioned in the auto-accidentals?
346             -rz
347
348             TODO: profile this.
349             
350             I'd be surprised if the impact of this would be
351             measurable.  Anyway, it seems localsig doesn't change
352             every time-step, but a set_property() is done every
353             time. We could save on that, probably.
354
355             --hwn.
356             
357             
358           */
359
360           while (origin_l)
361             {
362               SCM localsig = origin_l->get_property ("localKeySignature");
363               if (tie_changes)
364                 {
365                   /*
366                     Remember an alteration that is different both from
367                     that of the tied note and of the key signature.
368                   */
369                   localsig = ly_assoc_front_x
370                     (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
371                 }
372               else
373                 {
374                   /*
375                     not really really correct if there are more than one
376                     noteheads with the same notename.
377                   */
378                   localsig = ly_assoc_front_x
379                     (localsig, on_s, gh_cons (gh_int2scm (a), barnum)); 
380                 }
381               origin_l->set_property ("localKeySignature",  localsig);
382               origin_l = origin_l->daddy_trans_l_;
383             }
384         }
385     }
386 }
387
388 void
389 New_accidental_engraver::finalize ()
390 {
391
392 }
393
394 void
395 New_accidental_engraver::stop_translation_timestep ()
396 {
397   for (int i = 0; i < accidental_arr_.size(); i++)
398     {
399       Grob *a = accidental_arr_[i].accidental_;
400       if (a)
401         {
402           typeset_grob (a);
403         }
404     }
405
406   if (accidental_placement_)
407     typeset_grob(accidental_placement_);
408   accidental_placement_ = 00;
409   
410   accidental_arr_.clear();
411   arpeggios_.clear ();
412   tie_arr_.clear ();
413 }
414
415 void
416 New_accidental_engraver::acknowledge_grob (Grob_info info)
417 {
418   Note_req * note_l =  dynamic_cast <Note_req *> (info.music_cause ());
419
420   if (note_l && Rhythmic_head::has_interface (info.grob_l_))
421     {
422       Accidental_entry entry ;
423       entry.head_ = info.grob_l_;
424       entry.origin_ = info.origin_trans_l_->daddy_trans_l_;
425       entry.melodic_ = note_l;
426
427       accidental_arr_.push (entry);
428     }
429   else if (Tie::has_interface (info.grob_l_))
430     {
431       tie_arr_.push (info.grob_l_);
432     }
433   else if (Arpeggio::has_interface (info.grob_l_))
434     {
435       arpeggios_.push (info.grob_l_); 
436     }
437   
438 }
439
440 void
441 New_accidental_engraver::process_music ()
442 {
443   SCM sig = get_property ("keySignature");
444
445   /* Detect key sig changes.
446      Update all parents and children
447   */
448   if (last_keysig_ != sig)
449     {
450       Translator_group * trans_ = daddy_trans_l_;
451       while (trans_)
452         {
453           trans_ -> set_property ("localKeySignature",  ly_deep_copy (sig));
454           trans_ = trans_->daddy_trans_l_;
455         }
456       daddy_trans_l_->set_children_property ("localKeySignature", sig);
457
458       last_keysig_ = sig;
459     }
460 }
461
462
463
464
465
466 ENTER_DESCRIPTION (New_accidental_engraver,
467 "Make accidentals.  Catches note heads, ties and notices key-change
468 events.  Due to interaction with ties (which don't come together
469 with note heads), this needs to be in a context higher than Tie_engraver.",
470                    
471                "Accidental",
472                "rhythmic-head-interface tie-interface arpeggio-interface",
473                "localKeySignature extraNatural autoAccidentals autoCautionaries",
474                    "localKeySignature");