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