]> git.donarmstrong.com Git - lilypond.git/blob - lily/part-combine-music-iterator.cc
release: 1.4.15
[lilypond.git] / lily / part-combine-music-iterator.cc
1 /*   
2   part-combine-music-iterator.cc -- implement  Part_combine_music_iterator
3
4   source file of the GNU LilyPond music typesetter
5   
6   (c) 2000--2001 Jan Nieuwenhuizen <janneke@gnu.org>
7  */
8
9 #include "part-combine-music.hh"
10 #include "part-combine-music-iterator.hh"
11 #include "translator-group.hh"
12 #include "musical-request.hh"
13 #include "music-sequence.hh"
14 #include "lily-guile.hh"
15 #include "warn.hh"
16
17 Part_combine_music_iterator::Part_combine_music_iterator ()
18 {
19   first_iter_p_ = 0;
20   second_iter_p_ = 0;
21   first_until_ = 0;
22   second_until_ = 0;
23 }
24
25 Part_combine_music_iterator::~Part_combine_music_iterator ()
26 {
27   delete second_iter_p_;
28   delete first_iter_p_;
29 }
30
31 Part_combine_music_iterator::Part_combine_music_iterator (Part_combine_music_iterator const &src)
32   : Music_iterator (src)
33 {
34   second_iter_p_ = src.second_iter_p_ ? src.second_iter_p_->clone () : 0;
35   first_iter_p_ = src.first_iter_p_ ? src.first_iter_p_->clone () : 0;
36
37   first_until_ = src.first_until_;
38   second_until_ = src.second_until_;
39   state_ = src.state_;
40   suffix_ = src.suffix_;
41 }
42
43 Moment
44 Part_combine_music_iterator::pending_moment () const
45 {
46   Moment p;
47   p.set_infinite (1);
48   if (first_iter_p_->ok ())
49     p = p <? first_iter_p_->pending_moment ();
50
51   if (second_iter_p_->ok ())
52     p = p <? second_iter_p_->pending_moment ();
53   return p;
54 }
55
56 bool
57 Part_combine_music_iterator::ok () const
58 {
59   return first_iter_p_->ok () || second_iter_p_->ok ();
60 }
61
62 void
63 Part_combine_music_iterator::construct_children ()
64 {
65   Part_combine_music const * m = dynamic_cast<Part_combine_music const*> (music_l_);
66   
67   first_iter_p_ = get_iterator_p (m->first_l ());
68   second_iter_p_ = get_iterator_p (m->second_l ());
69 }
70
71 void
72 Part_combine_music_iterator::change_to (Music_iterator *it, String to_type,
73                                         String to_id)
74 {
75   Translator_group * current = it->report_to_l ();
76   Translator_group * last = 0;
77
78   /*
79     Cut & Paste from from Auto_change_iterator from Change_iterator (ugh).
80
81     TODO: abstract this function 
82    */
83   
84   /* find the type  of translator that we're changing.
85      
86      If \translator Staff = bass, then look for Staff = *
87    */
88   while (current && current->type_str_ != to_type)
89     {
90       last = current;
91       current = current->daddy_trans_l_;
92     }
93
94   if (current && current->id_str_ == to_id)
95     {
96       String msg;
97       msg += _ ("Can't switch translators, I'm there already");
98     }
99   
100   if (current) 
101     if (last)
102       {
103         Translator_group * dest = 
104           it->report_to_l ()->find_create_translator_l (to_type, to_id);
105         current->remove_translator_p (last);
106         dest->add_used_group_translator (last);
107       }
108     else
109       {
110         /*
111           We could change the current translator's id, but that would make 
112           errors hard to catch
113           
114            last->translator_id_str_  = change_l ()->change_to_id_str_;
115         */
116         error (_f ("I'm one myself: `%s'", to_type.ch_C ()));
117       }
118   else
119     error (_f ("none of these in my family: `%s'", to_id.ch_C ()));
120 }
121
122
123 // SCM*, moet / kan dat niet met set_x ofzo?
124 static void
125 get_music_info (Moment m, Music_iterator* iter, SCM *pitches, SCM *durations)
126 {
127   if (iter->ok ())
128     {
129       for (SCM i = iter->get_music (m); gh_pair_p (i); i = gh_cdr (i))
130         {
131           Music *m = unsmob_music (gh_car (i));
132           if (Melodic_req *r = dynamic_cast<Melodic_req *> (m))
133             *pitches = gh_cons (r->get_mus_property ("pitch"), *pitches);
134           if (Rhythmic_req *r = dynamic_cast<Rhythmic_req *> (m))
135             *durations = gh_cons (r->get_mus_property ("duration"), *durations);
136         }
137     }
138 }
139   
140 int
141 Part_combine_music_iterator::get_state (Moment)
142 {
143   int state = UNKNOWN;
144   Part_combine_music const *p = dynamic_cast<Part_combine_music const* > (music_l_);
145
146   String w = ly_scm2string (p->get_mus_property ("what"));
147     
148   
149   Translator_group *first_translator = first_iter_p_->report_to_l ()->find_create_translator_l (w, "one" + suffix_);
150
151   SCM s = first_translator->get_property (ly_symbol2scm ("changeMoment"));
152   if (!gh_pair_p (s))
153     return state;
154
155   Moment change_mom = *unsmob_moment (gh_car (s));
156   Moment diff_mom = *unsmob_moment (gh_cdr (s));
157   
158   Moment now = pending_moment ();
159
160   if (!now.mod_rat (change_mom))
161     {
162       SCM interval = SCM_BOOL_F;
163       if (first_until_ < now)
164         first_until_ = now;
165       if (second_until_ < now)
166         second_until_ = now;
167
168       Moment first_mom = first_until_;
169       Moment second_mom = second_until_;
170       Moment diff_until = diff_mom + now;
171
172       bool first = true;
173       Music_iterator *first_iter = first_iter_p_->clone ();
174       Music_iterator *second_iter = second_iter_p_->clone ();
175
176       Moment last_pending (-1);
177       Moment pending = now;
178       while (now < diff_until
179               && (first_iter->ok () || second_iter->ok ())
180
181              // urg, this is a hack, haven't caught this case yet
182              && (pending != last_pending))
183         {
184           if (!second_iter->ok ())
185             pending = first_iter->pending_moment ();
186           else if (!first_iter->ok ())
187             pending = second_iter->pending_moment ();
188           else
189             pending = first_iter->pending_moment () <? second_iter->pending_moment ();
190           last_pending = pending;
191
192           SCM first_pitches = SCM_EOL;
193           SCM first_durations = SCM_EOL;
194           get_music_info (pending, first_iter,
195                           &first_pitches, &first_durations);
196       
197           SCM second_pitches = SCM_EOL;
198           SCM second_durations = SCM_EOL;
199           get_music_info (pending, second_iter,
200                           &second_pitches, &second_durations);
201
202           if (first_pitches != SCM_EOL && second_pitches != SCM_EOL)
203             {
204               scm_sort_list_x (first_pitches,
205                                scm_primitive_eval (ly_symbol2scm ("Pitch::less_p")));
206               scm_sort_list_x (second_pitches,
207                                scm_primitive_eval (ly_symbol2scm ("Pitch::less_p")));
208
209               interval = gh_int2scm (unsmob_pitch (gh_car (first_pitches))->steps ()
210                                      - unsmob_pitch (gh_car (scm_last_pair (second_pitches)))->steps ());
211             }
212           
213           if (first_durations != SCM_EOL)
214             {
215               scm_sort_list_x (first_durations,
216                                scm_primitive_eval (ly_symbol2scm ("Duration::less_p")));
217               first_mom += unsmob_duration (gh_car (first_durations))->length_mom ();
218             }
219           
220           if (second_durations != SCM_EOL)
221             {
222               scm_sort_list_x (second_durations,
223                                scm_primitive_eval (ly_symbol2scm ("Duration::less_p")));
224               second_mom += unsmob_duration (gh_car (second_durations))->length_mom ();
225             }
226           
227           if (first_pitches != SCM_EOL && second_pitches == SCM_EOL
228                   && ! (second_until_ > now))
229             {
230               state |= UNRELATED;
231               state &= ~UNISILENCE;
232               if (! (state & ~ (UNRELATED | SOLO1 | UNISILENCE)))
233                 state |= SOLO1;
234             }
235           else
236             state &= ~SOLO1;
237
238           if (first_pitches == SCM_EOL && second_pitches != SCM_EOL
239               && ! (first_until_ > now))
240             {
241               state |= UNRELATED;
242               state &= ~UNISILENCE;
243               if (! (state & ~ (UNRELATED | SOLO2 | UNISILENCE)))
244                 state |= SOLO2;
245             }
246           else
247             state &= ~SOLO2;
248
249           if (gh_equal_p (first_durations, second_durations))
250             {
251               state &= ~UNISILENCE;
252               if (! (state & ~ (UNIRHYTHM | UNISON)))
253                 state |= UNIRHYTHM;
254             }
255           else
256             state &= ~ (UNIRHYTHM | UNISILENCE);
257           
258           if (first_pitches != SCM_EOL
259               && gh_equal_p (first_pitches, second_pitches))
260             {
261               state &= ~UNISILENCE;
262               if (! (state & ~ (UNIRHYTHM | UNISON)))
263                 state |= UNISON;
264             }
265           else
266             state &= ~ (UNISON);
267             
268           if (first_pitches == SCM_EOL && second_pitches == SCM_EOL)
269             {
270               if (! (state & ~ (UNIRHYTHM | UNISILENCE)))
271                 state |= UNISILENCE;
272             }
273           else if (!state)
274             state |= UNRELATED;
275           else
276             state &= ~ (UNISILENCE);
277
278           if (gh_number_p (interval))
279             {
280               SCM s = first_translator->get_property (ly_symbol2scm ("splitInterval"));
281               int i = gh_scm2int (interval);
282               if (gh_pair_p (s)
283                   && gh_number_p (gh_car (s))
284                   && gh_number_p (gh_cdr (s))
285                   && i >= gh_scm2int (gh_car (s))
286                   && i <= gh_scm2int (gh_cdr (s)))
287                 {
288                   if (! (state & ~ (SPLIT_INTERVAL | UNIRHYTHM | UNISON)))
289                     state |= SPLIT_INTERVAL;
290                 }
291               else
292                 state &= ~ (SPLIT_INTERVAL);
293             }
294
295           if (first && first_pitches != SCM_EOL)
296             first_until_ = first_mom;
297           if (first && second_pitches != SCM_EOL)
298             second_until_ = second_mom;
299           first = false;
300
301           if (first_iter->ok ())
302             first_iter->skip (pending);
303           if (second_iter->ok ())
304             second_iter->skip (pending);
305           now = pending;
306         }
307       delete first_iter;
308       delete second_iter;
309     }
310   return state;
311 }
312
313 static Span_req* abort_req = NULL;
314
315 void
316 Part_combine_music_iterator::process (Moment m)
317 {
318
319   /*
320     TODO:
321     - Use three named contexts (be it Thread or Voice): one, two, solo.
322       Let user pre-set (pushproperty) stem direction, remove
323       dynamic-engraver, and such.
324
325       **** Tried this, but won't work:
326
327       Consider thread switching: threads "one", "two" and "both".
328       User can't pre-set the (most important) stem direction at
329       thread level!
330    */
331  
332   if (suffix_.empty_b ())
333     suffix_ = first_iter_p_->report_to_l ()->daddy_trans_l_->id_str_.cut_str (3, INT_MAX);
334
335   int state = get_state (m);
336   if (state)
337     state_ = state;
338   else
339     state = state_;
340   
341   Part_combine_music const *p = dynamic_cast<Part_combine_music const* > (music_l_);
342
343
344   bool previously_combined_b = first_iter_p_->report_to_l ()->daddy_trans_l_
345     == second_iter_p_->report_to_l ()->daddy_trans_l_;
346
347   bool combine_b = previously_combined_b;
348
349   if (! (state & UNIRHYTHM)
350       || (state & SPLIT_INTERVAL)
351       || (state & (SOLO1 | SOLO2)))
352     combine_b = false;
353   else if (state & (UNIRHYTHM | UNISILENCE))
354     combine_b = true;
355
356   /*
357     When combining, abort all running spanners
358    */
359
360   if (!abort_req)
361     {
362       abort_req = new Span_req;
363       abort_req->set_mus_property ("span-type", ly_str02scm ("abort"));
364     }
365   
366   if (combine_b && combine_b != previously_combined_b)
367     {
368       if (second_iter_p_ && second_iter_p_->ok ())
369         second_iter_p_->try_music (abort_req);
370      }
371   String w = ly_scm2string (p->get_mus_property ("what"));
372   if (combine_b != previously_combined_b)
373     change_to (second_iter_p_, w, (combine_b ? "one" : "two")
374                + suffix_);
375   
376   Translator_group *first_translator = first_iter_p_->report_to_l ()->find_create_translator_l (w, "one" + suffix_);
377   Translator_group *second_translator = second_iter_p_->report_to_l ()->find_create_translator_l (w, "two" + suffix_);
378   
379
380   /* Hmm */
381   first_translator->set_property ("combineParts", SCM_BOOL_T);
382   second_translator ->set_property ("combineParts", SCM_BOOL_T);
383  
384  
385   /* hmm */
386   SCM b = (state & UNIRHYTHM) ? SCM_BOOL_T : SCM_BOOL_F;
387   first_translator->set_property ("unirhythm", b);
388   second_translator->set_property ("unirhythm", b);
389
390   b = (state & SPLIT_INTERVAL) ? SCM_BOOL_T : SCM_BOOL_F;
391   first_translator->set_property ("split-interval", b);
392   second_translator->set_property ("split-interval",  b);
393
394   b = (state & UNISILENCE) ? SCM_BOOL_T : SCM_BOOL_F;
395   first_translator->set_property ("unisilence", b);
396   second_translator->set_property ("unisilence", b);
397
398   // difference in definition...
399   //b = ((state & UNISON) ? SCM_BOOL_T : SCM_BOOL_F;
400   b = ((state & UNISON) && (state & UNIRHYTHM)) ? SCM_BOOL_T : SCM_BOOL_F;
401   first_translator->set_property ("unison", b);
402   second_translator->set_property ("unison", b);
403
404   SCM b1 = (state & SOLO1) ? SCM_BOOL_T : SCM_BOOL_F;
405   SCM b2 = (state & SOLO2) ? SCM_BOOL_T : SCM_BOOL_F;
406   first_translator->set_property ("solo", b1);
407   second_translator->set_property ("solo", b2);
408
409   if (first_iter_p_->ok ())
410     first_iter_p_->process (m);
411   
412   if (second_iter_p_->ok ())
413     second_iter_p_->process (m);
414 }
415
416 Music_iterator*
417 Part_combine_music_iterator::try_music_in_children (Music *m) const
418 {
419   Music_iterator * i =  first_iter_p_->try_music (m);
420   if (i)
421     return i;
422   else
423     return second_iter_p_->try_music (m);
424 }
425
426
427 SCM
428 Part_combine_music_iterator::get_music (Moment m)const
429 {
430   SCM s = SCM_EOL;
431   if (first_iter_p_)
432     s = gh_append2 (s,first_iter_p_->get_music (m));
433   if (second_iter_p_)
434     s = gh_append2 (second_iter_p_->get_music (m),s);
435   return s;
436 }
437
438 IMPLEMENT_CTOR_CALLBACK (Part_combine_music_iterator);