]> git.donarmstrong.com Git - lilypond.git/blob - lily/music.cc
0524cbfbb612616cce59c722f016f327b456642c
[lilypond.git] / lily / music.cc
1 /*
2   music.cc -- implement Music
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2005 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "music.hh"
10 #include "music-sequence.hh"
11 #include "duration.hh"
12 #include "input-smob.hh"
13 #include "ly-smobs.icc"
14 #include "main.hh"
15 #include "pitch.hh"
16 #include "score.hh"
17 #include "warn.hh"
18
19 /*
20   Music is anything that has duration and supports both time compression
21   and transposition.
22
23   In Lily, everything that can be thought to have a length and a pitch
24   (which has a duration which can be transposed) is considered "music",
25 */
26 bool
27 Music::internal_is_music_type (SCM k) const
28 {
29   SCM ifs = get_property ("types");
30
31   return scm_c_memq (k, ifs) != SCM_BOOL_F;
32 }
33
34 String
35 Music::name () const
36 {
37   SCM nm = get_property ("name");
38   if (scm_is_symbol (nm))
39     return ly_symbol2string (nm);
40   else
41     return "Music";
42 }
43
44 Music::Music (SCM init)
45 {
46   self_scm_ = SCM_EOL;
47   immutable_property_alist_ = init;
48   mutable_property_alist_ = SCM_EOL;
49   smobify_self ();
50
51   length_callback_ = get_property ("length-callback");
52   if (!ly_is_procedure (length_callback_))
53     length_callback_ = duration_length_callback_proc;
54
55   start_callback_ = get_property ("start-callback");
56 }
57
58 Music::Music (Music const &m)
59 {
60   immutable_property_alist_ = m.immutable_property_alist_;
61   mutable_property_alist_ = SCM_EOL;
62   self_scm_ = SCM_EOL;
63
64   /* First we smobify_self, then we copy over the stuff.  If we don't,
65      stack vars that hold the copy might be optimized away, meaning
66      that they won't be protected from GC. */
67   smobify_self ();
68   mutable_property_alist_ = ly_music_deep_copy (m.mutable_property_alist_);
69   length_callback_ = m.length_callback_;
70   start_callback_ = m.start_callback_;
71   set_spot (*m.origin ());
72 }
73
74 Music::~Music ()
75 {
76 }
77
78 SCM
79 Music::get_property_alist (bool m) const
80 {
81   return (m) ? mutable_property_alist_ : immutable_property_alist_;
82 }
83
84 SCM
85 Music::mark_smob (SCM m)
86 {
87   Music *mus = (Music *) SCM_CELL_WORD_1 (m);
88   scm_gc_mark (mus->immutable_property_alist_);
89   return mus->mutable_property_alist_;
90 }
91
92 Moment
93 Music::get_length () const
94 {
95   SCM lst = get_property ("length");
96   if (unsmob_moment (lst))
97     return *unsmob_moment (lst);
98
99   if (ly_is_procedure (length_callback_))
100     {
101       SCM res = scm_call_1 (length_callback_, self_scm ());
102       return *unsmob_moment (res);
103     }
104
105   return Moment (0);
106 }
107
108 Moment
109 Music::start_mom () const
110 {
111   SCM lst = start_callback_;
112   if (ly_is_procedure (lst))
113     {
114       SCM res = scm_call_1 (lst, self_scm ());
115       return *unsmob_moment (res);
116     }
117
118   Moment m;
119   return m;
120 }
121
122 void
123 print_alist (SCM a, SCM port)
124 {
125   /* SCM_EOL  -> catch malformed lists.  */
126   for (SCM s = a; scm_is_pair (s); s = scm_cdr (s))
127     {
128       scm_display (scm_caar (s), port);
129       scm_puts (" = ", port);
130       scm_write (scm_cdar (s), port);
131       scm_puts ("\n", port);
132     }
133 }
134
135 int
136 Music::print_smob (SCM s, SCM p, scm_print_state*)
137 {
138   scm_puts ("#<Music ", p);
139   Music *m = unsmob_music (s);
140
141   SCM nm = m->get_property ("name");
142   if (scm_is_symbol (nm) || scm_is_string (nm))
143     scm_display (nm, p);
144   else
145     scm_puts ("Music", p);
146
147   /* Printing properties takes a lot of time, especially during backtraces.
148      For inspecting, it is better to explicitly use an inspection
149      function.  */
150
151   scm_puts (">", p);
152   return 1;
153 }
154
155 Pitch
156 Music::generic_to_relative_octave (Pitch last)
157 {
158   SCM elt = get_property ("element");
159   Pitch *old_pit = unsmob_pitch (get_property ("pitch"));
160   if (old_pit)
161     {
162       Pitch new_pit = *old_pit;
163       new_pit = new_pit.to_relative_octave (last);
164
165       SCM check = get_property ("absolute-octave");
166       if (scm_is_number (check)
167           && new_pit.get_octave () != scm_to_int (check))
168         {
169           Pitch expected_pit (scm_to_int (check),
170                               new_pit.get_notename (),
171                               new_pit.get_alteration ());
172           origin ()->warning (_f ("octave check failed; expected %s, found: %s",
173                                   expected_pit.to_string (),
174                                   new_pit.to_string ()));
175           new_pit = expected_pit;
176         }
177
178       set_property ("pitch", new_pit.smobbed_copy ());
179
180       last = new_pit;
181     }
182
183   if (Music *m = unsmob_music (elt))
184     last = m->to_relative_octave (last);
185
186   last = music_list_to_relative (get_property ("elements"), last, false);
187   return last;
188 }
189
190 Pitch
191 Music::to_relative_octave (Pitch last)
192 {
193   SCM callback = get_property ("to-relative-callback");
194   if (ly_is_procedure (callback))
195     {
196       Pitch *p = unsmob_pitch (scm_call_2 (callback, self_scm (), last.smobbed_copy ()));
197       return *p;
198     }
199
200   return generic_to_relative_octave (last);
201 }
202
203 void
204 Music::compress (Moment factor)
205 {
206   SCM elt = get_property ("element");
207
208   if (Music *m = unsmob_music (elt))
209     m->compress (factor);
210
211   compress_music_list (get_property ("elements"), factor);
212   Duration *d = unsmob_duration (get_property ("duration"));
213   if (d)
214     set_property ("duration", d->compressed (factor.main_part_).smobbed_copy ());
215 }
216
217 void
218 Music::transpose (Pitch delta)
219 {
220   if (to_boolean (get_property ("untransposable")))
221     return;
222
223   for (SCM s = this->get_property_alist (true); scm_is_pair (s); s = scm_cdr (s))
224     {
225       SCM entry = scm_car (s);
226       SCM val = scm_cdr (entry);
227
228       if (Pitch *p = unsmob_pitch (val))
229         {
230           Pitch transposed = p->transposed (delta);
231           scm_set_cdr_x (entry, transposed.smobbed_copy ());
232
233           if (abs (transposed.get_alteration ()) > DOUBLE_SHARP)
234             {
235               warning (_f ("transposition by %s makes alteration larger than double",
236                            delta.to_string ()));
237             }
238         }
239     }
240
241   SCM elt = get_property ("element");
242
243   if (Music *m = unsmob_music (elt))
244     m->transpose (delta);
245
246   transpose_music_list (get_property ("elements"), delta);
247
248   /*
249     UGH - how do this more generically?
250   */
251   SCM pa = get_property ("pitch-alist");
252   if (scm_is_pair (pa))
253     set_property ("pitch-alist", ly_transpose_key_alist (pa, delta.smobbed_copy ()));
254 }
255
256 IMPLEMENT_TYPE_P (Music, "ly:music?");
257 IMPLEMENT_SMOBS (Music);
258 IMPLEMENT_DEFAULT_EQUAL_P (Music);
259
260 SCM
261 Music::internal_get_property (SCM sym) const
262 {
263   SCM s = scm_sloppy_assq (sym, mutable_property_alist_);
264   if (s != SCM_BOOL_F)
265     return scm_cdr (s);
266
267   s = scm_sloppy_assq (sym, immutable_property_alist_);
268   return (s == SCM_BOOL_F) ? SCM_EOL : scm_cdr (s);
269 }
270
271 SCM
272 Music::internal_get_object (SCM s) const
273 {
274   return internal_get_property (s);
275 }
276
277 void
278 Music::internal_set_object (SCM s, SCM v)
279 {
280   return internal_set_property (s, v);
281 }
282
283 void
284 Music::internal_set_property (SCM s, SCM v)
285 {
286   if (do_internal_type_checking_global)
287     if (!type_check_assignment (s, v, ly_symbol2scm ("music-type?")))
288       abort ();
289
290   mutable_property_alist_ = scm_assq_set_x (mutable_property_alist_, s, v);
291 }
292
293 void
294 Music::set_spot (Input ip)
295 {
296   set_property ("origin", make_input (ip));
297 }
298
299 Input *
300 Music::origin () const
301 {
302   Input *ip = unsmob_input (get_property ("origin"));
303   return ip ? ip : &dummy_input_global;
304 }
305
306 Music *
307 make_music_by_name (SCM sym)
308 {
309   SCM make_music_proc = ly_lily_module_constant ("make-music");
310   SCM rv = scm_call_1 (make_music_proc, 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 }