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