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