]> git.donarmstrong.com Git - lilypond.git/blob - lily/part-combine-music-iterator.cc
release: 1.3.87
[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   //hmm
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 void
111 Part_combine_music_iterator::process (Moment m)
112 {
113
114   /*
115     TODO:
116     - Use three named contexts (be it Thread or Voice): one, two, solo.
117       Let user pre-set (pushproperty) stem direction, remove
118       dynamic-engraver, and such.
119     - staff-combiner must switch only on per-measure basis (maybe even on
120       per-line-basis, but that's not feasible).  Maybe set minimum lengths
121       of matching rhythm for combine/split?
122    */
123  
124   Part_combine_music const * p = dynamic_cast<Part_combine_music const* > (music_l_);
125
126   Moment now = pending_moment ();
127
128   Array<Musical_pitch> first_pitches;
129   Array<Duration> first_durations;
130   if (first_iter_p_->ok ())
131     {
132       /* get_music () also performs next, modifying iterator */
133       Music_iterator *mi = first_iter_p_->clone ();
134       for (SCM i = mi->get_music (now); gh_pair_p (i); i = gh_cdr (i))
135         {
136           Music *m = unsmob_music (gh_car (i));
137           if (Melodic_req *r = dynamic_cast<Melodic_req *> (m))
138             first_pitches.push (r->pitch_);
139           if (Rhythmic_req *r = dynamic_cast<Rhythmic_req *> (m))
140                     first_durations.push (r->duration_);
141         }
142       delete mi;
143     }
144   
145   Array<Musical_pitch> second_pitches;
146   Array<Duration> second_durations;
147   if (second_iter_p_->ok ())
148     {
149       Music_iterator *mi = second_iter_p_->clone ();
150       for (SCM i = mi->get_music (now); gh_pair_p (i); i = gh_cdr (i))
151         {
152           Music *m = unsmob_music (gh_car (i));
153           if (Melodic_req *r = dynamic_cast<Melodic_req *> (m))
154             second_pitches.push (r->pitch_);
155           if (Rhythmic_req *r = dynamic_cast<Rhythmic_req *> (m))
156             second_durations.push (r->duration_);
157         }
158       delete mi;
159     }
160   
161   SCM interval = SCM_BOOL_F;
162   if (first_pitches.size () && second_pitches.size ())
163     {
164       first_pitches.sort (Musical_pitch::compare);
165       second_pitches.sort (Musical_pitch::compare);
166       interval = gh_int2scm (first_pitches.top ().steps ()
167                              - second_pitches[0].steps ());
168     }
169   if (first_durations.size ())
170     {
171       first_durations.sort (Duration::compare);
172       Moment new_until = now + first_durations.top ().length_mom ();
173       if (new_until > first_until_)
174         first_until_ = new_until;
175     }
176
177     if (second_durations.size ())
178     {
179       second_durations.sort (Duration::compare);
180       Moment new_until = now + second_durations.top ().length_mom ();
181       if (new_until > second_until_)
182         second_until_ = new_until;
183     }
184
185 #if 0 /* DEBUG */
186   printf ("now: %s\n", now.str ().ch_C ());
187   printf ("first: ");
188   for (int i = 0; i < first_pitches.size (); i++)
189     {
190       printf ("%s, ", first_pitches[i].str ().ch_C ());
191     }
192   printf ("\nsecond: ");
193   for (int i = 0; i < second_pitches.size (); i++)
194     {
195       printf ("%s, ", second_pitches[i].str ().ch_C ());
196     }
197   printf ("\n");
198 #endif
199
200   Translator_group * fir = first_iter_p_->report_to_l ();
201   Translator_group * sir = second_iter_p_->report_to_l ();
202
203   bool solo_b = (first_pitches.empty () != second_pitches.empty ())
204     && !(first_until_ > now && second_until_ > now);
205
206   bool unirhythm_b = !solo_b && !compare (&first_durations, &second_durations);
207   bool unison_b = unirhythm_b && !first_pitches.empty ()
208     &&!compare (&first_pitches, &second_pitches);
209   bool unisilence_b = unirhythm_b && first_pitches.empty ();
210
211   Translator_group * fd = fir->find_create_translator_l (p->what_str_, "one");
212   Translator_group * sd = sir->find_create_translator_l (p->what_str_, "two");
213
214   bool split_interval_b = false;
215   if (gh_number_p (interval))
216     {
217       SCM s = fd->get_property (ly_symbol2scm ("splitInterval"));
218       int i = gh_scm2int (interval);
219       if (gh_pair_p (s)
220           && gh_number_p (gh_car (s))
221           && gh_number_p (gh_cdr (s))
222           && i >= gh_scm2int (gh_car (s))
223           && i <= gh_scm2int (gh_cdr (s)))
224         split_interval_b = true;
225     }
226
227   bool combined_b = first_iter_p_->report_to_l ()->daddy_trans_l_
228     == second_iter_p_->report_to_l ()->daddy_trans_l_;
229
230   String to_id =  combined_b ? "one" : "two";
231   if ((!unirhythm_b && combined_b)
232       || (split_interval_b && combined_b)
233       || (solo_b && combined_b)
234       /*|| (unisilence_b && combined_b) */
235       || ((unirhythm_b || unison_b || unisilence_b)
236           && !combined_b && !split_interval_b && !solo_b))
237     {
238       combined_b = !combined_b;
239       to_id =  combined_b ? "one" : "two";
240       change_to (second_iter_p_, p->what_str_, to_id);
241     }
242
243   if (!combined_b)
244     sir = second_iter_p_->report_to_l ();
245
246   SCM b = unirhythm_b ? SCM_BOOL_T : SCM_BOOL_F;
247   fd->set_property ("unirhythm", b);
248   sd->set_property ("unirhythm", b);
249
250   b = split_interval_b ? SCM_BOOL_T : SCM_BOOL_F;
251   fd->set_property ("split-interval", b);
252   sd->set_property ("split-interval",  b);
253
254   b = unisilence_b ? SCM_BOOL_T : SCM_BOOL_F;
255   fd->set_property ("unisilence", b);
256   sd->set_property ("unisilence", b);
257
258   b = unison_b ? SCM_BOOL_T : SCM_BOOL_F;
259   fd->set_property ("unison", b);
260   sd->set_property ("unison", b);
261
262   b = solo_b  ? SCM_BOOL_T : SCM_BOOL_F;
263   if (first_pitches.size ())
264     {
265       fd->set_property ("solo", b);
266       sd->set_property ("solo", SCM_BOOL_F);
267     }
268
269   if (second_pitches.size ())
270     {
271       fd->set_property ("solo", SCM_BOOL_F);
272       sd->set_property ("solo", b);
273     }
274
275   if (first_iter_p_->ok ())
276     first_iter_p_->process (m);
277   
278   if (second_iter_p_->ok ())
279     second_iter_p_->process (m);
280 }
281
282 Music_iterator*
283 Part_combine_music_iterator::try_music_in_children (Music *m) const
284 {
285   Music_iterator * i =  first_iter_p_->try_music (m);
286   if (i)
287     return i;
288   else
289     return second_iter_p_->try_music (m);
290 }
291
292
293 SCM
294 Part_combine_music_iterator::get_music (Moment m)const
295 {
296   SCM s = SCM_EOL;
297   if (first_iter_p_)
298     s = gh_append2 (s,first_iter_p_->get_music (m));
299   if (second_iter_p_)
300     s = gh_append2 (second_iter_p_->get_music (m),s);
301   return s;
302 }