1 /*
2 This file is part of LilyPond, the GNU music typesetter.
3
4 Copyright (C) 1998--2021 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-sequence.hh"
21
22 #include "ly-smob-list.hh"
23 #include "warn.hh"
24 #include "program-option.hh"
25 #include "duration.hh"
26 #include "moment.hh"
27 #include "music.hh"
28 #include "input.hh"
29
30 void
transpose_music_list(SCM lst,Pitch rq)31 transpose_music_list (SCM lst, Pitch rq)
32 {
33 for (auto *p : as_ly_smob_list<Prob> (lst))
34 if (p)
35 p->transpose (rq);
36 }
37
38 Moment
cumulative_length(SCM l)39 Music_sequence::cumulative_length (SCM l)
40 {
41 Moment length;
42
43 for (auto *mus : as_ly_smob_list<const Music> (l))
44 {
45 if (!mus)
46 programming_error ("Music sequence should have music elements");
47 else
48 length += mus->get_length ();
49 }
50
51 return length;
52 }
53
54 Moment
maximum_length(SCM l)55 Music_sequence::maximum_length (SCM l)
56 {
57 Moment dur = 0;
58 bool definite = false;
59 bool indefinite = false;
60
61 for (auto *m : as_ly_smob_list<const Music> (l))
62 {
63 if (!m)
64 {
65 programming_error ("Music sequence should have music elements");
66 definite = true; // damage control, hopefully
67 }
68 else
69 {
70 const auto &len = m->get_length ();
71 if (len < Moment::infinity ())
72 {
73 definite = true;
74 dur = std::max (dur, len);
75 }
76 else
77 {
78 indefinite = true;
79 }
80 }
81 }
82
83 // An empty set has zero length. In a set with mixed definite- and
84 // indefinite-length music, we expect that the indefinite-length music is
85 // dependent on the definite-length music, so we ignore the indefinite-length
86 // music. Therefore, we only propagate an indefinite length for a non-empty
87 // set where the length of every element is indefinite.
88 return (definite || !indefinite) ? dur : Moment::infinity ();
89 }
90
91 MAKE_SCHEME_CALLBACK (Music_sequence, maximum_length_callback, 1);
92 SCM
maximum_length_callback(SCM m)93 Music_sequence::maximum_length_callback (SCM m)
94 {
95 Music *me = unsmob<Music> (m);
96 return maximum_length (get_property (me, "elements")).smobbed_copy ();
97 }
98
99 MAKE_SCHEME_CALLBACK (Music_sequence, event_chord_length_callback, 1);
100 SCM
event_chord_length_callback(SCM m)101 Music_sequence::event_chord_length_callback (SCM m)
102 {
103 Music *me = unsmob<Music> (m);
104 Duration *d = unsmob<Duration> (get_property (me, "duration"));
105 // Preset duration is used in chord repetitions.
106 if (d)
107 {
108 Moment mom (d->get_length ());
109 return mom.smobbed_copy ();
110 }
111 return maximum_length (get_property (me, "elements")).smobbed_copy ();
112 }
113
114 MAKE_SCHEME_CALLBACK (Music_sequence, cumulative_length_callback, 1);
115 SCM
cumulative_length_callback(SCM m)116 Music_sequence::cumulative_length_callback (SCM m)
117 {
118 Music *me = unsmob<Music> (m);
119 return cumulative_length (get_property (me, "elements")).smobbed_copy ();
120 }
121
122 MAKE_SCHEME_CALLBACK (Music_sequence, minimum_start_callback, 1);
123 SCM
minimum_start_callback(SCM m)124 Music_sequence::minimum_start_callback (SCM m)
125 {
126 Music *me = unsmob<Music> (m);
127 return minimum_start (get_property (me, "elements")).smobbed_copy ();
128 }
129
130 MAKE_SCHEME_CALLBACK (Music_sequence, first_start_callback, 1);
131 SCM
first_start_callback(SCM m)132 Music_sequence::first_start_callback (SCM m)
133 {
134 Music *me = unsmob<Music> (m);
135 return first_start (get_property (me, "elements")).smobbed_copy ();
136 }
137
138 Pitch
music_list_to_relative(SCM l,Pitch p,bool ret_first)139 music_list_to_relative (SCM l, Pitch p, bool ret_first)
140 {
141 Pitch first = p;
142 int count = 0;
143
144 Pitch last = p;
145 for (auto *m : as_ly_smob_list<Music> (l))
146 {
147 if (m)
148 {
149 last = m->to_relative_octave (last);
150 if (!count++)
151 first = last;
152 }
153 }
154
155 return (ret_first) ? first : last;
156 }
157
158 Moment
minimum_start(SCM l)159 Music_sequence::minimum_start (SCM l)
160 {
161 Moment m;
162
163 for (auto *mus : as_ly_smob_list<const Music> (l))
164 {
165 if (!mus)
166 programming_error ("Music sequence should have music elements");
167 else
168 m = std::min (m, mus->start_mom ());
169 }
170 return m;
171 }
172
173 Moment
first_start(SCM l)174 Music_sequence::first_start (SCM l)
175 {
176 Moment accum;
177
178 // Accumulate grace time until finding the first element with non-grace time.
179 for (auto *mus : as_ly_smob_list<const Music> (l))
180 {
181 if (!mus)
182 {
183 programming_error ("Music sequence should have music elements");
184 break;
185 }
186
187 accum.grace_part_ += mus->start_mom ().grace_part_;
188 if (mus->get_length ())
189 break;
190 }
191
192 return accum;
193 }
194
195 MAKE_SCHEME_CALLBACK (Music_sequence, simultaneous_relative_callback, 2);
196 SCM
simultaneous_relative_callback(SCM music,SCM pitch)197 Music_sequence::simultaneous_relative_callback (SCM music, SCM pitch)
198 {
199 Music *me = unsmob<Music> (music);
200 Pitch p = *unsmob<Pitch> (pitch);
201 return music_list_to_relative (get_property (me, "elements"),
202 p, false).smobbed_copy ();
203 }
204
205 MAKE_SCHEME_CALLBACK (Music_sequence, event_chord_relative_callback, 2);
206 SCM
event_chord_relative_callback(SCM music,SCM pitch)207 Music_sequence::event_chord_relative_callback (SCM music, SCM pitch)
208 {
209 Music *me = unsmob<Music> (music);
210 Pitch p = *unsmob<Pitch> (pitch);
211 return music_list_to_relative (get_property (me, "elements"),
212 p, true).smobbed_copy ();
213 }
214