]> git.donarmstrong.com Git - lilypond.git/blob - lily/timing-translator.cc
8298a2b56f4ac3154ea0cb017f5ae1723d1e883a
[lilypond.git] / lily / timing-translator.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--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 "timing-translator.hh"
21
22 #include "warn.hh"
23 #include "translator-group.hh"
24 #include "global-context.hh"
25 #include "moment.hh"
26
27 void
28 Timing_translator::stop_translation_timestep ()
29 {
30   Global_context *global = get_global_context ();
31
32   if (to_boolean (get_property ("timing"))
33       && !to_boolean (get_property ("skipBars")))
34     {
35       Moment barleft = (measure_length () - measure_position (context ()));
36       Moment now = now_mom ();
37
38       if (barleft > Moment (0))
39         {
40           Moment nextmom = now + barleft;
41           nextmom.grace_part_ = Rational (0);
42           global->add_moment_to_process (nextmom);
43         }
44     }
45 }
46
47 void
48 Timing_translator::initialize ()
49 {
50   Context *timing = unsmob_context (scm_call_2 (ly_lily_module_constant ("ly:context-find"),
51                                                 context ()->self_scm (),
52                                                 ly_symbol2scm ("Timing")));
53   if (timing != context ())
54     {
55       context ()->add_alias (ly_symbol2scm ("Timing"));
56
57       if (!timing)
58         {
59           programming_error ("Can't find Timing context template");
60           timing = context ();
61         }
62     }
63
64   SCM barnumber = timing->get_property ("currentBarNumber");
65   if (!scm_is_integer (barnumber))
66     barnumber = scm_from_int (1);
67   context ()->set_property ("currentBarNumber", barnumber);
68   context ()->set_property ("internalBarNumber", barnumber);
69
70   SCM timeSignatureFraction = timing->get_property ("timeSignatureFraction");
71
72   if (!scm_is_pair (timeSignatureFraction))
73     {
74       programming_error ("missing timeSignatureFraction");
75       timeSignatureFraction = scm_cons (scm_from_int (4), scm_from_int (4));
76     }
77   context ()->set_property ("timeSignatureFraction", timeSignatureFraction);
78
79   SCM measureLength = timing->get_property ("measureLength");
80
81   if (!unsmob_moment (measureLength))
82     {
83       measureLength =
84         Moment (ly_scm2rational
85                 (scm_divide (scm_car (timeSignatureFraction),
86                              scm_cdr (timeSignatureFraction)))).smobbed_copy ();
87     }
88   context ()->set_property ("measureLength", measureLength);
89
90   /*
91     Do not init measurePosition; this should be done from global
92     context.
93   */
94
95   SCM timeSignatureSettings = timing->get_property ("timeSignatureSettings");
96   if (!scm_is_pair (timeSignatureSettings))
97     {
98       programming_error ("missing timeSignatureSettings");
99       // A memoized constant is not the prettiest thing as a fallback
100       // since it does not track changes of the variable.  However,
101       // this is still better than nothing, and we already complained
102       // via a programming_error
103       timeSignatureSettings = ly_lily_module_constant ("default-time-signature-settings");
104     }
105   context ()->set_property ("timeSignatureSettings", timeSignatureSettings);
106
107   SCM beamExceptions = timing->get_property ("beamExceptions");
108   if (!scm_is_pair (beamExceptions))
109     {
110       beamExceptions =
111         scm_call_2 (ly_lily_module_constant ("beam-exceptions"),
112                     timeSignatureFraction,
113                     timeSignatureSettings);
114     }
115   context ()->set_property ("beamExceptions", beamExceptions);
116
117   SCM baseMoment = timing->get_property ("baseMoment");
118   if (!unsmob_moment (baseMoment))
119     {
120       baseMoment =
121         Moment (ly_scm2rational
122                 (scm_call_2 (ly_lily_module_constant ("base-length"),
123                              timeSignatureFraction,
124                              timeSignatureSettings))).smobbed_copy ();
125     }
126   context ()->set_property ("baseMoment", baseMoment);
127
128   SCM beatStructure = timing->get_property ("beatStructure");
129   if (!scm_is_pair (beatStructure))
130     {
131       beatStructure =
132         scm_call_3 (ly_lily_module_constant ("beat-structure"),
133                     ly_rational2scm (unsmob_moment (baseMoment)->main_part_),
134                     timeSignatureFraction,
135                     timeSignatureSettings);
136     }
137   context ()->set_property ("beatStructure", beatStructure);
138
139   context ()->set_property ("beamHalfMeasure",
140                             timing->get_property ("beamHalfMeasure"));
141
142   context ()->set_property ("autoBeaming",
143                             timing->get_property ("autoBeaming"));
144 }
145
146 Rational
147 Timing_translator::measure_length () const
148 {
149   SCM l = get_property ("measureLength");
150   if (unsmob_moment (l))
151     return unsmob_moment (l)->main_part_;
152   else
153     return Rational (1);
154 }
155
156 Timing_translator::Timing_translator ()
157 {
158 }
159
160 void
161 Timing_translator::start_translation_timestep ()
162 {
163   Global_context *global = get_global_context ();
164
165   Moment now = global->now_mom ();
166   Moment dt = now - global->previous_moment ();
167   if (dt < Moment (0))
168     {
169       programming_error ("moving backwards in time");
170       dt = 0;
171     }
172   else if (dt.main_part_.is_infinity ())
173     {
174       programming_error ("moving infinitely to future");
175       dt = 0;
176     }
177
178   if (!dt.to_bool ())
179     return;
180
181   Moment measposp;
182
183   SCM s = get_property ("measurePosition");
184   if (unsmob_moment (s))
185     measposp = *unsmob_moment (s);
186   else
187     {
188       measposp = now;
189     }
190
191   int current_barnumber = robust_scm2int (get_property ("currentBarNumber"), 0);
192   int internal_barnumber = robust_scm2int (get_property ("internalBarNumber"), 0);
193
194   SCM cad = get_property ("timing");
195   bool c = to_boolean (cad);
196
197   if (c)
198     {
199       Rational len = measure_length ();
200
201       measposp += dt;
202
203       while (measposp.main_part_ >= len)
204         {
205           measposp.main_part_ -= len;
206           current_barnumber++;
207           internal_barnumber++;
208         }
209     }
210
211
212   // Because "timing" can be switched on and off asynchronously with
213   // graces, measurePosition might get into strange settings of
214   // grace_part_.  It does not actually make sense to have it diverge
215   // from the main timing.  Updating the grace part outside of the
216   // actual check for "timing" looks strange and will lead to changes
217   // of grace_part_ even when timing is off.  However, when timing is
218   // switched back on again, this will generally happen in an override
219   // that does _not_ in itself advance current_moment.  So the whole
220   // timing advance logic will only get triggered while "timing" is
221   // still of.  Maybe we should keep measurePosition.grace_part_
222   // constantly at zero anyway?
223
224   measposp.grace_part_ = now.grace_part_;
225
226
227   context ()->set_property ("currentBarNumber", scm_from_int (current_barnumber));
228   context ()->set_property ("internalBarNumber", scm_from_int (internal_barnumber));
229   context ()->set_property ("measurePosition", measposp.smobbed_copy ());
230 }
231
232 #include "translator.icc"
233
234 ADD_TRANSLATOR (Timing_translator,
235                 /* doc */
236                 "This engraver adds the alias @code{Timing} to its containing"
237                 " context.  Responsible for synchronizing timing information"
238                 " from staves.  Normally in @code{Score}.  In order to create"
239                 " polyrhythmic music, this engraver should be removed from"
240                 " @code{Score} and placed in @code{Staff}.",
241
242                 /* create */
243                 "",
244
245                 /* read */
246                 "baseMoment "
247                 "currentBarNumber "
248                 "internalBarNumber "
249                 "measureLength "
250                 "measurePosition "
251                 "timeSignatureFraction ",
252
253                 /* write */
254                 "baseMoment "
255                 "currentBarNumber "
256                 "internalBarNumber "
257                 "measureLength "
258                 "measurePosition "
259                 "timeSignatureFraction "
260                );