]> git.donarmstrong.com Git - lilypond.git/blob - lily/quote-iterator.cc
dead1d503e25466cf3e6dbe541f74662a11e244f
[lilypond.git] / lily / quote-iterator.cc
1 /*
2   quote-iterator.cc -- implement Quote_iterator
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2004--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "music-wrapper-iterator.hh"
10
11 #include "context.hh"
12 #include "dispatcher.hh"
13 #include "input.hh"
14 #include "international.hh"
15 #include "lily-guile.hh"
16 #include "music-sequence.hh"
17 #include "music.hh"
18 #include "stream-event.hh"
19 #include "warn.hh"
20
21 class Quote_iterator : public Music_wrapper_iterator
22 {
23 public:
24   Quote_iterator ();
25   Moment vector_moment (int idx) const;
26   Context_handle quote_outlet_;
27
28   Moment start_moment_;
29   Moment stop_moment_;
30   SCM event_vector_;
31   int event_idx_;
32   int end_idx_;
33   
34   SCM transposed_musics_;
35
36   DECLARE_SCHEME_CALLBACK (constructor, ());
37   bool quote_ok () const;
38   bool accept_music_type (Stream_event *) const;
39
40 protected:
41   virtual void derived_mark () const;
42   virtual void construct_children ();
43   virtual Moment pending_moment () const;
44   virtual void process (Moment);
45   virtual void do_quit ();
46   virtual bool ok () const;
47 };
48
49 void
50 Quote_iterator::do_quit ()
51 {
52   Music_wrapper_iterator::do_quit ();
53   quote_outlet_.set_context (0);
54 }
55
56 bool
57 Quote_iterator::accept_music_type (Stream_event *ev) const
58 {
59   for (SCM accept = get_outlet ()->get_property ("quotedEventTypes");
60        scm_is_pair (accept); accept = scm_cdr (accept))
61     {
62       if (ev->internal_in_event_class (scm_car (accept)))
63         return true;
64     }
65   return false;
66 }
67
68 void
69 Quote_iterator::derived_mark () const
70 {
71   Music_wrapper_iterator::derived_mark ();
72   scm_gc_mark (transposed_musics_);
73 }
74
75 Quote_iterator::Quote_iterator ()
76 {
77   transposed_musics_ = SCM_EOL;
78   event_vector_ = SCM_EOL;
79   event_idx_ = 0;
80   end_idx_ = 0;
81 }
82
83 int
84 binsearch_scm_vector (SCM vec, SCM key, bool (*is_less) (SCM a, SCM b))
85 {
86   int lo = 0;
87   int hi = scm_c_vector_length (vec);
88
89   /* binary search */
90   do
91     {
92       int cmp = (lo + hi) / 2;
93
94       SCM when = scm_caar (scm_c_vector_ref (vec, cmp));
95       bool result = (*is_less) (key, when);
96       if (result)
97         hi = cmp;
98       else
99         lo = cmp;
100     }
101   while (hi - lo > 1);
102
103   return lo;
104 }
105
106 void
107 Quote_iterator::construct_children ()
108 {
109   Music_wrapper_iterator::construct_children ();
110       
111   SCM name = get_music ()->get_property ("quoted-context-type");
112   SCM id = get_music ()->get_property ("quoted-context-id");
113
114   if (scm_is_string (id)
115       && scm_is_symbol (name))
116     {
117       Context *cue_context = get_outlet ()->find_create_context (name,
118                                                                  ly_scm2string (id), SCM_EOL);
119       quote_outlet_.set_context (cue_context);
120     }
121   else
122     quote_outlet_.set_context (get_outlet ());
123
124   event_vector_ = get_music ()->get_property ("quoted-events");
125
126   /*
127     We have to delay initting event_idx_ , since we have to
128     take starting grace notes into account. Those may offset
129     event_idx_.
130   */
131   event_idx_ = -1;
132 }
133
134 bool
135 Quote_iterator::ok () const
136 {
137   return
138     Music_wrapper_iterator::ok ()
139     || quote_ok ();
140 }
141
142 bool
143 Quote_iterator::quote_ok () const
144 {
145   return (event_idx_ >= 0
146           && scm_is_vector (event_vector_)
147           && event_idx_ <= end_idx_
148
149           /*
150             Don't quote the grace notes leading to an unquoted note.
151           */
152           && vector_moment (event_idx_).main_part_ < stop_moment_.main_part_);
153 }
154
155 Moment
156 Quote_iterator::pending_moment () const
157 {
158   Rational infty;
159   infty.set_infinite (1);
160   Moment m (infty);
161
162   if (Music_wrapper_iterator::ok ())
163     m = min (m, Music_wrapper_iterator::pending_moment ());
164
165   /*
166     In case event_idx_ < 0, we're not initted yet, and the wrapped
167     music expression determines the starting moment.
168   */
169   if (quote_ok ())
170     m = min (m, vector_moment (event_idx_) - start_moment_);
171
172   return m;
173 }
174
175 Moment
176 Quote_iterator::vector_moment (int idx) const
177 {
178   SCM entry = scm_c_vector_ref (event_vector_, idx);
179   return *unsmob_moment (scm_caar (entry));
180 }
181
182 void
183 Quote_iterator::process (Moment m)
184 {
185   if (Music_wrapper_iterator::ok ())
186     Music_wrapper_iterator::process (m);
187
188   if (!scm_is_vector (event_vector_))
189     return;
190
191   if (event_idx_ < 0)
192     {
193       event_idx_ = binsearch_scm_vector (event_vector_,
194                                          get_outlet ()->now_mom ().smobbed_copy (),
195                                          &moment_less);
196       start_moment_ = get_outlet ()->now_mom () - music_start_mom ();
197       stop_moment_ = start_moment_ + get_music ()->get_length ();
198
199       end_idx_ = binsearch_scm_vector (event_vector_,
200                                        stop_moment_.smobbed_copy (),
201                                        &moment_less);
202     }
203
204   m += start_moment_;
205   while (event_idx_ <= end_idx_)
206     {
207       Moment em = vector_moment (event_idx_);
208       if (em > m)
209         return;
210
211       if (em == m)
212         break;
213
214       event_idx_++;
215     }
216
217   if (quote_ok ())
218     {
219       SCM entry = scm_c_vector_ref (event_vector_, event_idx_);
220       Pitch *quote_pitch = unsmob_pitch (scm_cdar (entry));
221
222       /*
223         The pitch that sounds like central C
224       */
225       Pitch *me_pitch = unsmob_pitch (get_music ()->get_property ("quoted-transposition"));
226       if (!me_pitch)
227         me_pitch = unsmob_pitch (get_outlet ()->get_property ("instrumentTransposition"));
228
229       for (SCM s = scm_cdr (entry); scm_is_pair (s); s = scm_cdr (s))
230         {
231           SCM ev_acc = scm_car (s);
232
233           Stream_event *ev = unsmob_stream_event (scm_car (ev_acc));
234           if (!ev)
235             programming_error ("no music found in quote");
236           else if (accept_music_type (ev))
237             {
238               /* create a transposed copy if necessary */
239               if (quote_pitch || me_pitch)
240                 {
241                   Pitch qp, mp;
242                   if (quote_pitch)
243                     qp = *quote_pitch;
244                   if (me_pitch)
245                     mp = *me_pitch;
246
247                   Pitch diff = pitch_interval (qp, mp);
248
249                   SCM props =  transpose_mutable (ev->get_property_alist (true), diff);
250                   ev = new Stream_event (ev->get_property ("class"), props);
251                   transposed_musics_ = scm_cons (ev->unprotect (), transposed_musics_);
252                 }
253               quote_outlet_.get_outlet ()->event_source ()->broadcast (ev);
254             }
255         }
256
257       event_idx_++;
258     }
259 }
260
261 IMPLEMENT_CTOR_CALLBACK (Quote_iterator);