]> git.donarmstrong.com Git - lilypond.git/blob - lily/timing-translator.cc
Issue 4462/1: Create a module variable access system for C++
[lilypond.git] / lily / timing-translator.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 "timing-translator.hh"
21
22 #include "warn.hh"
23 #include "translator-group.hh"
24 #include "global-context.hh"
25 #include "moment.hh"
26 #include "lily-imports.hh"
27
28 void
29 Timing_translator::stop_translation_timestep ()
30 {
31   Global_context *global = get_global_context ();
32
33   if (to_boolean (get_property ("timing"))
34       && !to_boolean (get_property ("skipBars")))
35     {
36       Moment barleft = (measure_length () - measure_position (context ()));
37       Moment now = now_mom ();
38
39       if (barleft > Moment (0))
40         {
41           Moment nextmom = now + barleft;
42           nextmom.grace_part_ = Rational (0);
43           global->add_moment_to_process (nextmom);
44         }
45     }
46 }
47
48 void
49 Timing_translator::initialize ()
50 {
51   Context *timing = unsmob<Context>
52     (Lily::ly_context_find (context ()->self_scm (), 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 = Lily::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 = Lily::beam_exceptions (timeSignatureFraction,
111                                               timeSignatureSettings);
112     }
113   context ()->set_property ("beamExceptions", beamExceptions);
114
115   SCM baseMoment = timing->get_property ("baseMoment");
116   if (!unsmob<Moment> (baseMoment))
117     {
118       baseMoment =
119         Moment (ly_scm2rational
120                 (Lily::base_length (timeSignatureFraction,
121                                     timeSignatureSettings))).smobbed_copy ();
122     }
123   context ()->set_property ("baseMoment", baseMoment);
124
125   SCM beatStructure = timing->get_property ("beatStructure");
126   if (!scm_is_pair (beatStructure))
127     {
128       beatStructure =
129         Lily::beat_structure (ly_rational2scm (unsmob<Moment> (baseMoment)->main_part_),
130                               timeSignatureFraction,
131                               timeSignatureSettings);
132     }
133   context ()->set_property ("beatStructure", beatStructure);
134
135   context ()->set_property ("beamHalfMeasure",
136                             timing->get_property ("beamHalfMeasure"));
137
138   context ()->set_property ("autoBeaming",
139                             timing->get_property ("autoBeaming"));
140 }
141
142 Rational
143 Timing_translator::measure_length () const
144 {
145   SCM l = get_property ("measureLength");
146   if (unsmob<Moment> (l))
147     return unsmob<Moment> (l)->main_part_;
148   else
149     return Rational (1);
150 }
151
152 Timing_translator::Timing_translator ()
153 {
154 }
155
156 void
157 Timing_translator::start_translation_timestep ()
158 {
159   Global_context *global = get_global_context ();
160
161   Moment now = global->now_mom ();
162   Moment dt = now - global->previous_moment ();
163   if (dt < Moment (0))
164     {
165       programming_error ("moving backwards in time");
166       dt = 0;
167     }
168   else if (dt.main_part_.is_infinity ())
169     {
170       programming_error ("moving infinitely to future");
171       dt = 0;
172     }
173
174   if (!dt.to_bool ())
175     return;
176
177   Moment measposp;
178
179   SCM s = get_property ("measurePosition");
180   if (unsmob<Moment> (s))
181     measposp = *unsmob<Moment> (s);
182   else
183     {
184       measposp = now;
185     }
186
187   int current_barnumber = robust_scm2int (get_property ("currentBarNumber"), 0);
188   int internal_barnumber = robust_scm2int (get_property ("internalBarNumber"), 0);
189
190   SCM cad = get_property ("timing");
191   bool c = to_boolean (cad);
192
193   if (c)
194     {
195       Rational len = measure_length ();
196
197       measposp += dt;
198
199       while (measposp.main_part_ >= len)
200         {
201           measposp.main_part_ -= len;
202           current_barnumber++;
203           internal_barnumber++;
204         }
205     }
206
207
208   // Because "timing" can be switched on and off asynchronously with
209   // graces, measurePosition might get into strange settings of
210   // grace_part_.  It does not actually make sense to have it diverge
211   // from the main timing.  Updating the grace part outside of the
212   // actual check for "timing" looks strange and will lead to changes
213   // of grace_part_ even when timing is off.  However, when timing is
214   // switched back on again, this will generally happen in an override
215   // that does _not_ in itself advance current_moment.  So the whole
216   // timing advance logic will only get triggered while "timing" is
217   // still of.  Maybe we should keep measurePosition.grace_part_
218   // constantly at zero anyway?
219
220   measposp.grace_part_ = now.grace_part_;
221
222
223   context ()->set_property ("currentBarNumber", scm_from_int (current_barnumber));
224   context ()->set_property ("internalBarNumber", scm_from_int (internal_barnumber));
225   context ()->set_property ("measurePosition", measposp.smobbed_copy ());
226 }
227
228 #include "translator.icc"
229
230 ADD_TRANSLATOR (Timing_translator,
231                 /* doc */
232                 "This engraver adds the alias @code{Timing} to its containing"
233                 " context.  Responsible for synchronizing timing information"
234                 " from staves.  Normally in @code{Score}.  In order to create"
235                 " polyrhythmic music, this engraver should be removed from"
236                 " @code{Score} and placed in @code{Staff}.",
237
238                 /* create */
239                 "",
240
241                 /* read */
242                 "baseMoment "
243                 "currentBarNumber "
244                 "internalBarNumber "
245                 "measureLength "
246                 "measurePosition "
247                 "timeSignatureFraction ",
248
249                 /* write */
250                 "baseMoment "
251                 "currentBarNumber "
252                 "internalBarNumber "
253                 "measureLength "
254                 "measurePosition "
255                 "timeSignatureFraction "
256                );