]> git.donarmstrong.com Git - lilypond.git/blob - lily/local-key-engraver.cc
release: 1.3.86
[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
20
21 /**
22    Make accidentals.  Catches note heads, ties and notices key-change
23    events.  Due to interaction with ties (which don't come together
24    with note heads), this needs to be in a context higher than Tie_engraver.
25    (FIXME).
26
27    FIXME: should not compute vertical positioning of accidentals, but
28    get them from the noteheads
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 do_process_music();
38   virtual void acknowledge_element (Score_element_info);
39   virtual void do_pre_move_processing();
40   virtual void do_creation_processing ();
41   virtual void process_acknowledged ();
42   virtual void do_removal_processing ();
43 public:
44
45   // todo -> property
46   SCM last_keysig_;
47   
48   Link_array<Note_req> mel_l_arr_;
49   Link_array<Score_element> support_l_arr_;
50   Link_array<Item> forced_l_arr_;
51   Link_array<Score_element> tied_l_arr_;
52   Local_key_engraver();
53
54   Item * grace_align_l_;
55 };
56
57 Local_key_engraver::Local_key_engraver()
58 {
59   key_item_p_ =0;
60   grace_align_l_ =0;
61   last_keysig_ = SCM_EOL;
62 }
63
64 void
65 Local_key_engraver::do_creation_processing ()
66 {
67   last_keysig_ = get_property ("keySignature");
68   daddy_trans_l_->set_property ("localKeySignature",  last_keysig_);  
69 }
70
71 void
72 Local_key_engraver::process_acknowledged ()
73 {
74   if (!key_item_p_ && mel_l_arr_.size()) 
75     {
76       SCM localsig = get_property ("localKeySignature");
77   
78       SCM f = get_property ("forgetAccidentals");
79       bool forget = to_boolean (f);
80       for (int i=0; i  < mel_l_arr_.size(); i++) 
81         {
82           Score_element * support_l = support_l_arr_[i];
83           Note_req * note_l = mel_l_arr_[i];
84
85           int n = note_l->pitch_.notename_i_;
86           int o = note_l->pitch_.octave_i_;
87           int a = note_l->pitch_.accidental_i_;
88           
89           /* see if there's a tie that "changes" the accidental */
90           /* works because if there's a tie, the note to the left
91              is of the same pitch as the actual note */
92
93           SCM prev = scm_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), localsig);
94           if (prev == SCM_BOOL_F)
95             prev = scm_assoc (gh_int2scm (n), localsig);
96           int prev_acc = (prev == SCM_BOOL_F) ? 0 : gh_scm2int (gh_cdr (prev));
97           bool different = prev_acc != a;
98           
99           bool tie_changes = tied_l_arr_.find_l (support_l) && different;
100           if (!forget
101               && (note_l->forceacc_b_ || different)
102               && !tie_changes)
103             {
104               if (!key_item_p_) 
105                 {
106                   key_item_p_ = new Item(get_property ("basicLocalKeyProperties"));
107                   Local_key_item::set_interface (key_item_p_);
108                   Side_position::set_axis (key_item_p_, X_AXIS);
109                   Side_position::set_direction (key_item_p_, LEFT);
110                   Staff_symbol_referencer::set_interface (key_item_p_);
111                          
112                   announce_element (key_item_p_, 0);
113                 }
114
115               
116               bool extra_natural =
117                 sign (prev_acc) * (prev_acc - a) == 1
118                 && abs(prev_acc) == 2;
119
120               Local_key_item::add_pitch (key_item_p_, note_l->pitch_,
121                                          note_l->cautionary_b_,
122                                          extra_natural);
123               Side_position::add_support (key_item_p_,support_l);
124             }
125           
126           /*
127             We should not record the accidental if it is the first
128             note and it is tied from the previous measure.
129
130             Checking whether it is tied also works mostly, but will it
131             always do the correct thing?
132
133            */
134           if (!forget && !tie_changes)
135             {
136               /*
137                 not really really correct if there are more than one
138                 noteheads with the same notename.
139                */
140               localsig = scm_assoc_set_x (localsig, gh_cons (gh_int2scm (o),
141                                                              gh_int2scm (n)),
142                                           gh_int2scm (a)); 
143
144             }
145         }
146
147
148   
149   
150       daddy_trans_l_->set_property ("localKeySignature",  localsig);
151     }
152   
153   if (key_item_p_ && grace_align_l_)
154     {
155       Side_position::add_support (grace_align_l_,key_item_p_);
156       grace_align_l_ =0;
157     }
158   
159 }
160
161 void
162 Local_key_engraver::do_removal_processing ()
163 {
164   // TODO: if grace ? signal accidentals to Local_key_engraver the 
165 }
166
167 void
168 Local_key_engraver::do_pre_move_processing()
169 {
170   if (key_item_p_)
171     {
172       for (int i=0; i < support_l_arr_.size(); i++)
173         Side_position::add_support (key_item_p_,support_l_arr_[i]);
174
175       typeset_element (key_item_p_);
176       key_item_p_ =0;
177     }
178
179   grace_align_l_ = 0;
180   mel_l_arr_.clear();
181   tied_l_arr_.clear();
182   support_l_arr_.clear();
183   forced_l_arr_.clear();        
184 }
185
186 void
187 Local_key_engraver::acknowledge_element (Score_element_info info)
188 {
189   SCM wg= get_property ("weAreGraceContext");
190   
191   bool selfgr = gh_boolean_p (wg) &&gh_scm2bool (wg);
192   bool he_gr = to_boolean (info.elem_l_->get_elt_property ("grace"));
193
194   Item * item = dynamic_cast<Item*> (info.elem_l_);  
195   if (he_gr && !selfgr && item && Grace_align_item::has_interface (item))
196     {
197       grace_align_l_ = item;
198     }
199   if (he_gr != selfgr)
200     return;
201   
202   Note_req * note_l =  dynamic_cast <Note_req *> (info.req_l_);
203
204   if (note_l && Rhythmic_head::has_interface (info.elem_l_))
205     {
206       mel_l_arr_.push (note_l);
207       support_l_arr_.push (info.elem_l_);
208     }
209   else if (Tie::has_interface (info.elem_l_))
210     {
211       tied_l_arr_.push (Tie::head (info.elem_l_, RIGHT));
212     }
213 }
214
215 /*
216   ugh. repeated deep_copy generates lots of garbage.
217  */
218 void
219 Local_key_engraver::do_process_music()
220 {
221   SCM smp = get_property ("measurePosition");
222   Moment mp =  (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
223
224   SCM sig = get_property ("keySignature");
225
226   /*
227     Detect key sig changes. If we haven't found any, check if at start
228     of measure, and set localKeySignature anyhow.  */
229   if (last_keysig_ != sig) 
230     {
231       daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
232       last_keysig_ = sig;
233     }
234   else if (!mp)
235     {
236       if (!to_boolean (get_property ("noResetKey")))
237         daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
238     }
239 }
240
241
242
243 ADD_THIS_TRANSLATOR(Local_key_engraver);
244