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