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