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