]> git.donarmstrong.com Git - lilypond.git/blob - lily/music.cc
Issue 5167/3: Split off `markup-lambda' from `define-markup-command'
[lilypond.git] / lily / music.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2015 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.hh"
21
22 #include "context.hh"
23 #include "dispatcher.hh"
24 #include "duration.hh"
25 #include "input.hh"
26 #include "international.hh"
27 #include "main.hh"
28 #include "music-sequence.hh"
29 #include "score.hh"
30 #include "warn.hh"
31 #include "lily-imports.hh"
32
33 /*
34   Music is anything that has (possibly zero) duration and supports
35   both time compression and transposition.
36
37   In Lily, everything that can be thought to have a length and a pitch
38   (which has a duration which can be transposed) is considered "music".
39 */
40 bool
41 Music::internal_is_music_type (SCM k) const
42 {
43   SCM ifs = get_property ("types");
44
45   return scm_is_true (scm_c_memq (k, ifs));
46 }
47
48 Preinit_Music::Preinit_Music ()
49 {
50   length_callback_ = SCM_EOL;
51   start_callback_ = SCM_EOL;
52 }
53
54 Music::Music (SCM init)
55   : Prob (ly_symbol2scm ("Music"), init)
56 {
57   length_callback_ = get_property ("length-callback");
58   if (!ly_is_procedure (length_callback_))
59     length_callback_ = duration_length_callback_proc;
60
61   start_callback_ = get_property ("start-callback");
62 }
63
64 void
65 Music::derived_mark () const
66 {
67   scm_gc_mark (length_callback_);
68   scm_gc_mark (start_callback_);
69 }
70
71 SCM
72 Music::copy_mutable_properties () const
73 {
74   return music_deep_copy (mutable_property_alist_);
75 }
76
77 void
78 Music::type_check_assignment (SCM s, SCM v) const
79 {
80   ::type_check_assignment (s, v, ly_symbol2scm ("music-type?"));
81 }
82
83 Music::Music (Music const &m)
84   : Prob (m)
85 {
86   length_callback_ = m.length_callback_;
87   start_callback_ = m.start_callback_;
88
89   /// why?
90   set_spot (*m.origin ());
91 }
92
93 Moment
94 Music::get_length () const
95 {
96   SCM lst = get_property ("length");
97   if (unsmob<Moment> (lst))
98     return *unsmob<Moment> (lst);
99
100   if (ly_is_procedure (length_callback_))
101     {
102       SCM res = scm_call_1 (length_callback_, self_scm ());
103       return *unsmob<Moment> (res);
104     }
105
106   return Moment (0);
107 }
108
109 Moment
110 Music::start_mom () const
111 {
112   SCM lst = start_callback_;
113   if (ly_is_procedure (lst))
114     {
115       SCM res = scm_call_1 (lst, self_scm ());
116       return *unsmob<Moment> (res);
117     }
118
119   Moment m;
120   return m;
121 }
122
123 void
124 print_alist (SCM a, SCM port)
125 {
126   /* SCM_EOL  -> catch malformed lists.  */
127   for (SCM s = a; scm_is_pair (s); s = scm_cdr (s))
128     {
129       scm_display (scm_caar (s), port);
130       scm_puts (" = ", port);
131       scm_write (scm_cdar (s), port);
132       scm_puts ("\n", port);
133     }
134 }
135
136 Pitch
137 Music::generic_to_relative_octave (Pitch last)
138 {
139   SCM elt = get_property ("element");
140   Pitch *old_pit = unsmob<Pitch> (get_property ("pitch"));
141   if (old_pit)
142     {
143       Pitch new_pit = *old_pit;
144       new_pit = new_pit.to_relative_octave (last);
145
146       SCM check = get_property ("absolute-octave");
147       if (scm_is_number (check)
148           && new_pit.get_octave () != scm_to_int (check))
149         {
150           Pitch expected_pit (scm_to_int (check),
151                               new_pit.get_notename (),
152                               new_pit.get_alteration ());
153           origin ()->warning (_f ("octave check failed; expected \"%s\", found: \"%s\"",
154                                   expected_pit.to_string (),
155                                   new_pit.to_string ()));
156           new_pit = expected_pit;
157         }
158
159       set_property ("pitch", new_pit.smobbed_copy ());
160
161       last = new_pit;
162     }
163
164   if (Music *m = unsmob<Music> (elt))
165     last = m->to_relative_octave (last);
166
167   (void) music_list_to_relative (get_property ("articulations"), last, true);
168   last = music_list_to_relative (get_property ("elements"), last, false);
169   return last;
170 }
171
172 Pitch
173 Music::to_relative_octave (Pitch last)
174 {
175   SCM callback = get_property ("to-relative-callback");
176   if (ly_is_procedure (callback))
177     {
178       Pitch *p = unsmob<Pitch> (scm_call_2 (callback, self_scm (),
179                                            last.smobbed_copy ()));
180       return *p;
181     }
182
183   return generic_to_relative_octave (last);
184 }
185
186 void
187 Music::compress (Moment factor)
188 {
189   SCM elt = get_property ("element");
190
191   if (Music *m = unsmob<Music> (elt))
192     m->compress (factor);
193
194   compress_music_list (get_property ("elements"), factor);
195   Duration *d = unsmob<Duration> (get_property ("duration"));
196   if (d)
197     set_property ("duration",
198                   d->compressed (factor.main_part_).smobbed_copy ());
199 }
200
201 /*
202   This mutates alist.  Hence, make sure that it is not shared
203 */
204
205 void
206 Prob::transpose (Pitch delta)
207 {
208   if (to_boolean (get_property ("untransposable")))
209     return;
210
211   for (SCM s = mutable_property_alist_; scm_is_pair (s); s = scm_cdr (s))
212     {
213       SCM entry = scm_car (s);
214       SCM prop = scm_car (entry);
215       SCM val = scm_cdr (entry);
216       SCM new_val = val;
217
218       if (Pitch *p = unsmob<Pitch> (val))
219         {
220           Pitch transposed = p->transposed (delta);
221
222           if (scm_is_eq (prop, ly_symbol2scm ("tonic")))
223             transposed = Pitch (-1, transposed.get_notename (),
224                                 transposed.get_alteration ());
225
226           new_val = transposed.smobbed_copy ();
227         }
228       else if (scm_is_eq (prop, ly_symbol2scm ("element")))
229         {
230           if (Prob *m = unsmob<Prob> (val))
231             m->transpose (delta);
232         }
233       else if (scm_is_eq (prop, ly_symbol2scm ("elements"))
234                || scm_is_eq (prop, ly_symbol2scm ("articulations")))
235         transpose_music_list (val, delta);
236       else if (scm_is_eq (prop, ly_symbol2scm ("pitch-alist"))
237                && scm_is_pair (val))
238         new_val = ly_transpose_key_alist (val, delta.smobbed_copy ());
239
240       if (!scm_is_eq (val, new_val))
241         scm_set_cdr_x (entry, new_val);
242     }
243 }
244
245 void
246 Music::set_spot (Input ip)
247 {
248   set_property ("origin", ip.smobbed_copy ());
249 }
250
251 Input *
252 Music::origin () const
253 {
254   Input *ip = unsmob<Input> (get_property ("origin"));
255   return ip ? ip : &dummy_input_global;
256 }
257
258 /*
259   ES TODO: This method should probably be reworked or junked.
260 */
261 Stream_event *
262 Music::to_event () const
263 {
264   SCM class_name = ly_camel_case_2_lisp_identifier (get_property ("name"));
265
266   // catch programming mistakes.
267   if (!internal_is_music_type (class_name))
268     programming_error ("Not a music type");
269
270   Stream_event *e = new Stream_event
271     (Lily::ly_make_event_class (class_name),
272      mutable_property_alist_);
273   Moment length = get_length ();
274   if (length.to_bool ())
275     e->set_property ("length", length.smobbed_copy ());
276
277   // articulations as events.
278   SCM art_mus = e->get_property ("articulations");
279   if (scm_is_pair (art_mus))
280     {
281       SCM art_ev = SCM_EOL;
282       for (; scm_is_pair (art_mus); art_mus = scm_cdr (art_mus))
283         {
284           Music *m = unsmob<Music> (scm_car (art_mus));
285           art_ev = scm_cons (m->to_event ()->unprotect (), art_ev);
286         }
287       e->set_property ("articulations", scm_reverse_x (art_ev, SCM_EOL));
288     }
289
290   /*
291     ES TODO: This is a temporary fix. Stream_events should not be
292     aware of music.
293   */
294   e->set_property ("music-cause", self_scm ());
295
296   return e;
297 }
298
299 void
300 Music::send_to_context (Context *c)
301 {
302   Stream_event *ev = to_event ();
303   c->event_source ()->broadcast (ev);
304   ev->unprotect ();
305 }
306
307 Music *
308 make_music_by_name (SCM sym)
309 {
310   SCM rv = Lily::make_music (sym);
311
312   /* UGH. */
313   Music *m = unsmob<Music> (rv);
314   m->protect ();
315   return m;
316 }
317
318 MAKE_SCHEME_CALLBACK (Music, duration_length_callback, 1);
319 SCM
320 Music::duration_length_callback (SCM m)
321 {
322   Music *me = unsmob<Music> (m);
323   Duration *d = unsmob<Duration> (me->get_property ("duration"));
324
325   Moment mom;
326   if (d)
327     mom = d->get_length ();
328   return mom.smobbed_copy ();
329 }
330
331 SCM
332 music_deep_copy (SCM m)
333 {
334   if (Music *mus = unsmob<Music> (m))
335       return mus->clone ()->unprotect ();
336   if (scm_is_pair (m))
337     {
338       SCM copy = SCM_EOL;
339       do
340         {
341           copy = scm_cons (music_deep_copy (scm_car (m)), copy);
342           m = scm_cdr (m);
343         }
344       while (scm_is_pair (m));
345       // Oh, come on, GUILE.  Why do you require the second argument
346       // of scm_reverse_x to be a proper list?  That makes no sense.
347       // return scm_reverse_x (copy, music_deep_copy (m));
348       SCM last_cons = copy;
349       copy = scm_reverse_x (copy, SCM_EOL);
350       scm_set_cdr_x (last_cons, music_deep_copy (m));
351       return copy;
352     }
353   return m;
354 }
355
356 void
357 set_origin (SCM m, SCM origin)
358 {
359   while (scm_is_pair (m))
360     {
361       set_origin (scm_car (m), origin);
362       m = scm_cdr (m);
363     }
364   if (Music *mus = unsmob<Music> (m))
365     mus->set_property ("origin", origin);
366 }