]> git.donarmstrong.com Git - lilypond.git/blob - lily/local-key-engraver.cc
release: 1.3.122
[lilypond.git] / lily / local-key-engraver.cc
1 /*
2   local-key-engraver.cc -- implement Local_key_engraver
3
4   (c)  1997--2000 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 #include "grace-align-item.hh"
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 */
28
29
30 struct Local_key_engraver : Engraver {
31   Item *key_item_p_;
32 protected:
33   VIRTUAL_COPY_CONS(Translator);
34   virtual void process_music();
35   virtual void acknowledge_grob (Grob_info);
36   virtual void stop_translation_timestep();
37   virtual void initialize ();
38   virtual void create_grobs ();
39   virtual void finalize ();
40 public:
41
42   // todo -> property
43   SCM last_keysig_;
44
45   /*
46     Urgh. Since the accidentals depend on lots of variables, we have to
47     store all information before we can really create the accidentals.
48    */
49   Link_array<Grob> arpeggios_;
50   
51   Link_array<Note_req> mel_l_arr_;
52   Link_array<Grob> support_l_arr_;
53   Link_array<Item> forced_l_arr_;
54   Link_array<Grob> tied_l_arr_;
55   Local_key_engraver();
56
57   Item * grace_align_l_;
58 };
59
60 Local_key_engraver::Local_key_engraver()
61 {
62   key_item_p_ =0;
63   grace_align_l_ =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) : gh_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           bool tie_changes = tied_l_arr_.find_l (support_l) && different;
102           if ((to_boolean (note_l->get_mus_property ("force-accidental"))
103               || different) && !tie_changes)
104             {
105               if (!key_item_p_) 
106                 {
107                   key_item_p_ = new Item(get_property ("Accidentals"));
108                   Local_key_item::set_interface (key_item_p_);
109
110
111                   Staff_symbol_referencer::set_interface (key_item_p_);
112                          
113                   announce_grob (key_item_p_, 0);
114                 }
115
116               
117               bool extra_natural =
118                 sign (p) * (p - a) == 1
119                 && abs(p) == 2;
120
121               Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
122                                          to_boolean (note_l->get_mus_property ("cautionary")),
123                                          extra_natural);
124               Side_position::add_support (key_item_p_,support_l);
125             }
126           
127           /*
128             We should not record the accidental if it is the first
129             note and it is tied from the previous measure.
130
131             Checking whether it is tied also works mostly, but will it
132             always do the correct thing?
133
134            */
135           bool forget = to_boolean (get_property ("forgetAccidentals"));
136           if (tie_changes)
137             {
138               /*
139                 Remember an alteration that is different both from
140                 that of the tied note and of the key signature.
141                */
142               localsig = scm_assoc_set_x (localsig, gh_cons (gh_int2scm (o),
143                                                              gh_int2scm (n)),
144                                           SCM_BOOL_T); 
145
146             }
147           else if (!forget)
148             {
149               /*
150                 not really really correct if there are more than one
151                 noteheads with the same notename.
152                */
153               localsig = scm_assoc_set_x (localsig, gh_cons (gh_int2scm (o),
154                                                              gh_int2scm (n)),
155                                           gh_int2scm (a)); 
156
157             }
158         }
159
160
161   
162   
163       daddy_trans_l_->set_property ("localKeySignature",  localsig);
164     }
165   
166   if (key_item_p_ && grace_align_l_)
167     {
168       Side_position::add_support (grace_align_l_,key_item_p_);
169       grace_align_l_ =0;
170     }
171
172   if (key_item_p_)
173     {
174       /*
175         Hmm. Which one has to be on the left?
176
177         On which left, code or paper?
178
179         (Arpeggios are engraved left of accidentals, of course.)
180        */
181       for (int i=0;  i < arpeggios_.size ();  i++)
182         Side_position::add_support (arpeggios_[i], key_item_p_);
183
184       arpeggios_.clear ();
185     }
186 }
187
188 void
189 Local_key_engraver::finalize ()
190 {
191   // TODO: if grace ? signal accidentals to Local_key_engraver the 
192 }
193
194 void
195 Local_key_engraver::stop_translation_timestep()
196 {
197   if (key_item_p_)
198     {
199       for (int i=0; i < support_l_arr_.size(); i++)
200         Side_position::add_support (key_item_p_,support_l_arr_[i]);
201
202       typeset_grob (key_item_p_);
203       key_item_p_ =0;
204     }
205
206   grace_align_l_ = 0;
207   mel_l_arr_.clear();
208   arpeggios_.clear ();
209   tied_l_arr_.clear();
210   support_l_arr_.clear();
211   forced_l_arr_.clear();        
212 }
213
214 void
215 Local_key_engraver::acknowledge_grob (Grob_info info)
216 {
217   SCM wg= get_property ("weAreGraceContext");
218   
219   bool selfgr = gh_boolean_p (wg) &&gh_scm2bool (wg);
220   bool he_gr = to_boolean (info.elem_l_->get_grob_property ("grace"));
221
222   Item * item = dynamic_cast<Item*> (info.elem_l_);  
223   if (he_gr && !selfgr && item && Grace_align_item::has_interface (item))
224     {
225       grace_align_l_ = item;
226     }
227   if (he_gr != selfgr)
228     return;
229   
230   Note_req * note_l =  dynamic_cast <Note_req *> (info.req_l_);
231
232   if (note_l && Rhythmic_head::has_interface (info.elem_l_))
233     {
234       mel_l_arr_.push (note_l);
235       support_l_arr_.push (info.elem_l_);
236     }
237   else if (Tie::has_interface (info.elem_l_))
238     {
239       tied_l_arr_.push (Tie::head (info.elem_l_, RIGHT));
240     }
241   else if (Arpeggio::has_interface (info.elem_l_))
242     {
243       arpeggios_.push (info.elem_l_); 
244     }
245   
246 }
247
248 /*
249   ugh. repeated deep_copy generates lots of garbage.
250  */
251 void
252 Local_key_engraver::process_music()
253 {
254   SCM smp = get_property ("measurePosition");
255   Moment mp =  (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
256
257   SCM sig = get_property ("keySignature");
258
259   /*
260     Detect key sig changes. If we haven't found any, check if at start
261     of measure, and set localKeySignature anyhow.  */
262   if (last_keysig_ != sig) 
263     {
264       daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
265       last_keysig_ = sig;
266     }
267   else if (!mp)
268     {
269       if (!to_boolean (get_property ("noResetKey")))
270         daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
271     }
272 }
273
274
275
276 ADD_THIS_TRANSLATOR(Local_key_engraver);
277