]> git.donarmstrong.com Git - lilypond.git/blob - lily/part-combine-music-iterator.cc
* lily/multi-measure-rest-engraver.cc (process_music): extra check
[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--2003 Jan Nieuwenhuizen <janneke@gnu.org>
7  */
8
9 #include "part-combine-music-iterator.hh"
10 #include "translator-group.hh"
11 #include "event.hh"
12 #include "music-sequence.hh"
13 #include "lily-guile.hh"
14 #include "warn.hh"
15 #include "music-iterator.hh"
16
17 class Part_combine_music_iterator : public Music_iterator
18 {
19 public:
20   VIRTUAL_COPY_CONS (Music_iterator);
21   Part_combine_music_iterator ();
22
23   enum State { UNKNOWN, UNRELATED=1, SOLO1=2, SOLO2=4, UNIRHYTHM=8, UNISON=16, UNISILENCE=32, SPLIT_INTERVAL=64 };
24   DECLARE_SCHEME_CALLBACK(constructor, ()); 
25 protected:
26   virtual void derived_substitute (Translator_group*f, Translator_group*t) ;
27
28   virtual void derived_mark () const;
29   Part_combine_music_iterator (Part_combine_music_iterator const &);
30   virtual void construct_children ();
31   virtual Moment pending_moment () const;
32   virtual void do_quit(); 
33   virtual void process (Moment);
34   virtual SCM get_pending_events (Moment)const;
35   virtual Music_iterator *try_music_in_children (Music *) const;
36   virtual bool ok () const;
37
38 private:
39   void change_to (Music_iterator*, SCM,  String);
40   int get_state (Moment m);
41
42   Music_iterator * first_iter_;
43   Music_iterator * second_iter_;
44   Moment first_until_;
45   Moment second_until_;
46   int state_;
47   String suffix_;
48 };
49
50
51 Part_combine_music_iterator::Part_combine_music_iterator ()
52 {
53   first_iter_ = 0;
54   second_iter_ = 0;
55   first_until_ = 0;
56   second_until_ = 0;
57   state_ = 0;
58 }
59
60 void
61 Part_combine_music_iterator::derived_mark () const
62 {
63   if (first_iter_)
64     scm_gc_mark (first_iter_->self_scm());
65   if (second_iter_)
66     scm_gc_mark(second_iter_->self_scm());
67 }
68
69 void
70 Part_combine_music_iterator::derived_substitute (Translator_group*f,
71                                                  Translator_group*t)
72 {
73   if (first_iter_)
74     first_iter_->substitute_outlet (f,t);
75   if (second_iter_)
76     second_iter_->substitute_outlet (f,t);
77 }
78
79 void
80 Part_combine_music_iterator::do_quit ()
81 {
82   if (first_iter_)
83     first_iter_->quit();
84   if (second_iter_)
85     second_iter_->quit();
86 }
87
88 Part_combine_music_iterator::Part_combine_music_iterator (Part_combine_music_iterator const &src)
89   : Music_iterator (src)
90 {
91   first_iter_ = 0;
92   second_iter_ = 0;
93
94   if(src.first_iter_)
95     first_iter_ = src.first_iter_->clone ();
96   if (src.second_iter_)
97     second_iter_ = src.second_iter_->clone ();
98
99   first_until_ = src.first_until_;
100   second_until_ = src.second_until_;
101   state_ = src.state_;
102   suffix_ = src.suffix_;
103
104   if (first_iter_)
105     scm_gc_unprotect_object (first_iter_->self_scm());
106   if (second_iter_)
107     scm_gc_unprotect_object (second_iter_->self_scm());
108 }
109
110 Moment
111 Part_combine_music_iterator::pending_moment () const
112 {
113   Moment p;
114   p.set_infinite (1);
115   if (first_iter_->ok ())
116     p = p <? first_iter_->pending_moment ();
117
118   if (second_iter_->ok ())
119     p = p <? second_iter_->pending_moment ();
120   return p;
121 }
122
123 bool
124 Part_combine_music_iterator::ok () const
125 {
126   return first_iter_->ok () || second_iter_->ok ();
127 }
128
129
130 void
131 Part_combine_music_iterator::construct_children ()
132 {
133   SCM lst = get_music ()->get_mus_property ("elements");
134
135   first_iter_ = unsmob_iterator (get_iterator (unsmob_music (gh_car (lst))));
136   second_iter_ = unsmob_iterator (get_iterator (unsmob_music (gh_cadr (lst))));
137 }
138
139 void
140 Part_combine_music_iterator::change_to (Music_iterator *it, SCM to_type,
141                                         String to_id)
142 {
143   Translator_group * current = it->report_to ();
144   Translator_group * last = 0;
145
146   /*
147     Cut & Paste from from Auto_change_iterator from Change_iterator (ugh).
148
149     TODO: abstract this function 
150    */
151   
152   /* find the type  of translator that we're changing.
153      
154      If \translator Staff = bass, then look for Staff = *
155    */
156   while (current && !current->is_alias (to_type))
157     {
158       last = current;
159       current = current->daddy_trans_;
160     }
161
162   if (current && current->id_string_ == to_id)
163     {
164       String msg;
165       msg += _ ("Can't switch translators, I'm there already");
166     }
167   
168   if (current) 
169     if (last)
170       {
171         Translator_group * dest = 
172           it->report_to ()->find_create_translator (to_type, to_id, SCM_EOL);
173         current->remove_translator (last);
174         dest->add_used_group_translator (last);
175       }
176     else
177       {
178         /*
179           We could change the current translator's id, but that would make 
180           errors hard to catch
181           
182            last->translator_id_string_  = get_change ()->change_to_id_string_;
183         */
184         error (_f ("I'm one myself: `%s'", ly_symbol2string (to_type).to_str0 ()));
185       }
186   else
187     error (_f ("none of these in my family: `%s'", to_id.to_str0 ()));
188 }
189
190
191 // SCM*, moet / kan dat niet met set_x ofzo?
192 static void
193 get_music_info (Moment m, Music_iterator* iter, SCM *pitches, SCM *durations)
194 {
195   if (iter->ok ())
196     {
197       for (SCM i = iter->get_pending_events (m); gh_pair_p (i); i = ly_cdr (i))
198         {
199           Music *m = unsmob_music (ly_car (i));
200           SCM p = m->get_mus_property ("pitch");
201           SCM d = m->get_mus_property ("duration");
202           if (unsmob_pitch (p))
203             *pitches = gh_cons (p, *pitches);
204           if (unsmob_duration (d))
205             *durations = gh_cons (d, *durations);
206         }
207     }
208 }
209
210 int
211 Part_combine_music_iterator::get_state (Moment)
212 {
213   int state = UNKNOWN;
214   
215   Music *p = get_music ();
216
217   SCM w = p->get_mus_property ("what");
218     
219   
220   Translator_group *first_translator = first_iter_->report_to ()->find_create_translator (w, "one" + suffix_, SCM_EOL);
221
222   SCM s = first_translator->get_property ("changeMoment");
223   if (!gh_pair_p (s))
224     return state;
225
226   Moment change_mom = *unsmob_moment (ly_car (s));
227   Moment diff_mom = *unsmob_moment (ly_cdr (s));
228   
229   Moment now = pending_moment ();
230
231   if (!now.main_part_.mod_rat (change_mom.main_part_))
232     {
233       SCM interval = SCM_BOOL_F;
234       if (first_until_ < now)
235         first_until_ = now;
236       if (second_until_ < now)
237         second_until_ = now;
238
239       Moment first_mom = first_until_;
240       Moment second_mom = second_until_;
241       Moment diff_until = diff_mom + now;
242
243
244       bool first = true;
245       Music_iterator *first_iter = first_iter_->clone ();
246       Music_iterator *second_iter = second_iter_->clone ();
247       
248       Moment last_pending (-1);
249       Moment pending = now;
250       while (now < diff_until
251               && (first_iter->ok () || second_iter->ok ())
252
253              // urg, this is a hack, haven't caught this case yet
254              && (pending != last_pending))
255         {
256           if (!second_iter->ok ())
257             pending = first_iter->pending_moment ();
258           else if (!first_iter->ok ())
259             pending = second_iter->pending_moment ();
260           else
261             pending = first_iter->pending_moment () <? second_iter->pending_moment ();
262           last_pending = pending;
263
264           SCM first_pitches = SCM_EOL;
265           SCM first_durations = SCM_EOL;
266           get_music_info (pending, first_iter,
267                           &first_pitches, &first_durations);
268       
269           SCM second_pitches = SCM_EOL;
270           SCM second_durations = SCM_EOL;
271           get_music_info (pending, second_iter,
272                           &second_pitches, &second_durations);
273
274           if (first_pitches != SCM_EOL && second_pitches != SCM_EOL)
275             {
276               scm_sort_list_x (first_pitches, Pitch::less_p_proc);
277               scm_sort_list_x (second_pitches, Pitch::less_p_proc);
278
279               interval = gh_int2scm (unsmob_pitch (ly_car (first_pitches))->steps ()
280                                      - unsmob_pitch (ly_car (scm_last_pair (second_pitches)))->steps ());
281             }
282
283           if (first_durations != SCM_EOL)
284             {
285               scm_sort_list_x (first_durations,
286                                Duration::less_p_proc);
287               first_mom += unsmob_duration (ly_car (first_durations))->get_length ();
288             }
289           
290           if (second_durations != SCM_EOL)
291             {
292               scm_sort_list_x (second_durations,
293                                Duration::less_p_proc);
294               second_mom += unsmob_duration (ly_car (second_durations))->get_length ();
295             }
296           
297           if (first_pitches != SCM_EOL && second_pitches == SCM_EOL
298                   && ! (second_until_ > now))
299             {
300               state |= UNRELATED;
301               state &= ~UNISILENCE;
302               if (! (state & ~ (UNRELATED | SOLO1 | UNISILENCE)))
303                 state |= SOLO1;
304             }
305           else
306             state &= ~SOLO1;
307
308           if (first_pitches == SCM_EOL && second_pitches != SCM_EOL
309               && ! (first_until_ > now))
310             {
311               state |= UNRELATED;
312               state &= ~UNISILENCE;
313               if (! (state & ~ (UNRELATED | SOLO2 | UNISILENCE)))
314                 state |= SOLO2;
315             }
316           else
317             state &= ~SOLO2;
318
319           if (gh_equal_p (first_durations, second_durations))
320             {
321               state &= ~UNISILENCE;
322               if (! (state & ~ (UNIRHYTHM | UNISON)))
323                 state |= UNIRHYTHM;
324             }
325           else
326             state &= ~ (UNIRHYTHM | UNISILENCE);
327           
328           if (first_pitches != SCM_EOL
329               && gh_equal_p (first_pitches, second_pitches))
330             {
331               state &= ~UNISILENCE;
332               if (! (state & ~ (UNIRHYTHM | UNISON)))
333                 state |= UNISON;
334             }
335           else
336             state &= ~ (UNISON);
337             
338           if (first_pitches == SCM_EOL && second_pitches == SCM_EOL)
339             {
340               if (! (state & ~ (UNIRHYTHM | UNISILENCE)))
341                 state |= UNISILENCE;
342             }
343           else if (!state)
344             state |= UNRELATED;
345           else
346             state &= ~ (UNISILENCE);
347
348           if (gh_number_p (interval))
349             {
350               SCM s = first_translator->get_property ("splitInterval");
351               int i = gh_scm2int (interval);
352               if (gh_pair_p (s)
353                   && gh_number_p (ly_car (s))
354                   && gh_number_p (ly_cdr (s))
355                   && i >= gh_scm2int (ly_car (s))
356                   && i <= gh_scm2int (ly_cdr (s)))
357                 {
358                   if (! (state & ~ (SPLIT_INTERVAL | UNIRHYTHM | UNISON)))
359                     state |= SPLIT_INTERVAL;
360                 }
361               else
362                 state &= ~ (SPLIT_INTERVAL);
363             }
364
365           if (first && first_pitches != SCM_EOL)
366             first_until_ = first_mom;
367           if (first && second_pitches != SCM_EOL)
368             second_until_ = second_mom;
369           first = false;
370
371           if (first_iter->ok ())
372             first_iter->skip (pending);
373           if (second_iter->ok ())
374             second_iter->skip (pending);
375           now = pending;
376         }
377       scm_gc_unprotect_object (first_iter->self_scm ());
378       scm_gc_unprotect_object (second_iter->self_scm ());
379     }
380
381   return state;
382 }
383
384 static Music* abort_req = NULL;
385
386 void
387 Part_combine_music_iterator::process (Moment m)
388 {
389
390   /*
391     TODO:
392     - Use three named contexts (be it Thread or Voice): one, two, solo.
393       Let user pre-set (pushproperty) stem direction, remove
394       dynamic-engraver, and such.
395
396       **** Tried this, but won't work:
397
398 s      Consider thread switching: threads "one", "two" and "both".
399       User can't pre-set the (most important) stem direction at
400       thread level!
401    */
402  
403   if (suffix_.is_empty ())
404     suffix_ = first_iter_->report_to ()
405       ->daddy_trans_->id_string_.cut_string (3, INT_MAX);
406
407   int state = get_state (m);
408   if (state)
409     state_ = state;
410   else
411     state = state_;
412   
413   Music *p =get_music ();
414
415
416   bool previously_combined_b = first_iter_->report_to ()->daddy_trans_
417     == second_iter_->report_to ()->daddy_trans_;
418
419   bool combine_b = previously_combined_b;
420
421   if (! (state & UNIRHYTHM)
422       || (state & SPLIT_INTERVAL)
423       || (state & (SOLO1 | SOLO2)))
424     combine_b = false;
425   else if (state & (UNIRHYTHM | UNISILENCE))
426     combine_b = true;
427
428   /*
429     When combining, abort all running spanners
430    */
431
432   if (!abort_req)
433     {
434       abort_req = make_music_by_name (ly_symbol2scm ("AbortEvent"));
435     }
436   
437   if (combine_b && combine_b != previously_combined_b)
438     {
439       if (second_iter_ && second_iter_->ok ())
440         second_iter_->try_music (abort_req);
441      }
442   SCM  w = p->get_mus_property ("what");
443   if (combine_b != previously_combined_b)
444     change_to (second_iter_, w, (combine_b ? "one" : "two")
445                + suffix_);
446   
447   Translator_group *first_translator = first_iter_->report_to ()->find_create_translator (w, "one" + suffix_, SCM_EOL);
448   Translator_group *second_translator = second_iter_->report_to ()->find_create_translator (w, "two" + suffix_, SCM_EOL);
449   
450
451   /* Hmm */
452   first_translator->set_property ("combineParts", SCM_BOOL_T);
453   second_translator ->set_property ("combineParts", SCM_BOOL_T);
454  
455  
456   /* hmm */
457   SCM b = (state & UNIRHYTHM) ? SCM_BOOL_T : SCM_BOOL_F;
458   first_translator->set_property ("unirhythm", b);
459   second_translator->set_property ("unirhythm", b);
460
461   b = (state & SPLIT_INTERVAL) ? SCM_BOOL_T : SCM_BOOL_F;
462   first_translator->set_property ("split-interval", b);
463   second_translator->set_property ("split-interval",  b);
464
465   b = (state & UNISILENCE) ? SCM_BOOL_T : SCM_BOOL_F;
466   first_translator->set_property ("unisilence", b);
467   second_translator->set_property ("unisilence", b);
468
469   // difference in definition...
470   //b = ((state & UNISON) ? SCM_BOOL_T : SCM_BOOL_F;
471   b = ((state & UNISON) && (state & UNIRHYTHM)) ? SCM_BOOL_T : SCM_BOOL_F;
472   first_translator->set_property ("unison", b);
473   second_translator->set_property ("unison", b);
474
475   SCM b1 = (state & SOLO1) ? SCM_BOOL_T : SCM_BOOL_F;
476   SCM b2 = (state & SOLO2) ? SCM_BOOL_T : SCM_BOOL_F;
477   first_translator->set_property ("solo", b1);
478   second_translator->set_property ("solo", b2);
479
480   /* Can't these be computed? */
481   first_translator->set_property ("othersolo", b2);
482   second_translator->set_property ("othersolo", b1);
483
484   if (first_iter_->ok ())
485     first_iter_->process (m);
486   
487   if (second_iter_->ok ())
488     second_iter_->process (m);
489 }
490
491 Music_iterator*
492 Part_combine_music_iterator::try_music_in_children (Music *m) const
493 {
494   Music_iterator * i =  first_iter_->try_music (m);
495   if (i)
496     return i;
497   else
498     return second_iter_->try_music (m);
499 }
500
501
502 SCM
503 Part_combine_music_iterator::get_pending_events (Moment m)const
504 {
505   SCM s = SCM_EOL;
506   if (first_iter_)
507     s = gh_append2 (s,first_iter_->get_pending_events (m));
508   if (second_iter_)
509     s = gh_append2 (second_iter_->get_pending_events (m),s);
510   return s;
511 }
512
513 IMPLEMENT_CTOR_CALLBACK (Part_combine_music_iterator);