]> git.donarmstrong.com Git - lilypond.git/blob - lily/local-key-engraver.cc
patch::: 1.5.11.jcn2
[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   VIRTUAL_COPY_CONS (Translator);
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   Local_key_engraver ();
59 };
60
61 Local_key_engraver::Local_key_engraver ()
62 {
63   key_item_p_ =0;
64
65   last_keysig_ = SCM_EOL;
66 }
67
68 void
69 Local_key_engraver::initialize ()
70 {
71   last_keysig_ = get_property ("keySignature");
72   daddy_trans_l_->set_property ("localKeySignature",  last_keysig_);  
73 }
74
75 void
76 Local_key_engraver::create_grobs ()
77 {
78   if (!key_item_p_ && mel_l_arr_.size ()) 
79     {
80       SCM localsig = get_property ("localKeySignature");
81   
82       for (int i=0; i  < mel_l_arr_.size (); i++) 
83         {
84           Grob * support_l = support_l_arr_[i];
85           Note_req * note_l = mel_l_arr_[i];
86
87           int n = unsmob_pitch (note_l->get_mus_property ("pitch"))->notename_i_;
88           int o = unsmob_pitch (note_l->get_mus_property ("pitch"))->octave_i () ;
89           int a = unsmob_pitch (note_l->get_mus_property ("pitch"))->alteration_i_;
90           
91           /* see if there's a tie that "changes" the accidental */
92           /* works because if there's a tie, the note to the left
93              is of the same pitch as the actual note */
94
95           SCM prev = scm_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), localsig);
96           if (prev == SCM_BOOL_F)
97             prev = scm_assoc (gh_int2scm (n), localsig);
98           SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
99           bool different = !gh_equal_p (prev_acc , gh_int2scm (a));
100           int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
101
102           Grob *tie_break_reminder = 0;
103           bool tie_changes = false;
104           for (int i=0; i < tie_l_arr_.size (); i++)
105             if (support_l == Tie::head (tie_l_arr_[i], RIGHT))
106               {
107                 tie_changes = different;
108                 /* Enable accidentals for broken tie
109
110                    We only want an accidental on a broken tie,
111                    if the tie changes the accidental.
112                    
113                    Maybe check property noTieBreakForceAccidental? */
114                 if (different)
115                   tie_break_reminder = tie_l_arr_[i];
116                 break;
117               }
118
119           /* When do we want accidentals:
120
121              1. when property force-accidental is set, and not
122              tie_changes
123              2. when different and not tie-changes
124              3. maybe when at end of a tie: we must later see if
125              we're after a line break */
126           if (( (to_boolean (note_l->get_mus_property ("force-accidental"))
127                 || different)
128                && !tie_changes)
129               || tie_break_reminder)
130             {
131               if (!key_item_p_) 
132                 {
133                   key_item_p_ = new Item (get_property ("Accidentals"));
134                   Local_key_item::set_interface (key_item_p_);
135
136                   
137                   Staff_symbol_referencer::set_interface (key_item_p_);
138                   SCM c0 = get_property ("centralCPosition");
139                   if (gh_number_p (c0))
140                     Staff_symbol_referencer::set_position (key_item_p_, gh_scm2int (c0));
141                          
142                   announce_grob (key_item_p_, 0);
143                 }
144
145               
146               bool extra_natural =
147                 sign (p) * (p - a) == 1
148                 && abs (p) == 2;
149
150               Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
151                                          to_boolean (note_l->get_mus_property ("cautionary")),
152                                          extra_natural,
153                                          tie_break_reminder);
154               Side_position_interface::add_support (key_item_p_,support_l);
155             }
156           
157           /*
158             We should not record the accidental if it is the first
159             note and it is tied from the previous measure.
160
161             Checking whether it is tied also works mostly, but will it
162             always do the correct thing?
163
164            */
165           bool forget = to_boolean (get_property ("forgetAccidentals"));
166           if (tie_changes)
167             {
168               /*
169                 Remember an alteration that is different both from
170                 that of the tied note and of the key signature.
171
172                */
173               localsig = scm_assoc_set_x (localsig, gh_cons (gh_int2scm (o),
174                                                              gh_int2scm (n)),
175                                           SCM_BOOL_T); 
176
177             }
178           else if (!forget)
179             {
180               /*
181                 not really really correct if there are more than one
182                 noteheads with the same notename.
183                */
184               localsig = scm_assoc_set_x (localsig, gh_cons (gh_int2scm (o),
185                                                              gh_int2scm (n)),
186                                           gh_int2scm (a)); 
187
188             }
189         }
190
191
192   
193   
194       daddy_trans_l_->set_property ("localKeySignature",  localsig);
195     }
196   
197
198   if (key_item_p_)
199     {
200       /*
201         Hmm. Which one has to be on the left?
202
203         On which left, code or paper?
204
205  (Arpeggios are engraved left of accidentals, of course.)
206        */
207       for (int i=0;  i < arpeggios_.size ();  i++)
208         Side_position_interface::add_support (arpeggios_[i], key_item_p_);
209
210       arpeggios_.clear ();
211     }
212 }
213
214 void
215 Local_key_engraver::finalize ()
216 {
217
218 }
219
220 void
221 Local_key_engraver::stop_translation_timestep ()
222 {
223   if (key_item_p_)
224     {
225       for (int i=0; i < support_l_arr_.size (); i++)
226         Side_position_interface::add_support (key_item_p_,support_l_arr_[i]);
227
228       typeset_grob (key_item_p_);
229       key_item_p_ =0;
230     }
231
232
233   mel_l_arr_.clear ();
234   arpeggios_.clear ();
235   tie_l_arr_.clear ();
236   support_l_arr_.clear ();
237   forced_l_arr_.clear ();       
238 }
239
240 void
241 Local_key_engraver::acknowledge_grob (Grob_info info)
242 {
243   Note_req * note_l =  dynamic_cast <Note_req *> (info.req_l_);
244
245   if (note_l && Rhythmic_head::has_interface (info.elem_l_))
246     {
247       mel_l_arr_.push (note_l);
248       support_l_arr_.push (info.elem_l_);
249     }
250   else if (Tie::has_interface (info.elem_l_))
251     {
252       tie_l_arr_.push (info.elem_l_);
253     }
254   else if (Arpeggio::has_interface (info.elem_l_))
255     {
256       arpeggios_.push (info.elem_l_); 
257     }
258   
259 }
260
261 /*
262   ugh. repeated deep_copy generates lots of garbage.
263  */
264 void
265 Local_key_engraver::process_music ()
266 {
267   SCM smp = get_property ("measurePosition");
268   Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
269
270   SCM sig = get_property ("keySignature");
271
272   /*
273     Detect key sig changes. If we haven't found any, check if at start
274     of measure, and set localKeySignature anyhow.  */
275   if (last_keysig_ != sig) 
276     {
277       daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
278       last_keysig_ = sig;
279     }
280   else if (!mp.to_bool () )
281     {
282       if (!to_boolean (get_property ("noResetKey")))
283         daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
284     }
285 }
286
287
288
289 ADD_THIS_TRANSLATOR (Local_key_engraver);
290