]> git.donarmstrong.com Git - lilypond.git/blob - lily/part-combine-music-iterator.cc
7a871bc815a26035d39eefeac5c344405d65a513
[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--2002 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_ = 0;
20   second_iter_ = 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_;
28   delete first_iter_;
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_ = src.second_iter_ ? src.second_iter_->clone () : 0;
35   first_iter_ = src.first_iter_ ? src.first_iter_->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_->ok ())
49     p = p <? first_iter_->pending_moment ();
50
51   if (second_iter_->ok ())
52     p = p <? second_iter_->pending_moment ();
53   return p;
54 }
55
56 bool
57 Part_combine_music_iterator::ok () const
58 {
59   return first_iter_->ok () || second_iter_->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*> (get_music ());
66   
67   first_iter_ = get_iterator (m->get_first ());
68   second_iter_ = get_iterator (m->get_second ());
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 ();
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_string_ != to_type)
89     {
90       last = current;
91       current = current->daddy_trans_;
92     }
93
94   if (current && current->id_string_ == 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 ()->find_create_translator (to_type, to_id);
105         current->remove_translator (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_string_  = get_change ()->change_to_id_string_;
115         */
116         error (_f ("I'm one myself: `%s'", to_type.to_str0 ()));
117       }
118   else
119     error (_f ("none of these in my family: `%s'", to_id.to_str0 ()));
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_pending_events (m); gh_pair_p (i); i = ly_cdr (i))
130         {
131           Music *m = unsmob_music (ly_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             {
136               SCM d = r->get_mus_property ("duration");
137               if (d == SCM_EOL)
138                 r->origin ()->warning ("Rhythmic_req has no duration\n");
139                 else
140                   // *durations = gh_cons (r->get_mus_property ("duration"), *durations);
141                   *durations = gh_cons (d, *durations);
142             }
143         }
144     }
145 }
146   
147 int
148 Part_combine_music_iterator::get_state (Moment)
149 {
150   int state = UNKNOWN;
151   Part_combine_music const *p = dynamic_cast<Part_combine_music const* > (get_music ());
152
153   String w = ly_scm2string (p->get_mus_property ("what"));
154     
155   
156   Translator_group *first_translator = first_iter_->report_to ()->find_create_translator (w, "one" + suffix_);
157
158   SCM s = first_translator->get_property ("changeMoment");
159   if (!gh_pair_p (s))
160     return state;
161
162   Moment change_mom = *unsmob_moment (ly_car (s));
163   Moment diff_mom = *unsmob_moment (ly_cdr (s));
164   
165   Moment now = pending_moment ();
166
167   if (!now.main_part_.mod_rat (change_mom.main_part_))
168     {
169       SCM interval = SCM_BOOL_F;
170       if (first_until_ < now)
171         first_until_ = now;
172       if (second_until_ < now)
173         second_until_ = now;
174
175       Moment first_mom = first_until_;
176       Moment second_mom = second_until_;
177       Moment diff_until = diff_mom + now;
178
179       bool first = true;
180       Music_iterator *first_iter = first_iter_->clone ();
181       Music_iterator *second_iter = second_iter_->clone ();
182
183       Moment last_pending (-1);
184       Moment pending = now;
185       while (now < diff_until
186               && (first_iter->ok () || second_iter->ok ())
187
188              // urg, this is a hack, haven't caught this case yet
189              && (pending != last_pending))
190         {
191           if (!second_iter->ok ())
192             pending = first_iter->pending_moment ();
193           else if (!first_iter->ok ())
194             pending = second_iter->pending_moment ();
195           else
196             pending = first_iter->pending_moment () <? second_iter->pending_moment ();
197           last_pending = pending;
198
199           SCM first_pitches = SCM_EOL;
200           SCM first_durations = SCM_EOL;
201           get_music_info (pending, first_iter,
202                           &first_pitches, &first_durations);
203       
204           SCM second_pitches = SCM_EOL;
205           SCM second_durations = SCM_EOL;
206           get_music_info (pending, second_iter,
207                           &second_pitches, &second_durations);
208
209           if (first_pitches != SCM_EOL && second_pitches != SCM_EOL)
210             {
211               scm_sort_list_x (first_pitches,
212                                scm_primitive_eval (ly_symbol2scm ("Pitch::less_p")));
213               scm_sort_list_x (second_pitches,
214                                scm_primitive_eval (ly_symbol2scm ("Pitch::less_p")));
215
216               interval = gh_int2scm (unsmob_pitch (ly_car (first_pitches))->steps ()
217                                      - unsmob_pitch (ly_car (scm_last_pair (second_pitches)))->steps ());
218             }
219
220           if (first_durations != SCM_EOL)
221             {
222               scm_sort_list_x (first_durations,
223                                scm_primitive_eval (ly_symbol2scm ("Duration::less_p")));
224               first_mom += unsmob_duration (ly_car (first_durations))->length_mom ();
225             }
226           
227           if (second_durations != SCM_EOL)
228             {
229               scm_sort_list_x (second_durations,
230                                scm_primitive_eval (ly_symbol2scm ("Duration::less_p")));
231               second_mom += unsmob_duration (ly_car (second_durations))->length_mom ();
232             }
233           
234           if (first_pitches != SCM_EOL && second_pitches == SCM_EOL
235                   && ! (second_until_ > now))
236             {
237               state |= UNRELATED;
238               state &= ~UNISILENCE;
239               if (! (state & ~ (UNRELATED | SOLO1 | UNISILENCE)))
240                 state |= SOLO1;
241             }
242           else
243             state &= ~SOLO1;
244
245           if (first_pitches == SCM_EOL && second_pitches != SCM_EOL
246               && ! (first_until_ > now))
247             {
248               state |= UNRELATED;
249               state &= ~UNISILENCE;
250               if (! (state & ~ (UNRELATED | SOLO2 | UNISILENCE)))
251                 state |= SOLO2;
252             }
253           else
254             state &= ~SOLO2;
255
256           if (gh_equal_p (first_durations, second_durations))
257             {
258               state &= ~UNISILENCE;
259               if (! (state & ~ (UNIRHYTHM | UNISON)))
260                 state |= UNIRHYTHM;
261             }
262           else
263             state &= ~ (UNIRHYTHM | UNISILENCE);
264           
265           if (first_pitches != SCM_EOL
266               && gh_equal_p (first_pitches, second_pitches))
267             {
268               state &= ~UNISILENCE;
269               if (! (state & ~ (UNIRHYTHM | UNISON)))
270                 state |= UNISON;
271             }
272           else
273             state &= ~ (UNISON);
274             
275           if (first_pitches == SCM_EOL && second_pitches == SCM_EOL)
276             {
277               if (! (state & ~ (UNIRHYTHM | UNISILENCE)))
278                 state |= UNISILENCE;
279             }
280           else if (!state)
281             state |= UNRELATED;
282           else
283             state &= ~ (UNISILENCE);
284
285           if (gh_number_p (interval))
286             {
287               SCM s = first_translator->get_property ("splitInterval");
288               int i = gh_scm2int (interval);
289               if (gh_pair_p (s)
290                   && gh_number_p (ly_car (s))
291                   && gh_number_p (ly_cdr (s))
292                   && i >= gh_scm2int (ly_car (s))
293                   && i <= gh_scm2int (ly_cdr (s)))
294                 {
295                   if (! (state & ~ (SPLIT_INTERVAL | UNIRHYTHM | UNISON)))
296                     state |= SPLIT_INTERVAL;
297                 }
298               else
299                 state &= ~ (SPLIT_INTERVAL);
300             }
301
302           if (first && first_pitches != SCM_EOL)
303             first_until_ = first_mom;
304           if (first && second_pitches != SCM_EOL)
305             second_until_ = second_mom;
306           first = false;
307
308           if (first_iter->ok ())
309             first_iter->skip (pending);
310           if (second_iter->ok ())
311             second_iter->skip (pending);
312           now = pending;
313         }
314       delete first_iter;
315       delete second_iter;
316     }
317   return state;
318 }
319
320 static Span_req* abort_req = NULL;
321
322 void
323 Part_combine_music_iterator::process (Moment m)
324 {
325
326   /*
327     TODO:
328     - Use three named contexts (be it Thread or Voice): one, two, solo.
329       Let user pre-set (pushproperty) stem direction, remove
330       dynamic-engraver, and such.
331
332       **** Tried this, but won't work:
333
334       Consider thread switching: threads "one", "two" and "both".
335       User can't pre-set the (most important) stem direction at
336       thread level!
337    */
338  
339   if (suffix_.empty_b ())
340     suffix_ = first_iter_->report_to ()
341       ->daddy_trans_->id_string_.cut_string (3, INT_MAX);
342
343   int state = get_state (m);
344   if (state)
345     state_ = state;
346   else
347     state = state_;
348   
349   Part_combine_music const *p =
350     dynamic_cast<Part_combine_music const* > (get_music ());
351
352
353   bool previously_combined_b = first_iter_->report_to ()->daddy_trans_
354     == second_iter_->report_to ()->daddy_trans_;
355
356   bool combine_b = previously_combined_b;
357
358   if (! (state & UNIRHYTHM)
359       || (state & SPLIT_INTERVAL)
360       || (state & (SOLO1 | SOLO2)))
361     combine_b = false;
362   else if (state & (UNIRHYTHM | UNISILENCE))
363     combine_b = true;
364
365   /*
366     When combining, abort all running spanners
367    */
368
369   if (!abort_req)
370     {
371       abort_req = new Span_req;
372       abort_req->set_mus_property ("span-type", scm_makfrom0str ("abort"));
373     }
374   
375   if (combine_b && combine_b != previously_combined_b)
376     {
377       if (second_iter_ && second_iter_->ok ())
378         second_iter_->try_music (abort_req);
379      }
380   String w = ly_scm2string (p->get_mus_property ("what"));
381   if (combine_b != previously_combined_b)
382     change_to (second_iter_, w, (combine_b ? "one" : "two")
383                + suffix_);
384   
385   Translator_group *first_translator = first_iter_->report_to ()->find_create_translator (w, "one" + suffix_);
386   Translator_group *second_translator = second_iter_->report_to ()->find_create_translator (w, "two" + suffix_);
387   
388
389   /* Hmm */
390   first_translator->set_property ("combineParts", SCM_BOOL_T);
391   second_translator ->set_property ("combineParts", SCM_BOOL_T);
392  
393  
394   /* hmm */
395   SCM b = (state & UNIRHYTHM) ? SCM_BOOL_T : SCM_BOOL_F;
396   first_translator->set_property ("unirhythm", b);
397   second_translator->set_property ("unirhythm", b);
398
399   b = (state & SPLIT_INTERVAL) ? SCM_BOOL_T : SCM_BOOL_F;
400   first_translator->set_property ("split-interval", b);
401   second_translator->set_property ("split-interval",  b);
402
403   b = (state & UNISILENCE) ? SCM_BOOL_T : SCM_BOOL_F;
404   first_translator->set_property ("unisilence", b);
405   second_translator->set_property ("unisilence", b);
406
407   // difference in definition...
408   //b = ((state & UNISON) ? SCM_BOOL_T : SCM_BOOL_F;
409   b = ((state & UNISON) && (state & UNIRHYTHM)) ? SCM_BOOL_T : SCM_BOOL_F;
410   first_translator->set_property ("unison", b);
411   second_translator->set_property ("unison", b);
412
413   SCM b1 = (state & SOLO1) ? SCM_BOOL_T : SCM_BOOL_F;
414   SCM b2 = (state & SOLO2) ? SCM_BOOL_T : SCM_BOOL_F;
415   first_translator->set_property ("solo", b1);
416   second_translator->set_property ("solo", b2);
417
418   /* Can't these be computed? */
419   first_translator->set_property ("othersolo", b2);
420   second_translator->set_property ("othersolo", b1);
421
422   if (first_iter_->ok ())
423     first_iter_->process (m);
424   
425   if (second_iter_->ok ())
426     second_iter_->process (m);
427 }
428
429 Music_iterator*
430 Part_combine_music_iterator::try_music_in_children (Music *m) const
431 {
432   Music_iterator * i =  first_iter_->try_music (m);
433   if (i)
434     return i;
435   else
436     return second_iter_->try_music (m);
437 }
438
439
440 SCM
441 Part_combine_music_iterator::get_pending_events (Moment m)const
442 {
443   SCM s = SCM_EOL;
444   if (first_iter_)
445     s = gh_append2 (s,first_iter_->get_pending_events (m));
446   if (second_iter_)
447     s = gh_append2 (second_iter_->get_pending_events (m),s);
448   return s;
449 }
450
451 IMPLEMENT_CTOR_CALLBACK (Part_combine_music_iterator);