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