]> git.donarmstrong.com Git - lilypond.git/blob - lily/part-combine-music-iterator.cc
patch::: 1.3.136.jcn3
[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_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_eval2 (ly_str02scm ("Pitch::less_p"),
206                                           SCM_EOL));
207               scm_sort_list_x (second_pitches,
208                                scm_eval2 (ly_str02scm ("Pitch::less_p"),
209                                           SCM_EOL));
210               interval = gh_int2scm (unsmob_pitch (gh_car (first_pitches))->steps ()
211                                      - unsmob_pitch (gh_car (scm_last_pair (second_pitches)))->steps ());
212             }
213           
214           if (first_durations != SCM_EOL)
215             {
216               scm_sort_list_x (first_durations,
217                                scm_eval2 (ly_str02scm ("Duration::less_p"),
218                                           SCM_EOL));
219               first_mom += unsmob_duration (gh_car (first_durations))->length_mom ();
220             }
221           
222           if (second_durations != SCM_EOL)
223             {
224               scm_sort_list_x (second_durations,
225                                scm_eval2 (ly_str02scm ("Duration::less_p"),
226                                           SCM_EOL));
227               second_mom += unsmob_duration (gh_car (second_durations))->length_mom ();
228             }
229           
230           if (first_pitches != SCM_EOL && second_pitches == SCM_EOL
231                   && ! (second_until_ > now))
232             {
233               state |= UNRELATED;
234               state &= ~UNISILENCE;
235               if (! (state & ~ (UNRELATED | SOLO1 | UNISILENCE)))
236                 state |= SOLO1;
237             }
238           else
239             state &= ~SOLO1;
240
241           if (first_pitches == SCM_EOL && second_pitches != SCM_EOL
242               && ! (first_until_ > now))
243             {
244               state |= UNRELATED;
245               state &= ~UNISILENCE;
246               if (! (state & ~ (UNRELATED | SOLO2 | UNISILENCE)))
247                 state |= SOLO2;
248             }
249           else
250             state &= ~SOLO2;
251
252           if (gh_equal_p (first_durations, second_durations))
253             {
254               state &= ~UNISILENCE;
255               if (! (state & ~ (UNIRHYTHM | UNISON)))
256                 state |= UNIRHYTHM;
257             }
258           else
259             state &= ~ (UNIRHYTHM | UNISILENCE);
260           
261           if (first_pitches != SCM_EOL
262               && gh_equal_p (first_pitches, second_pitches))
263             {
264               state &= ~UNISILENCE;
265               if (! (state & ~ (UNIRHYTHM | UNISON)))
266                 state |= UNISON;
267             }
268           else
269             state &= ~ (UNISON);
270             
271           if (first_pitches == SCM_EOL && second_pitches == SCM_EOL)
272             {
273               if (! (state & ~ (UNIRHYTHM | UNISILENCE)))
274                 state |= UNISILENCE;
275             }
276           else if (!state)
277             state |= UNRELATED;
278           else
279             state &= ~ (UNISILENCE);
280
281           if (gh_number_p (interval))
282             {
283               SCM s = first_translator->get_property (ly_symbol2scm ("splitInterval"));
284               int i = gh_scm2int (interval);
285               if (gh_pair_p (s)
286                   && gh_number_p (gh_car (s))
287                   && gh_number_p (gh_cdr (s))
288                   && i >= gh_scm2int (gh_car (s))
289                   && i <= gh_scm2int (gh_cdr (s)))
290                 {
291                   if (! (state & ~ (SPLIT_INTERVAL | UNIRHYTHM | UNISON)))
292                     state |= SPLIT_INTERVAL;
293                 }
294               else
295                 state &= ~ (SPLIT_INTERVAL);
296             }
297
298           if (first && first_pitches != SCM_EOL)
299             first_until_ = first_mom;
300           if (first && second_pitches != SCM_EOL)
301             second_until_ = second_mom;
302           first = false;
303
304           if (first_iter->ok ())
305             first_iter->skip (pending);
306           if (second_iter->ok ())
307             second_iter->skip (pending);
308           now = pending;
309         }
310       delete first_iter;
311       delete second_iter;
312     }
313   return state;
314 }
315
316 static Span_req* abort_req = NULL;
317
318 void
319 Part_combine_music_iterator::process (Moment m)
320 {
321
322   /*
323     TODO:
324     - Use three named contexts (be it Thread or Voice): one, two, solo.
325       Let user pre-set (pushproperty) stem direction, remove
326       dynamic-engraver, and such.
327
328       **** Tried this, but won't work:
329
330       Consider thread switching: threads "one", "two" and "both".
331       User can't pre-set the (most important) stem direction at
332       thread level!
333    */
334  
335   if (suffix_.empty_b ())
336     suffix_ = first_iter_p_->report_to_l ()->daddy_trans_l_->id_str_.cut_str (3, INT_MAX);
337
338   int state = get_state (m);
339   if (state)
340     state_ = state;
341   else
342     state = state_;
343   
344   Part_combine_music const *p = dynamic_cast<Part_combine_music const* > (music_l_);
345
346
347   bool previously_combined_b = first_iter_p_->report_to_l ()->daddy_trans_l_
348     == second_iter_p_->report_to_l ()->daddy_trans_l_;
349
350   bool combine_b = previously_combined_b;
351
352   if (! (state & UNIRHYTHM)
353       || (state & SPLIT_INTERVAL)
354       || (state & (SOLO1 | SOLO2)))
355     combine_b = false;
356   else if (state & (UNIRHYTHM | UNISILENCE))
357     combine_b = true;
358
359   /*
360     When combining, abort all running spanners
361    */
362
363   if (!abort_req)
364     {
365       abort_req = new Span_req;
366       abort_req->set_mus_property ("span-type", ly_str02scm ("abort"));
367     }
368   
369   if (combine_b && combine_b != previously_combined_b)
370     {
371       if (second_iter_p_ && second_iter_p_->ok ())
372         second_iter_p_->try_music (abort_req);
373      }
374   String w = ly_scm2string (p->get_mus_property ("what"));
375   if (combine_b != previously_combined_b)
376     change_to (second_iter_p_, w, (combine_b ? "one" : "two")
377                + suffix_);
378   
379   Translator_group *first_translator = first_iter_p_->report_to_l ()->find_create_translator_l (w, "one" + suffix_);
380   Translator_group *second_translator = second_iter_p_->report_to_l ()->find_create_translator_l (w, "two" + suffix_);
381   
382
383   /* Hmm */
384   first_translator->set_property ("combineParts", SCM_BOOL_T);
385   second_translator ->set_property ("combineParts", SCM_BOOL_T);
386  
387  
388   /* hmm */
389   SCM b = (state & UNIRHYTHM) ? SCM_BOOL_T : SCM_BOOL_F;
390   first_translator->set_property ("unirhythm", b);
391   second_translator->set_property ("unirhythm", b);
392
393   b = (state & SPLIT_INTERVAL) ? SCM_BOOL_T : SCM_BOOL_F;
394   first_translator->set_property ("split-interval", b);
395   second_translator->set_property ("split-interval",  b);
396
397   b = (state & UNISILENCE) ? SCM_BOOL_T : SCM_BOOL_F;
398   first_translator->set_property ("unisilence", b);
399   second_translator->set_property ("unisilence", b);
400
401   // difference in definition...
402   //b = ((state & UNISON) ? SCM_BOOL_T : SCM_BOOL_F;
403   b = ((state & UNISON) && (state & UNIRHYTHM)) ? SCM_BOOL_T : SCM_BOOL_F;
404   first_translator->set_property ("unison", b);
405   second_translator->set_property ("unison", b);
406
407   SCM b1 = (state & SOLO1) ? SCM_BOOL_T : SCM_BOOL_F;
408   SCM b2 = (state & SOLO2) ? SCM_BOOL_T : SCM_BOOL_F;
409   first_translator->set_property ("solo", b1);
410   second_translator->set_property ("solo", b2);
411
412   if (first_iter_p_->ok ())
413     first_iter_p_->process (m);
414   
415   if (second_iter_p_->ok ())
416     second_iter_p_->process (m);
417 }
418
419 Music_iterator*
420 Part_combine_music_iterator::try_music_in_children (Music *m) const
421 {
422   Music_iterator * i =  first_iter_p_->try_music (m);
423   if (i)
424     return i;
425   else
426     return second_iter_p_->try_music (m);
427 }
428
429
430 SCM
431 Part_combine_music_iterator::get_music (Moment m)const
432 {
433   SCM s = SCM_EOL;
434   if (first_iter_p_)
435     s = gh_append2 (s,first_iter_p_->get_music (m));
436   if (second_iter_p_)
437     s = gh_append2 (second_iter_p_->get_music (m),s);
438   return s;
439 }
440
441 IMPLEMENT_CTOR_CALLBACK (Part_combine_music_iterator);