]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob-pq-engraver.cc
31584e46b40be7249cfd7bd747cece150e631edd
[lilypond.git] / lily / grob-pq-engraver.cc
1 /*
2   grob-pq-engraver.cc -- implement Grob_pq_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2001--2007  Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "context.hh"
10 #include "engraver.hh"
11 #include "grob.hh"
12 #include "warn.hh"
13
14 struct Grob_pq_entry
15 {
16   Grob *grob_;
17   Moment end_;
18 };
19
20 bool
21 operator< (Grob_pq_entry const &a, Grob_pq_entry const &b)
22 {
23   return a.end_ < b.end_;
24 }
25
26 class Grob_pq_engraver : public Engraver
27 {
28 public:
29   TRANSLATOR_DECLARATIONS (Grob_pq_engraver);
30 protected:
31   virtual void initialize ();
32   DECLARE_ACKNOWLEDGER (grob);
33   void start_translation_timestep ();
34   void stop_translation_timestep ();
35   void process_acknowledged ();
36   
37   vector<Grob_pq_entry> started_now_;
38 };
39
40 Grob_pq_engraver::Grob_pq_engraver ()
41 {
42 }
43
44 void
45 Grob_pq_engraver::initialize ()
46 {
47   context ()->set_property ("busyGrobs", SCM_EOL);
48 }
49
50 LY_DEFINE (ly_grob_pq_less_p, "ly:grob-pq<?",
51            2, 0, 0, (SCM a, SCM b),
52            "Compare two grob priority queue entries."
53            "  This is an internal function.")
54 {
55   if (Moment::compare (*unsmob_moment (scm_car (a)),
56                        *unsmob_moment (scm_car (b))) < 0)
57     return SCM_BOOL_T;
58   else
59     return SCM_BOOL_F;
60 }
61
62 void
63 Grob_pq_engraver::acknowledge_grob (Grob_info gi)
64 {
65   Stream_event *ev = gi.event_cause ();
66
67   if (ev
68       && !gi.grob ()->internal_has_interface (ly_symbol2scm ("multi-measure-interface")))
69     {
70       Moment n = now_mom ();
71       Moment l = get_event_length (ev, n);
72
73       if (!l.to_bool ())
74         return;
75
76       Moment end = n + l;
77
78       Grob_pq_entry e;
79       e.grob_ = gi.grob ();
80       e.end_ = end;
81
82       started_now_.push_back (e);
83     }
84 }
85
86 void
87 Grob_pq_engraver::process_acknowledged ()
88 {
89   vector_sort (started_now_, less<Grob_pq_entry> ());
90   SCM lst = SCM_EOL;
91   SCM *tail = &lst;
92   for (vsize i = 0; i < started_now_.size (); i++)
93     {
94       *tail = scm_acons (started_now_[i].end_.smobbed_copy (),
95                          started_now_[i].grob_->self_scm (),
96                          SCM_EOL);
97       tail = SCM_CDRLOC (*tail);
98     }
99
100   SCM busy = get_property ("busyGrobs");
101   busy = scm_merge_x (lst, busy, ly_grob_pq_less_p_proc);
102   context ()->set_property ("busyGrobs", busy);
103
104   started_now_.clear ();
105 }
106
107 void
108 Grob_pq_engraver::stop_translation_timestep ()
109 {
110   Moment now = now_mom ();
111   SCM start_busy = get_property ("busyGrobs");
112   SCM busy = start_busy;
113   while (scm_is_pair (busy) && *unsmob_moment (scm_caar (busy)) == now)
114     busy = scm_cdr (busy);
115
116 }
117
118 void
119 Grob_pq_engraver::start_translation_timestep ()
120 {
121   Moment now = now_mom ();
122
123   SCM start_busy = get_property ("busyGrobs");
124   SCM busy = start_busy;
125   while (scm_is_pair (busy) && *unsmob_moment (scm_caar (busy)) < now)
126     {
127       /*
128         The grob-pq-engraver is not water tight, and stuff like
129         tupletSpannerDuration confuses it.
130       */
131       busy = scm_cdr (busy);
132     }
133
134   if (start_busy != busy)
135     context ()->set_property ("busyGrobs", busy);
136 }
137
138 #include "translator.icc"
139 ADD_ACKNOWLEDGER (Grob_pq_engraver, grob);
140 ADD_TRANSLATOR (Grob_pq_engraver,
141                 /* doc */
142                 "Administrate when certain grobs (e.g., note heads) stop"
143                 " playing.",
144
145                 /* create */
146                 "",
147
148                 /* read */
149                 "busyGrobs ",
150
151                 /* write */
152                 "busyGrobs "
153                 );