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