Left: | ||
Right: |
OLD | NEW |
---|---|
1 /* | 1 /* |
2 This file is part of LilyPond, the GNU music typesetter. | 2 This file is part of LilyPond, the GNU music typesetter. |
3 | 3 |
4 Copyright (C) 2000--2015 Jan Nieuwenhuizen <janneke@gnu.org> | 4 Copyright (C) 2000--2015 Jan Nieuwenhuizen <janneke@gnu.org> |
5 | 5 |
6 LilyPond is free software: you can redistribute it and/or modify | 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 | 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 | 8 the Free Software Foundation, either version 3 of the License, or |
9 (at your option) any later version. | 9 (at your option) any later version. |
10 | 10 |
11 LilyPond is distributed in the hope that it will be useful, | 11 LilyPond is distributed in the hope that it will be useful, |
12 but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 GNU General Public License for more details. | 14 GNU General Public License for more details. |
15 | 15 |
16 You should have received a copy of the GNU General Public License | 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/>. | 17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>. |
18 */ | 18 */ |
19 | 19 |
20 #include "performer.hh" | 20 #include "performer.hh" |
21 #include "audio-item.hh" | 21 #include "audio-item.hh" |
22 #include "std-vector.hh" | |
22 #include "stream-event.hh" | 23 #include "stream-event.hh" |
23 #include "international.hh" | 24 #include "international.hh" |
24 | 25 |
25 #include "translator.icc" | 26 #include "translator.icc" |
26 | 27 |
27 class Dynamic_performer : public Performer | 28 class Dynamic_performer : public Performer |
28 { | 29 { |
29 public: | 30 public: |
30 TRANSLATOR_DECLARATIONS (Dynamic_performer); | 31 TRANSLATOR_DECLARATIONS (Dynamic_performer); |
31 protected: | 32 protected: |
33 virtual void finalize (); | |
32 void stop_translation_timestep (); | 34 void stop_translation_timestep (); |
33 void process_music (); | 35 void process_music (); |
34 Real equalize_volume (Real); | 36 Real equalize_volume (Real); |
35 | 37 |
36 void listen_decrescendo (Stream_event *); | 38 void listen_decrescendo (Stream_event *); |
37 void listen_crescendo (Stream_event *); | 39 void listen_crescendo (Stream_event *); |
38 void listen_absolute_dynamic (Stream_event *); | 40 void listen_absolute_dynamic (Stream_event *); |
41 | |
42 private: | |
43 void close_and_enqueue_span (); | |
44 Real compute_departure_volume (Direction depart_dir, | |
45 Real start_vol, | |
46 Real end_vol, | |
47 Real min_vol, | |
48 Real max_vol); | |
49 bool drive_state_machine (Direction next_grow_dir); | |
50 // next_vol < 0 means select a target dynamic based on growth direction. | |
51 // return actual next volume (computed if not provided) | |
52 Real finish_queued_spans (Real next_vol = -1.0); | |
53 Real look_up_absolute_volume(SCM dynamicString, | |
ht
2016/07/09 18:16:50
missing space after open parenthesis
Dan Eble
2016/07/12 05:01:15
Thank you. I've run fixcc.py on dynamic-performer
| |
54 Real defaultValue); | |
55 | |
56 private: | |
57 // This performer queues a number of dynamic spans waiting for the following | |
58 // pattern before computing their volume levels. | |
59 // | |
60 // 1. the first (de)crescendo, followed by ... | |
61 // 2. zero or more spans that either change in the same direction as the | |
62 // first or do not change, followed by ... | |
63 // 3. zero or more spans that either change in the opposite direction as the | |
64 // first or do not change | |
65 // | |
66 // The search may be cut short by an absolute dynamic or the end of the | |
67 // context. | |
68 enum State | |
69 { | |
70 STATE_INITIAL = 0, // waiting for a (de)crescendo | |
71 STATE_DEPART, // enqueued the first span, gathering same-direction spans | |
72 STATE_RETURN // gathering opposite-direction spans | |
73 }; | |
74 | |
75 struct UnfinishedSpan | |
76 { | |
77 Audio_span_dynamic *dynamic_; | |
78 Direction grow_dir_; | |
79 | |
80 UnfinishedSpan () : dynamic_ (0), grow_dir_ (CENTER) {} | |
81 }; | |
82 | |
83 struct DynamicQueue | |
84 { | |
85 vector<UnfinishedSpan> spans_; | |
86 // total duration of (de)crescendi (i.e. excluding fixed-volume spans) | |
87 Real change_duration_; | |
88 Real min_target_vol_; | |
89 Real max_target_vol_; | |
90 | |
91 DynamicQueue () : change_duration_ (0) {} | |
92 | |
93 void clear () | |
94 { | |
95 spans_.clear (); | |
96 change_duration_ = 0; | |
97 } | |
98 | |
99 void push_back (const UnfinishedSpan& span, | |
100 Real min_target_vol, | |
101 Real max_target_vol) | |
ht
2016/07/09 18:16:50
Assuming that equalizer settings do not change bet
Dan Eble
2016/07/12 05:01:15
Assuming that context properties do not change in
| |
102 { | |
103 if (span.grow_dir_ != CENTER) | |
104 change_duration_ += span.dynamic_->get_duration (); | |
105 min_target_vol_ = min_target_vol; | |
106 max_target_vol_ = max_target_vol; | |
107 spans_.push_back (span); | |
108 } | |
109 | |
110 void set_volume (Real start_vol, Real target_vol); | |
111 }; | |
112 | |
39 private: | 113 private: |
40 Stream_event *script_event_; | 114 Stream_event *script_event_; |
41 Drul_array<Stream_event *> span_events_; | 115 Drul_array<Stream_event *> span_events_; |
42 Drul_array<Direction> grow_dir_; | 116 Direction next_grow_dir_; |
43 Real last_volume_; | 117 Direction depart_dir_; |
44 Audio_dynamic *absolute_; | 118 UnfinishedSpan open_span_; |
45 Audio_span_dynamic *span_dynamic_; | 119 DynamicQueue depart_queue_; |
46 Audio_span_dynamic *finished_span_dynamic_; | 120 DynamicQueue return_queue_; |
121 State state_; | |
47 }; | 122 }; |
48 | 123 |
49 Dynamic_performer::Dynamic_performer () | 124 Dynamic_performer::Dynamic_performer () |
50 { | 125 : script_event_ (0), |
51 last_volume_ = -1; | 126 next_grow_dir_ (CENTER), |
52 script_event_ = 0; | 127 depart_dir_ (CENTER), |
53 absolute_ = 0; | 128 state_ (STATE_INITIAL) |
129 { | |
54 span_events_[LEFT] | 130 span_events_[LEFT] |
55 = span_events_[RIGHT] = 0; | 131 = span_events_[RIGHT] = 0; |
56 span_dynamic_ = 0; | 132 } |
57 finished_span_dynamic_ = 0; | 133 |
58 } | 134 bool |
59 | 135 Dynamic_performer::drive_state_machine (Direction next_grow_dir) |
136 { | |
137 switch (state_) | |
138 { | |
139 case STATE_INITIAL: | |
140 if (next_grow_dir != CENTER) | |
141 { | |
142 state_ = STATE_DEPART; | |
143 depart_dir_ = next_grow_dir; | |
144 } | |
145 break; | |
146 | |
147 case STATE_DEPART: | |
148 if (next_grow_dir == -depart_dir_) | |
149 state_ = STATE_RETURN; | |
150 break; | |
151 | |
152 case STATE_RETURN: | |
153 if (next_grow_dir == depart_dir_) | |
154 { | |
155 state_ = STATE_DEPART; | |
156 depart_dir_ = next_grow_dir; | |
ht
2016/07/09 18:16:50
This looks like a redundant assignment due to the
Dan Eble
2016/07/12 05:01:15
Yes. I appreciate your attention to detail.
| |
157 return true; | |
158 } | |
159 break; | |
160 } | |
161 | |
162 return false; | |
163 } | |
164 | |
165 void | |
166 Dynamic_performer::close_and_enqueue_span () | |
167 { | |
168 if (!open_span_.dynamic_) | |
169 programming_error("no open dynamic span"); | |
170 else | |
171 { | |
172 DynamicQueue& dq = | |
173 (state_ == STATE_RETURN) ? return_queue_ : depart_queue_; | |
174 | |
175 // Changing equalizer settings in the course of the performance does not | |
176 // seem very likely. This is a fig leaf: Equalize these limit volumes | |
177 // now as the required context properties are current. Note that only | |
178 // the limits at the end of the last span in the queue are kept. | |
179 | |
180 // Resist diminishing to silence. (Idea: Look up "ppppp" | |
181 // with dynamicAbsoluteVolumeFunction, however that would yield 0.25.) | |
182 const Real min_target = equalize_volume (0.1); | |
183 const Real max_target = | |
184 equalize_volume (Audio_span_dynamic::MAXIMUM_VOLUME); | |
185 | |
186 open_span_.dynamic_->set_end_moment (now_mom ()); | |
187 dq.push_back (open_span_, min_target, max_target); | |
188 } | |
189 | |
190 open_span_ = UnfinishedSpan (); | |
191 } | |
192 | |
193 // Set the starting and target volume for each span in the queue. The gain | |
194 // (loss) of any (de)crescendo is proportional to its share of the total time | |
195 // spent changing. | |
196 void | |
197 Dynamic_performer::DynamicQueue::set_volume (Real start_vol, | |
198 Real target_vol) | |
199 { | |
200 const Real gain = target_vol - start_vol; | |
201 Real dur = 0; // duration of (de)crescendi processed so far | |
202 Real vol = start_vol; | |
203 for (vector<UnfinishedSpan>::iterator it = spans_.begin (); | |
204 it != spans_.end (); ++it) | |
205 { | |
206 const Real prev_vol = vol; | |
207 if (it->grow_dir_ != CENTER) | |
208 { | |
209 // grant this (de)crescendo its portion of the gain | |
210 dur += it->dynamic_->get_duration (); | |
211 vol = start_vol + gain * (dur / change_duration_); | |
212 } | |
213 it->dynamic_->set_volume (prev_vol, vol); | |
214 } | |
215 } | |
216 | |
217 // Return a volume which is reasonably distant from the given volumes in the | |
ht
2016/07/09 18:16:50
I guess that the "given volumes" mean only start_
Dan Eble
2016/07/12 05:01:15
Yes. I added max and min parameters after documen
| |
218 // given direction, for use as a peak volume in a passage with a crescendo | |
219 // followed by a decrescendo (or vice versa). If the given volumes are equal, | |
220 // the returned volume is a also reasonable target volume for a single | |
221 // (de)crescendo. | |
60 Real | 222 Real |
223 Dynamic_performer::compute_departure_volume (Direction depart_dir, | |
224 Real start_vol, | |
225 Real end_vol, | |
226 Real min_vol, | |
227 Real max_vol) | |
228 { | |
229 if (depart_dir == CENTER) | |
230 return start_vol; | |
231 | |
232 // Try to find a volume that is a minimum distance from the starting and | |
233 // ending volumes. If the endpoint volumes differ, the nearer one is padded | |
234 // less than the farther one. | |
ht
2016/07/09 18:16:50
I believe I eventually understood what this functi
Dan Eble
2016/07/12 05:01:15
That was my intent. Consider mf < ... > p. The p
| |
235 | |
236 // Idea: Use a context property or callback, e.g. the difference between two | |
237 // dynamics in dynamicAbsoluteVolumeFunction. 0.25 is the default difference | |
238 // between "p" and "ff". (Isn't that rather wide for this purpose?) | |
ht
2016/07/09 18:16:50
I think using "0.25 * the available volume range"
| |
239 const Real far_padding = 0.25; | |
240 const Real near_padding = 0.07; | |
241 | |
242 // If for some reason one of the endpoints is already below the supposed | |
243 // minimum or maximum, just accept it. | |
244 // TODO: If this is impossible, programming_error. | |
ht
2016/07/09 18:16:50
Is there still something left to be done here?
Dan Eble
2016/07/12 05:01:15
I've removed the comment.
| |
245 min_vol = min (min (min_vol, start_vol), end_vol); | |
246 max_vol = max (max (max_vol, start_vol), end_vol); | |
247 | |
248 const Real vol_range = max_vol - min_vol; | |
249 | |
250 const Real near = minmax (depart_dir, start_vol, end_vol) | |
251 + depart_dir * near_padding * vol_range; | |
252 const Real far = minmax (-depart_dir, start_vol, end_vol) | |
253 + depart_dir * far_padding * vol_range; | |
254 const Real depart_vol = minmax (depart_dir, near, far); | |
255 return max (min (depart_vol, max_vol), min_vol); | |
256 } | |
257 | |
258 Real | |
259 Dynamic_performer::finish_queued_spans (Real next_vol) | |
260 { | |
261 if (depart_queue_.spans_.empty ()) | |
262 { | |
263 programming_error("no dynamic span to finish"); | |
ht
2016/07/09 18:16:50
missing whitespace before open parenthesis
| |
264 return next_vol; | |
265 } | |
266 | |
267 const Real start_vol = depart_queue_.spans_.front ().dynamic_->get_start_volum e (); | |
268 | |
269 if (return_queue_.spans_.empty ()) | |
270 { | |
271 Real depart_vol = next_vol; | |
272 | |
273 // If the next dynamic is not specified or is inconsistent with the | |
274 // direction of growth, choose a reasonable target. | |
275 if ((next_vol < 0) || (depart_dir_ != sign (next_vol - start_vol))) | |
276 { | |
277 depart_vol = compute_departure_volume (depart_dir_, | |
278 start_vol, start_vol, | |
279 depart_queue_.min_target_vol_, | |
280 depart_queue_.max_target_vol_); | |
281 } | |
282 | |
283 depart_queue_.set_volume(start_vol, depart_vol); | |
ht
2016/07/09 18:16:50
missing whitespace before open parenthesis
| |
284 depart_queue_.clear (); | |
285 return (next_vol >= 0) ? next_vol : depart_vol; | |
286 } | |
287 else | |
288 { | |
289 // If the next dynamic is not specified, return to the starting volume. | |
290 const Real return_vol = (next_vol >= 0) ? next_vol : start_vol; | |
291 Real depart_vol = compute_departure_volume(depart_dir_, | |
ht
2016/07/09 18:16:50
missing whitespace before open parenthesis
| |
292 start_vol, return_vol, | |
293 depart_queue_.min_target_vol_, | |
294 depart_queue_.max_target_vol_); | |
295 depart_queue_.set_volume (start_vol, depart_vol); | |
296 depart_queue_.clear (); | |
297 return_queue_.set_volume (depart_vol, return_vol); | |
298 return_queue_.clear (); | |
299 return return_vol; | |
300 } | |
301 } | |
302 | |
303 Real | |
61 Dynamic_performer::equalize_volume (Real volume) | 304 Dynamic_performer::equalize_volume (Real volume) |
62 { | 305 { |
63 /* | 306 /* |
64 properties override default equaliser setting | 307 properties override default equaliser setting |
65 */ | 308 */ |
66 SCM min = get_property ("midiMinimumVolume"); | 309 SCM min = get_property ("midiMinimumVolume"); |
67 SCM max = get_property ("midiMaximumVolume"); | 310 SCM max = get_property ("midiMaximumVolume"); |
68 if (scm_is_number (min) || scm_is_number (max)) | 311 if (scm_is_number (min) || scm_is_number (max)) |
69 { | 312 { |
70 Interval iv (0, 1); | 313 Interval iv (Audio_span_dynamic::MINIMUM_VOLUME, |
314 Audio_span_dynamic::MAXIMUM_VOLUME); | |
71 if (scm_is_number (min)) | 315 if (scm_is_number (min)) |
72 iv[MIN] = scm_to_double (min); | 316 iv[MIN] = scm_to_double (min); |
73 if (scm_is_number (max)) | 317 if (scm_is_number (max)) |
74 iv[MAX] = scm_to_double (max); | 318 iv[MAX] = scm_to_double (max); |
75 volume = iv[MIN] + iv.length () * volume; | 319 volume = iv[MIN] + iv.length () * volume; |
76 } | 320 } |
77 else | 321 else |
78 { | 322 { |
79 /* | 323 /* |
80 urg, code duplication:: staff_performer | 324 urg, code duplication:: staff_performer |
81 */ | 325 */ |
82 SCM s = get_property ("midiInstrument"); | 326 SCM s = get_property ("midiInstrument"); |
83 | 327 |
84 if (!scm_is_string (s)) | 328 if (!scm_is_string (s)) |
85 s = get_property ("instrumentName"); | 329 s = get_property ("instrumentName"); |
86 | 330 |
87 if (!scm_is_string (s)) | 331 if (!scm_is_string (s)) |
88 s = scm_from_ascii_string ("piano"); | 332 s = scm_from_ascii_string ("piano"); |
89 | 333 |
90 SCM eq = get_property ("instrumentEqualizer"); | 334 SCM eq = get_property ("instrumentEqualizer"); |
91 if (ly_is_procedure (eq)) | 335 if (ly_is_procedure (eq)) |
92 s = scm_call_1 (eq, s); | 336 s = scm_call_1 (eq, s); |
93 | 337 |
94 if (is_number_pair (s)) | 338 if (is_number_pair (s)) |
95 { | 339 { |
96 Interval iv = ly_scm2interval (s); | 340 Interval iv = ly_scm2interval (s); |
97 volume = iv[MIN] + iv.length () * volume; | 341 volume = iv[MIN] + iv.length () * volume; |
98 } | 342 } |
99 } | 343 } |
100 return volume; | 344 return std::max (std::min (volume, Audio_span_dynamic::MAXIMUM_VOLUME), |
345 Audio_span_dynamic::MINIMUM_VOLUME); | |
ht
2016/07/09 18:16:50
max and min are usually used without namespace
Dan Eble
2016/07/12 05:01:15
That's currently true in Lilypond, but someone has
| |
346 } | |
347 | |
348 void | |
349 Dynamic_performer::finalize () | |
350 { | |
351 if (open_span_.dynamic_) | |
352 close_and_enqueue_span (); | |
353 finish_queued_spans (); | |
354 } | |
355 | |
356 Real | |
357 Dynamic_performer::look_up_absolute_volume(SCM dynamicString, | |
358 Real defaultValue) | |
359 { | |
360 SCM proc = get_property ("dynamicAbsoluteVolumeFunction"); | |
361 | |
362 SCM svolume = SCM_EOL; | |
363 if (ly_is_procedure (proc)) | |
364 svolume = scm_call_1 (proc, dynamicString); | |
365 | |
366 return robust_scm2double (svolume, defaultValue); | |
101 } | 367 } |
102 | 368 |
103 void | 369 void |
104 Dynamic_performer::process_music () | 370 Dynamic_performer::process_music () |
105 { | 371 { |
106 if (span_events_[START] || span_events_[STOP] || script_event_) | 372 Real volume = -1; |
373 | |
374 if (script_event_) // explicit dynamic | |
107 { | 375 { |
108 // End the previous spanner when a new one begins or at an explicit stop | 376 volume = look_up_absolute_volume (script_event_->get_property ("text"), |
109 // or absolute dynamic. | 377 Audio_span_dynamic::DEFAULT_VOLUME); |
110 finished_span_dynamic_ = span_dynamic_; | 378 volume = equalize_volume (volume); |
111 span_dynamic_ = 0; | 379 } |
380 else if (!open_span_.dynamic_) // first time only | |
381 { | |
ht
2016/07/09 18:16:50
Could this case be combined with the code further
Dan Eble
2016/07/12 05:01:16
This block is executed the first time only, but th
| |
382 // Idea: look_up_absolute_volume (ly_symbol2scm ("mf")). | |
ht
2016/07/09 18:16:50
Actually, the value 90.0/127.0 for Audio_span_dyna
Dan Eble
2016/07/12 05:01:16
I know. Did you mean to suggest that this idea is
| |
383 // It is likely to change regtests. | |
384 volume = equalize_volume (Audio_span_dynamic::DEFAULT_VOLUME); | |
112 } | 385 } |
113 | 386 |
114 if (span_events_[START]) | 387 // end the current span at relevant points |
388 if (open_span_.dynamic_ | |
389 && (span_events_[START] || span_events_[STOP] || script_event_)) | |
115 { | 390 { |
116 // Start of a dynamic spanner. Create a new Audio_span_dynamic for | 391 close_and_enqueue_span (); |
117 // collecting changes in dynamics within this spanner. | 392 if (script_event_) |
118 span_dynamic_ = new Audio_span_dynamic (equalize_volume (0.1), equalize_vo lume (1.0)); | 393 { |
119 announce_element (Audio_element_info (span_dynamic_, span_events_[START])) ; | 394 state_ = STATE_INITIAL; |
120 | 395 volume = finish_queued_spans (volume); |
121 span_dynamic_->grow_dir_ = grow_dir_[START]; | 396 } |
122 } | 397 } |
123 | 398 |
124 if (script_event_ | 399 // start a new span so that some dynamic is always in effect |
125 || span_dynamic_ | 400 if (!open_span_.dynamic_) |
126 || finished_span_dynamic_) | |
127 { | 401 { |
128 // New change in dynamics. | 402 if (drive_state_machine (next_grow_dir_)) |
129 absolute_ = new Audio_dynamic (); | 403 volume = finish_queued_spans (volume); |
130 | 404 |
131 if (script_event_) | 405 // if not known by now, use a default volume for robustness |
132 { | 406 if (volume < 0) |
133 // Explicit dynamic script event: determine the volume. | 407 volume = Audio_span_dynamic::DEFAULT_VOLUME; |
ht
2016/07/09 18:16:50
Should equalization be applied to this default vol
Dan Eble
2016/07/12 05:01:16
I believe it makes no difference because in the ca
| |
134 SCM proc = get_property ("dynamicAbsoluteVolumeFunction"); | |
135 | 408 |
136 SCM svolume = SCM_EOL; | 409 Stream_event *cause = |
137 if (ly_is_procedure (proc)) | 410 span_events_[START] ? span_events_[START] : |
138 { | 411 script_event_ ? script_event_ : |
139 // urg | 412 span_events_[STOP]; |
140 svolume = scm_call_1 (proc, script_event_->get_property ("text")); | |
141 } | |
142 | 413 |
143 Real volume = robust_scm2double (svolume, 0.5); | 414 open_span_.dynamic_ = new Audio_span_dynamic (now_mom (), volume); |
144 | 415 open_span_.grow_dir_ = next_grow_dir_; |
145 last_volume_ | 416 announce_element (Audio_element_info (open_span_.dynamic_, cause)); |
146 = absolute_->volume_ = equalize_volume (volume); | |
147 } | |
148 | |
149 Audio_element_info info (absolute_, script_event_); | |
150 announce_element (info); | |
151 } | 417 } |
152 | |
153 if (last_volume_ < 0) | |
154 { | |
155 absolute_ = new Audio_dynamic (); | |
156 | |
157 last_volume_ | |
158 = absolute_->volume_ = equalize_volume (0.71); // Backward compatible | |
159 | |
160 Audio_element_info info (absolute_, script_event_); | |
161 announce_element (info); | |
162 } | |
163 | |
164 if (span_dynamic_) | |
165 span_dynamic_->add_absolute (absolute_); | |
166 | |
167 if (finished_span_dynamic_) | |
168 finished_span_dynamic_->add_absolute (absolute_); | |
169 } | 418 } |
170 | 419 |
171 void | 420 void |
172 Dynamic_performer::stop_translation_timestep () | 421 Dynamic_performer::stop_translation_timestep () |
173 { | 422 { |
174 if (finished_span_dynamic_) | |
175 { | |
176 finished_span_dynamic_->render (); | |
177 finished_span_dynamic_ = 0; | |
178 } | |
179 | |
180 if (absolute_) | |
181 { | |
182 if (absolute_->volume_ < 0) | |
183 { | |
184 absolute_->volume_ = last_volume_; | |
185 } | |
186 else | |
187 { | |
188 last_volume_ = absolute_->volume_; | |
189 } | |
190 } | |
191 | |
192 absolute_ = 0; | |
193 script_event_ = 0; | 423 script_event_ = 0; |
194 span_events_[LEFT] | 424 span_events_[LEFT] |
195 = span_events_[RIGHT] = 0; | 425 = span_events_[RIGHT] = 0; |
426 next_grow_dir_ = CENTER; | |
196 } | 427 } |
197 | 428 |
198 void | 429 void |
199 Dynamic_performer::listen_decrescendo (Stream_event *r) | 430 Dynamic_performer::listen_decrescendo (Stream_event *r) |
200 { | 431 { |
201 Direction d = to_dir (r->get_property ("span-direction")); | 432 Direction d = to_dir (r->get_property ("span-direction")); |
202 span_events_[d] = r; | 433 if (ASSIGN_EVENT_ONCE (span_events_[d], r) && (d == START)) |
203 grow_dir_[d] = SMALLER; | 434 next_grow_dir_ = SMALLER; |
204 } | 435 } |
205 | 436 |
206 void | 437 void |
207 Dynamic_performer::listen_crescendo (Stream_event *r) | 438 Dynamic_performer::listen_crescendo (Stream_event *r) |
208 { | 439 { |
209 Direction d = to_dir (r->get_property ("span-direction")); | 440 Direction d = to_dir (r->get_property ("span-direction")); |
210 span_events_[d] = r; | 441 if (ASSIGN_EVENT_ONCE (span_events_[d], r) && (d == START)) |
211 grow_dir_[d] = BIGGER; | 442 next_grow_dir_ = BIGGER; |
212 } | 443 } |
213 | 444 |
214 void | 445 void |
215 Dynamic_performer::listen_absolute_dynamic (Stream_event *r) | 446 Dynamic_performer::listen_absolute_dynamic (Stream_event *r) |
216 { | 447 { |
217 if (!script_event_) | 448 ASSIGN_EVENT_ONCE (script_event_, r); |
218 script_event_ = r; | |
219 } | 449 } |
220 | 450 |
221 void | 451 void |
222 Dynamic_performer::boot () | 452 Dynamic_performer::boot () |
223 { | 453 { |
224 ADD_LISTENER (Dynamic_performer, decrescendo); | 454 ADD_LISTENER (Dynamic_performer, decrescendo); |
225 ADD_LISTENER (Dynamic_performer, crescendo); | 455 ADD_LISTENER (Dynamic_performer, crescendo); |
226 ADD_LISTENER (Dynamic_performer, absolute_dynamic); | 456 ADD_LISTENER (Dynamic_performer, absolute_dynamic); |
227 } | 457 } |
228 | 458 |
229 ADD_TRANSLATOR (Dynamic_performer, | 459 ADD_TRANSLATOR (Dynamic_performer, |
230 /* doc */ | 460 /* doc */ |
231 "", | 461 "", |
ht
2016/07/09 18:16:50
Now that the logic of Dynamic_performer is being r
Dan Eble
2016/07/12 05:01:15
If someone can point me to a good example of IM do
| |
232 | 462 |
233 /* create */ | 463 /* create */ |
234 "", | 464 "", |
235 | 465 |
236 /* read */ | 466 /* read */ |
237 "dynamicAbsoluteVolumeFunction " | 467 "dynamicAbsoluteVolumeFunction " |
238 "instrumentEqualizer " | 468 "instrumentEqualizer " |
239 "midiMaximumVolume " | 469 "midiMaximumVolume " |
240 "midiMinimumVolume " | 470 "midiMinimumVolume " |
241 "midiInstrument ", | 471 "midiInstrument ", |
242 | 472 |
243 /* write */ | 473 /* write */ |
244 "" | 474 "" |
245 ); | 475 ); |
OLD | NEW |