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