]> git.donarmstrong.com Git - lilypond.git/blob - lily/local-key-engraver.cc
release: 1.5.13
[lilypond.git] / lily / local-key-engraver.cc
1 /*
2   local-key-engraver.cc -- implement Local_key_engraver
3
4   (c)  1997--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 */
6
7 #include "musical-request.hh"
8 #include "command-request.hh"
9 #include "local-key-item.hh"
10 #include "item.hh"
11 #include "tie.hh"
12 #include "rhythmic-head.hh"
13 #include "timing-translator.hh"
14 #include "engraver-group-engraver.hh"
15
16 #include "staff-symbol-referencer.hh"
17 #include "side-position-interface.hh"
18 #include "engraver.hh"
19 #include "arpeggio.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
33 struct Local_key_engraver : Engraver {
34   Item *key_item_p_;
35 protected:
36   TRANSLATOR_DECLARATIONS(Local_key_engraver);
37   virtual void process_music ();
38   virtual void acknowledge_grob (Grob_info);
39   virtual void stop_translation_timestep ();
40   virtual void initialize ();
41   virtual void create_grobs ();
42   virtual void finalize ();
43 public:
44
45   // todo -> property
46   SCM last_keysig_;
47
48   /*
49     Urgh. Since the accidentals depend on lots of variables, we have to
50     store all information before we can really create the accidentals.
51    */
52   Link_array<Grob> arpeggios_;
53   
54   Link_array<Note_req> mel_l_arr_;
55   Link_array<Grob> support_l_arr_;
56   Link_array<Item> forced_l_arr_;
57   Link_array<Grob> tie_l_arr_;
58
59 };
60
61 Local_key_engraver::Local_key_engraver ()
62 {
63   key_item_p_ =0;
64   last_keysig_ = SCM_EOL;
65 }
66
67 void
68 Local_key_engraver::initialize ()
69 {
70   last_keysig_ = get_property ("keySignature");
71   daddy_trans_l_->set_property ("localKeySignature",  last_keysig_);  
72 }
73
74 void
75 Local_key_engraver::create_grobs ()
76 {
77   if (!key_item_p_ && mel_l_arr_.size ()) 
78     {
79       SCM localsig = get_property ("localKeySignature");
80   
81       for (int i=0; i  < mel_l_arr_.size (); i++) 
82         {
83           Grob * support_l = support_l_arr_[i];
84           Note_req * note_l = mel_l_arr_[i];
85
86           int n = unsmob_pitch (note_l->get_mus_property ("pitch"))->notename_i_;
87           int o = unsmob_pitch (note_l->get_mus_property ("pitch"))->octave_i () ;
88           int a = unsmob_pitch (note_l->get_mus_property ("pitch"))->alteration_i_;
89           
90           /* see if there's a tie that "changes" the accidental */
91           /* works because if there's a tie, the note to the left
92              is of the same pitch as the actual note */
93
94           SCM prev = scm_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), localsig);
95           if (prev == SCM_BOOL_F)
96             prev = scm_assoc (gh_int2scm (n), localsig);
97           SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
98           bool different = !gh_equal_p (prev_acc , gh_int2scm (a));
99           int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
100
101           Grob *tie_break_reminder = 0;
102           bool tie_changes = false;
103           for (int i=0; i < tie_l_arr_.size (); i++)
104             if (support_l == Tie::head (tie_l_arr_[i], RIGHT))
105               {
106                 tie_changes = different;
107                 /* Enable accidentals for broken tie
108
109                    We only want an accidental on a broken tie,
110                    if the tie changes the accidental.
111                    
112                    Maybe check property noTieBreakForceAccidental? */
113                 if (different)
114                   tie_break_reminder = tie_l_arr_[i];
115                 break;
116               }
117
118           /* When do we want accidentals:
119
120              1. when property force-accidental is set, and not
121              tie_changes
122              2. when different and not tie-changes
123              3. maybe when at end of a tie: we must later see if
124              we're after a line break */
125           if (( (to_boolean (note_l->get_mus_property ("force-accidental"))
126                 || different)
127                && !tie_changes)
128               || tie_break_reminder)
129             {
130               if (!key_item_p_) 
131                 {
132                   key_item_p_ = new Item (get_property ("Accidentals"));
133                   Local_key_item::set_interface (key_item_p_);
134
135                   
136                   Staff_symbol_referencer::set_interface (key_item_p_);
137                   SCM c0 = get_property ("centralCPosition");
138                   if (gh_number_p (c0))
139                     Staff_symbol_referencer::set_position (key_item_p_, gh_scm2int (c0));
140                          
141                   announce_grob (key_item_p_, 0);
142                 }
143
144               
145               bool extra_natural =
146                 sign (p) * (p - a) == 1
147                 && abs (p) == 2;
148
149               Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
150                                          to_boolean (note_l->get_mus_property ("cautionary")),
151                                          extra_natural,
152                                          tie_break_reminder);
153               Side_position_interface::add_support (key_item_p_,support_l);
154             }
155           
156           /*
157             We should not record the accidental if it is the first
158             note and it is tied from the previous measure.
159
160             Checking whether it is tied also works mostly, but will it
161             always do the correct thing?
162
163            */
164           bool forget = to_boolean (get_property ("forgetAccidentals"));
165           if (tie_changes)
166             {
167               /*
168                 Remember an alteration that is different both from
169                 that of the tied note and of the key signature.
170
171                */
172               localsig = scm_assoc_set_x (localsig, gh_cons (gh_int2scm (o),
173                                                              gh_int2scm (n)),
174                                           SCM_BOOL_T); 
175
176             }
177           else if (!forget)
178             {
179               /*
180                 not really really correct if there are more than one
181                 noteheads with the same notename.
182                */
183               localsig = scm_assoc_set_x (localsig, gh_cons (gh_int2scm (o),
184                                                              gh_int2scm (n)),
185                                           gh_int2scm (a)); 
186
187             }
188         }
189
190
191   
192   
193       daddy_trans_l_->set_property ("localKeySignature",  localsig);
194     }
195   
196
197   if (key_item_p_)
198     {
199       /*
200         Hmm. Which one has to be on the left?
201
202         On which left, code or paper?
203
204  (Arpeggios are engraved left of accidentals, of course.)
205        */
206       for (int i=0;  i < arpeggios_.size ();  i++)
207         Side_position_interface::add_support (arpeggios_[i], key_item_p_);
208
209       arpeggios_.clear ();
210     }
211 }
212
213 void
214 Local_key_engraver::finalize ()
215 {
216
217 }
218
219 void
220 Local_key_engraver::stop_translation_timestep ()
221 {
222   if (key_item_p_)
223     {
224       for (int i=0; i < support_l_arr_.size (); i++)
225         Side_position_interface::add_support (key_item_p_,support_l_arr_[i]);
226
227       typeset_grob (key_item_p_);
228       key_item_p_ =0;
229     }
230
231
232   mel_l_arr_.clear ();
233   arpeggios_.clear ();
234   tie_l_arr_.clear ();
235   support_l_arr_.clear ();
236   forced_l_arr_.clear ();       
237 }
238
239 void
240 Local_key_engraver::acknowledge_grob (Grob_info info)
241 {
242   Note_req * note_l =  dynamic_cast <Note_req *> (info.req_l_);
243
244   if (note_l && Rhythmic_head::has_interface (info.grob_l_))
245     {
246       mel_l_arr_.push (note_l);
247       support_l_arr_.push (info.grob_l_);
248     }
249   else if (Tie::has_interface (info.grob_l_))
250     {
251       tie_l_arr_.push (info.grob_l_);
252     }
253   else if (Arpeggio::has_interface (info.grob_l_))
254     {
255       arpeggios_.push (info.grob_l_); 
256     }
257   
258 }
259
260 /*
261   ugh. repeated deep_copy generates lots of garbage.
262  */
263 void
264 Local_key_engraver::process_music ()
265 {
266   SCM smp = get_property ("measurePosition");
267   Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
268
269   SCM sig = get_property ("keySignature");
270
271   /*
272     Detect key sig changes. If we haven't found any, check if at start
273     of measure, and set localKeySignature anyhow.  */
274   if (last_keysig_ != sig) 
275     {
276       daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
277       last_keysig_ = sig;
278     }
279   else if (!mp.to_bool () )
280     {
281       if (!to_boolean (get_property ("noResetKey")))
282         daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
283     }
284 }
285
286
287
288
289
290 ENTER_DESCRIPTION(Local_key_engraver,
291 /* descr */       "Make accidentals.  Catches note heads, ties and notices key-change
292 events.  Due to interaction with ties (which don't come together
293 with note heads), this needs to be in a context higher than Tie_engraver. FIXME",
294 /* creats*/       "Accidentals",
295 /* acks  */       "rhythmic-head-interface tie-interface arpeggio-interface",
296 /* reads */       "localKeySignature forgetAccidentals noResetKey",
297 /* write */       "");