]> git.donarmstrong.com Git - lilypond.git/blob - lily/part-combine-iterator.cc
Update.
[lilypond.git] / lily / part-combine-iterator.cc
1 /*
2   new-part-combine-music-iterator.cc -- implement Part_combine_iterator
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2004--2005 Han-Wen Nienhuys
7 */
8
9 #include "context.hh"
10 #include "event.hh"
11 #include "music-sequence.hh"
12 #include "lily-guile.hh"
13 #include "warn.hh"
14 #include "music-iterator.hh"
15
16 class Part_combine_iterator : public Music_iterator
17 {
18 public:
19   Part_combine_iterator ();
20
21   DECLARE_SCHEME_CALLBACK (constructor, ());
22 protected:
23   virtual void derived_substitute (Context *f, Context *t);
24   virtual void derived_mark () const;
25   Part_combine_iterator (Part_combine_iterator const &);
26
27   virtual void construct_children ();
28   virtual Moment pending_moment () const;
29   virtual void do_quit ();
30   virtual void process (Moment);
31
32   virtual Music_iterator *try_music_in_children (Music *) const;
33
34   virtual bool ok () const;
35
36 private:
37   Music_iterator *first_iter_;
38   Music_iterator *second_iter_;
39   Moment start_moment_;
40
41   SCM split_list_;
42
43   enum Status
44     {
45       APART,
46       TOGETHER,
47       SOLO1,
48       SOLO2,
49       UNISONO,
50       UNISILENCE,
51     };
52   Status state_;
53   Status playing_state_;
54
55   /*
56     Should be SOLO1 or SOLO2
57   */
58   Status last_playing_;
59
60   /*
61     TODO: this is getting of hand...
62   */
63   Interpretation_context_handle one_;
64   Interpretation_context_handle two_;
65   Interpretation_context_handle null_;
66   Interpretation_context_handle shared_;
67   Interpretation_context_handle solo_;
68
69   void substitute_both (Context *to1,
70                         Context *to2);
71
72   void kill_mmrest (Context *);
73   void chords_together ();
74   void solo1 ();
75   void solo2 ();
76   void apart (bool silent);
77   void unisono (bool silent);
78 };
79
80 static Music *busy_playing_event;
81
82 void
83 Part_combine_iterator::do_quit ()
84 {
85   if (first_iter_)
86     first_iter_->quit ();
87   if (second_iter_)
88     second_iter_->quit ();
89
90   null_.set_context (0);
91   one_.set_context (0);
92   two_.set_context (0);
93   shared_.set_context (0);
94   solo_.set_context (0);
95 }
96
97 Part_combine_iterator::Part_combine_iterator ()
98 {
99   first_iter_ = 0;
100   second_iter_ = 0;
101   split_list_ = SCM_EOL;
102   state_ = APART;
103   playing_state_ = APART;
104
105   if (!busy_playing_event)
106     {
107       busy_playing_event
108         = make_music_by_name (ly_symbol2scm ("BusyPlayingEvent"));
109     }
110 }
111
112 void
113 Part_combine_iterator::derived_mark () const
114 {
115   if (first_iter_)
116     scm_gc_mark (first_iter_->self_scm ());
117   if (second_iter_)
118     scm_gc_mark (second_iter_->self_scm ());
119 }
120
121 void
122 Part_combine_iterator::derived_substitute (Context *f,
123                                            Context *t)
124 {
125   if (first_iter_)
126     first_iter_->substitute_outlet (f, t);
127 }
128
129 Moment
130 Part_combine_iterator::pending_moment () const
131 {
132   Moment p;
133   p.set_infinite (1);
134   if (first_iter_->ok ())
135     p = p <? first_iter_->pending_moment ();
136
137   if (second_iter_->ok ())
138     p = p <? second_iter_->pending_moment ();
139   return p;
140 }
141
142 bool
143 Part_combine_iterator::ok () const
144 {
145   return first_iter_->ok () || second_iter_->ok ();
146 }
147
148 void
149 Part_combine_iterator::chords_together ()
150 {
151   if (state_ == TOGETHER)
152     return;
153   else
154     {
155       playing_state_ = TOGETHER;
156       state_ = TOGETHER;
157
158       substitute_both (shared_.get_outlet (), shared_.get_outlet ());
159     }
160 }
161
162 void
163 Part_combine_iterator::kill_mmrest (Context *tg)
164 {
165   static Music *mmrest;
166   if (!mmrest)
167     {
168       mmrest = make_music_by_name (ly_symbol2scm ("MultiMeasureRestEvent"));
169       mmrest->set_property ("duration", SCM_EOL);
170     }
171
172   tg->try_music (mmrest);
173 }
174
175 void
176 Part_combine_iterator::solo1 ()
177 {
178   if (state_ == SOLO1)
179     return;
180   else
181     {
182       state_ = SOLO1;
183       substitute_both (solo_.get_outlet (),
184                        null_.get_outlet ());
185
186       kill_mmrest (two_.get_outlet ());
187       kill_mmrest (shared_.get_outlet ());
188
189       if (playing_state_ != SOLO1)
190         {
191           static Music *event;
192           if (!event)
193             event = make_music_by_name (ly_symbol2scm ("SoloOneEvent"));
194
195           first_iter_->try_music_in_children (event);
196         }
197       playing_state_ = SOLO1;
198     }
199 }
200
201 void
202 Part_combine_iterator::substitute_both (Context *to1,
203                                         Context *to2)
204 {
205   Context *tos[] = {to1, to2};
206   Music_iterator *mis[] = {first_iter_, second_iter_};
207   Interpretation_context_handle *hs[]
208     = {
209     &null_,
210     &one_, &two_,
211     &shared_, &solo_,
212     0
213   };
214
215   for (int i = 0; i < 2; i++)
216     {
217       for (int j = 0; hs[j]; j++)
218         if (hs[j]->get_outlet () != tos[i])
219           mis[i]->substitute_outlet (hs[j]->get_outlet (), tos[i]);
220     }
221
222   for (int j = 0; hs[j]; j++)
223     {
224       Context *t = hs[j]->get_outlet ();
225       if (t != to1 && t != to2)
226         kill_mmrest (t);
227     }
228 }
229
230 void
231 Part_combine_iterator::unisono (bool silent)
232 {
233   Status newstate = (silent) ? UNISILENCE : UNISONO;
234
235   if (newstate == state_)
236     return;
237   else
238     {
239       /*
240         If we're coming from SOLO2 state, we might have kill mmrests
241         in the 1st voice, so in that case, we use the second voice
242         as a basis for events.
243       */
244       Context *c1 = (last_playing_ == SOLO2) ? null_.get_outlet () : shared_.get_outlet ();
245       Context *c2 = (last_playing_ == SOLO2) ? shared_.get_outlet () : null_.get_outlet ();
246       substitute_both (c1, c2);
247       kill_mmrest ((last_playing_ == SOLO2)
248                    ? one_.get_outlet () : two_.get_outlet ());
249       kill_mmrest (shared_.get_outlet ());
250
251       if (playing_state_ != UNISONO
252           && newstate == UNISONO)
253         {
254           static Music *event;
255           if (!event)
256             event = make_music_by_name (ly_symbol2scm ("UnisonoEvent"));
257
258           (last_playing_ == SOLO2 ? second_iter_ : first_iter_)
259             ->try_music_in_children (event);
260           playing_state_ = UNISONO;
261         }
262       state_ = newstate;
263     }
264 }
265
266 void
267 Part_combine_iterator::solo2 ()
268 {
269   if (state_ == SOLO2)
270     return;
271   else
272     {
273       state_ = SOLO2;
274
275       substitute_both (null_.get_outlet (), solo_.get_outlet ());
276
277       if (playing_state_ != SOLO2)
278         {
279           static Music *event;
280           if (!event)
281             event = make_music_by_name (ly_symbol2scm ("SoloTwoEvent"));
282
283           second_iter_->try_music_in_children (event);
284           playing_state_ = SOLO2;
285         }
286     }
287 }
288
289 void
290 Part_combine_iterator::apart (bool silent)
291 {
292   if (!silent)
293     playing_state_ = APART;
294
295   if (state_ == APART)
296     return;
297   else
298     {
299       state_ = APART;
300       substitute_both (one_.get_outlet (), two_.get_outlet ());
301     }
302 }
303
304 void
305 Part_combine_iterator::construct_children ()
306 {
307   start_moment_ = get_outlet ()->now_mom ();
308   split_list_ = get_music ()->get_property ("split-list");
309   SCM lst = get_music ()->get_property ("elements");
310
311   SCM props = scm_list_n (/*
312                             used to have tweaks here.
313                           */
314
315                           SCM_UNDEFINED);
316
317   Context *tr
318     = get_outlet ()->find_create_context (ly_symbol2scm ("Voice"),
319                                           "shared", props);
320
321   shared_.set_context (tr);
322
323   /*
324     If we don't, we get a new staff for every Voice.
325   */
326   set_context (tr);
327
328   Context *solo_tr
329     = get_outlet ()->find_create_context (ly_symbol2scm ("Voice"),
330                                           "solo", props);
331
332   solo_.set_context (solo_tr);
333
334   Context *null
335     = get_outlet ()->find_create_context (ly_symbol2scm ("Devnull"),
336                                           "", SCM_EOL);
337
338   if (!null)
339     programming_error ("No Devnull found?");
340
341   null_.set_context (null);
342
343   Context *one = tr->find_create_context (ly_symbol2scm ("Voice"),
344                                           "one", props);
345
346   one_.set_context (one);
347
348   set_context (one);
349   first_iter_ = unsmob_iterator (get_iterator (unsmob_music (scm_car (lst))));
350
351   Context *two = tr->find_create_context (ly_symbol2scm ("Voice"),
352                                           "two", props);
353   two_.set_context (two);
354   set_context (two);
355   second_iter_ = unsmob_iterator (get_iterator (unsmob_music (scm_cadr (lst))));
356
357   set_context (tr);
358
359   char const *syms[]
360     = {
361     "Stem",
362     "DynamicLineSpanner",
363     "Tie",
364     "Dots",
365     "Rest",
366     "Slur",
367     "TextScript",
368     "Script",
369     0
370   };
371
372   for (char const **p = syms; *p; p++)
373     {
374       SCM sym = ly_symbol2scm (*p);
375       execute_pushpop_property (one, sym,
376                                 ly_symbol2scm ("direction"), scm_int2num (1));
377
378       execute_pushpop_property (two, sym,
379                                 ly_symbol2scm ("direction"), scm_int2num (-1));
380     }
381 }
382
383 void
384 Part_combine_iterator::process (Moment m)
385 {
386   Moment now = get_outlet ()->now_mom ();
387   Moment *splitm = 0;
388
389   for (; scm_is_pair (split_list_); split_list_ = scm_cdr (split_list_))
390     {
391       splitm = unsmob_moment (scm_caar (split_list_));
392       if (splitm && *splitm + start_moment_ > now)
393         break;
394
395       SCM tag = scm_cdar (split_list_);
396
397       if (tag == ly_symbol2scm ("chords"))
398         chords_together ();
399       else if (tag == ly_symbol2scm ("apart")
400                || tag == ly_symbol2scm ("apart-silence")
401                || tag == ly_symbol2scm ("apart-spanner"))
402         apart (tag == ly_symbol2scm ("apart-silence"));
403       else if (tag == ly_symbol2scm ("unisono"))
404         unisono (false);
405       else if (tag == ly_symbol2scm ("unisilence"))
406         unisono (true);
407       else if (tag == ly_symbol2scm ("solo1"))
408         solo1 ();
409       else if (tag == ly_symbol2scm ("solo2"))
410         solo2 ();
411       else if (scm_is_symbol (tag))
412         {
413           String s = "Unknown split directive: "
414             + (scm_is_symbol (tag) ? ly_symbol2string (tag) : String ("not a symbol"));
415           programming_error (s);
416         }
417     }
418
419   if (first_iter_->ok ())
420     {
421       first_iter_->process (m);
422       if (first_iter_->try_music_in_children (busy_playing_event))
423         last_playing_ = SOLO1;
424     }
425
426   if (second_iter_->ok ())
427     {
428       second_iter_->process (m);
429       if (first_iter_->try_music_in_children (busy_playing_event))
430         last_playing_ = SOLO2;
431     }
432 }
433
434 Music_iterator *
435 Part_combine_iterator::try_music_in_children (Music *m) const
436 {
437   Music_iterator *i = first_iter_->try_music (m);
438   if (i)
439     return i;
440   else
441     return second_iter_->try_music (m);
442 }
443
444 IMPLEMENT_CTOR_CALLBACK (Part_combine_iterator);