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