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