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