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