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