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