]> git.donarmstrong.com Git - lilypond.git/blob - lily/part-combine-iterator.cc
* configure.in: Test for and accept lmodern if EC fonts not found.
[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
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     APART,
45     TOGETHER,
46     SOLO1,
47     SOLO2,
48     UNISONO,
49     UNISILENCE,
50   };
51   Status state_;
52   Status playing_state_;
53
54   /*
55     Should be SOLO1 or SOLO2
56    */
57   Status last_playing_;
58
59   /*
60     TODO: this is getting of hand... 
61    */
62   Interpretation_context_handle one_;
63   Interpretation_context_handle two_;
64   Interpretation_context_handle null_;
65   Interpretation_context_handle shared_;
66   Interpretation_context_handle solo_;
67   
68   void substitute_both (Context * to1,
69                         Context * to2);
70
71   void kill_mmrest (Context *);
72   void chords_together ();
73   void solo1 ();
74   void solo2 ();
75   void apart (bool silent);
76   void unisono (bool silent);
77 };
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
165 void
166 Part_combine_iterator::kill_mmrest (Context * tg)
167 {
168   static Music * mmrest;
169   if (!mmrest)
170     {
171       mmrest = make_music_by_name (ly_symbol2scm ("MultiMeasureRestEvent"));
172       mmrest->set_property ("duration", SCM_EOL);
173     }
174
175   tg->try_music (mmrest);
176 }
177
178 void
179 Part_combine_iterator::solo1 ()
180 {
181   if (state_ == SOLO1)
182     return;
183   else
184     {
185       state_ = SOLO1;
186       substitute_both (solo_.get_outlet (),
187                        null_.get_outlet ());
188       
189       kill_mmrest (two_.get_outlet ());
190       kill_mmrest (shared_.get_outlet ());
191
192       if (playing_state_ != SOLO1)
193         {
194           static Music* event;
195           if (!event)
196             event = make_music_by_name (ly_symbol2scm ("SoloOneEvent"));
197
198           first_iter_-> try_music_in_children (event);
199         }
200       playing_state_ = SOLO1;
201     }
202 }
203
204 void
205 Part_combine_iterator::substitute_both (Context * to1,
206                                         Context * to2)
207 {
208   Context *tos[]  = {to1,to2};
209   Music_iterator *mis[] = {first_iter_, second_iter_}; 
210   Interpretation_context_handle *hs[] = {
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
233 void
234 Part_combine_iterator::unisono (bool silent)
235 {
236   Status newstate = (silent) ? UNISILENCE : UNISONO;
237   
238   if (newstate == state_)
239     return; 
240   else
241     {
242       /*
243         If we're coming from SOLO2 state, we might have kill mmrests
244         in the 1st voice, so in that case, we use the second voice 
245         as a basis for events.
246        */
247       Context *c1 = (last_playing_ == SOLO2) ? null_.get_outlet() : shared_.get_outlet();
248       Context *c2 = (last_playing_ == SOLO2) ? shared_.get_outlet() : null_.get_outlet();
249       substitute_both (c1, c2);
250       kill_mmrest ((last_playing_ == SOLO2)
251                    ? one_.get_outlet () : two_.get_outlet ());
252       kill_mmrest (shared_.get_outlet ());
253
254       if (playing_state_ != UNISONO
255           && newstate == UNISONO)
256         {
257           static Music* event;
258           if (!event)
259             event = make_music_by_name (ly_symbol2scm ("UnisonoEvent"));
260
261           (last_playing_ == SOLO2 ? second_iter_ : first_iter_)
262             ->try_music_in_children (event);      
263           playing_state_ = UNISONO;
264         }
265       state_ = newstate;
266     }
267 }
268
269 void
270 Part_combine_iterator::solo2 ()
271 {
272   if (state_ == SOLO2)
273     return;
274   else
275     {
276       state_ = SOLO2;
277       
278       substitute_both (null_.get_outlet (), solo_.get_outlet ());
279       
280       if (playing_state_ != SOLO2)
281         {
282           static Music* event;
283           if (!event)
284             event = make_music_by_name (ly_symbol2scm ("SoloTwoEvent"));
285
286           second_iter_-> try_music_in_children (event);
287           playing_state_ = SOLO2;
288         }
289     }
290 }
291
292 void
293 Part_combine_iterator::apart (bool silent)
294 {
295   if (!silent)
296     playing_state_ = APART;
297
298   if (state_ == APART)
299     return;
300   else
301     {
302       state_ = APART;
303       substitute_both (one_.get_outlet (), two_.get_outlet ());
304     }
305 }
306
307
308 void
309 Part_combine_iterator::construct_children ()
310 {
311   start_moment_ = get_outlet ()->now_mom ();
312   split_list_ =  get_music ()->get_property ("split-list");
313   SCM lst =  get_music ()->get_property ("elements");
314
315   SCM props = scm_list_n (/*
316                             used to have tweaks here.
317                            */
318                           
319                           SCM_UNDEFINED);
320
321   Context *tr
322     =  get_outlet ()->find_create_context (ly_symbol2scm ("Voice"),
323                                              "shared",props);
324
325   shared_.set_context (tr);
326
327   /*
328     If we don't, we get a new staff for every Voice.
329    */
330   set_context (tr);
331
332   Context *solo_tr
333     =  get_outlet ()->find_create_context (ly_symbol2scm ("Voice"),
334                                               "solo",props);
335
336   solo_ .set_context (solo_tr);
337
338   Context *null
339     =  get_outlet ()->find_create_context (ly_symbol2scm ("Devnull"),
340                                              "", SCM_EOL);
341
342   if (!null)
343     programming_error ("No Devnull found?");
344   
345   null_.set_context (null);
346
347   Context *one = tr->find_create_context (ly_symbol2scm ("Voice"),
348                                                       "one", props);
349
350   one_.set_context (one);
351
352   set_context (one);
353   first_iter_ = unsmob_iterator (get_iterator (unsmob_music (scm_car (lst))));
354
355
356   Context *two = tr->find_create_context (ly_symbol2scm ("Voice"),
357                                                       "two", props);
358   two_.set_context (two);
359   set_context (two);
360   second_iter_ = unsmob_iterator (get_iterator (unsmob_music (scm_cadr (lst))));
361
362
363   set_context (tr);
364
365
366   char const * syms[] = {
367     "Stem",
368     "DynamicLineSpanner",
369     "Tie",
370     "Dots",
371     "Rest",
372     "Slur",
373     "TextScript",
374     "Script",
375     0
376   };
377   
378   for (char const**p = syms; *p; p++)
379     {
380       SCM sym = ly_symbol2scm (*p);
381       execute_pushpop_property (one, sym,
382                                      ly_symbol2scm ("direction"), scm_int2num (1));
383
384       execute_pushpop_property (two, sym,
385                                 ly_symbol2scm ("direction"), scm_int2num (-1));
386     }
387
388 }
389
390 void
391 Part_combine_iterator::process (Moment m)
392 {
393   Moment now = get_outlet ()->now_mom ();
394   Moment *splitm = 0;
395   
396   for (; scm_is_pair (split_list_); split_list_ = scm_cdr (split_list_))
397     {
398       splitm = unsmob_moment (scm_caar (split_list_));
399       if (splitm && *splitm + start_moment_ > now)
400         break ;
401
402       SCM tag = scm_cdar (split_list_);
403       
404       if (tag == ly_symbol2scm ("chords"))
405         chords_together ();
406       else if (tag == ly_symbol2scm ("apart")
407                || tag == ly_symbol2scm ("apart-silence")
408                || tag == ly_symbol2scm ("apart-spanner"))
409         apart (tag == ly_symbol2scm ("apart-silence"));
410       else if (tag == ly_symbol2scm ("unisono"))
411         unisono (false);
412       else if (tag == ly_symbol2scm ("unisilence"))
413         unisono (true);
414       else if (tag == ly_symbol2scm ("solo1"))
415         solo1 ();
416       else if (tag == ly_symbol2scm ("solo2"))
417         solo2 ();
418       else if (scm_is_symbol (tag))
419         {
420           String s =  "Unknown split directive: "
421             + (scm_is_symbol (tag) ? ly_symbol2string (tag) : String ("not a symbol")); 
422           programming_error (s);
423         }
424     }
425
426   if (first_iter_->ok ())
427     {
428       first_iter_->process (m);
429       if (first_iter_->try_music_in_children (busy_playing_event))
430         last_playing_ = SOLO1;
431     }
432   
433   if (second_iter_->ok ())
434     {
435       second_iter_->process (m);
436       if (first_iter_->try_music_in_children (busy_playing_event))
437         last_playing_ = SOLO2;
438     }
439 }
440
441 Music_iterator*
442 Part_combine_iterator::try_music_in_children (Music *m) const
443 {
444   Music_iterator * i =  first_iter_->try_music (m);
445   if (i)
446     return i;
447   else
448     return second_iter_->try_music (m);
449 }
450
451 IMPLEMENT_CTOR_CALLBACK (Part_combine_iterator);