]> git.donarmstrong.com Git - lilypond.git/blob - lily/dynamic-performer.cc
Merge branch 'master' of /home/jcharles/GIT/Lily/. into translation
[lilypond.git] / lily / dynamic-performer.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2000--2015 Jan Nieuwenhuizen <janneke@gnu.org>
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 "performer.hh"
21 #include "audio-item.hh"
22 #include "std-vector.hh"
23 #include "stream-event.hh"
24 #include "international.hh"
25
26 #include "translator.icc"
27
28 class Dynamic_performer : public Performer
29 {
30 public:
31   TRANSLATOR_DECLARATIONS (Dynamic_performer);
32 protected:
33   virtual void finalize ();
34   void stop_translation_timestep ();
35   void process_music ();
36   Real equalize_volume (Real);
37
38   void listen_decrescendo (Stream_event *);
39   void listen_crescendo (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,
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)
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
113 private:
114   Stream_event *script_event_;
115   Drul_array<Stream_event *> span_events_;
116   Direction next_grow_dir_;
117   Direction depart_dir_;
118   UnfinishedSpan open_span_;
119   DynamicQueue depart_queue_;
120   DynamicQueue return_queue_;
121   State state_;
122 };
123
124 Dynamic_performer::Dynamic_performer ()
125   : script_event_ (0),
126     next_grow_dir_ (CENTER),
127     depart_dir_ (CENTER),
128     state_ (STATE_INITIAL)
129 {
130   span_events_[LEFT]
131   = span_events_[RIGHT] = 0;
132 }
133
134 bool
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           return true;
157         }
158       break;
159     }
160
161   return false;
162 }
163
164 void
165 Dynamic_performer::close_and_enqueue_span ()
166 {
167   if (!open_span_.dynamic_)
168     programming_error ("no open dynamic span");
169   else
170     {
171       DynamicQueue &dq
172       = (state_ == STATE_RETURN) ? return_queue_ : depart_queue_;
173
174       // Changing equalizer settings in the course of the performance does not
175       // seem very likely.  This is a fig leaf: Equalize these limit volumes
176       // now as the required context properties are current.  Note that only
177       // the limits at the end of the last span in the queue are kept.
178
179       // Resist diminishing to silence.  (Idea: Look up "ppppp"
180       // with dynamicAbsoluteVolumeFunction, however that would yield 0.25.)
181       const Real min_target = equalize_volume (0.1);
182       const Real max_target
183       = equalize_volume (Audio_span_dynamic::MAXIMUM_VOLUME);
184
185       open_span_.dynamic_->set_end_moment (now_mom ());
186       dq.push_back (open_span_, min_target, max_target);
187     }
188
189   open_span_ = UnfinishedSpan ();
190 }
191
192 // Set the starting and target volume for each span in the queue.  The gain
193 // (loss) of any (de)crescendo is proportional to its share of the total time
194 // spent changing.
195 void
196 Dynamic_performer::DynamicQueue::set_volume (Real start_vol,
197                                              Real target_vol)
198 {
199   const Real gain = target_vol - start_vol;
200   Real dur = 0; // duration of (de)crescendi processed so far
201   Real vol = start_vol;
202   for (vector<UnfinishedSpan>::iterator it = spans_.begin ();
203        it != spans_.end (); ++it)
204     {
205       const Real prev_vol = vol;
206       if (it->grow_dir_ != CENTER)
207         {
208           // grant this (de)crescendo its portion of the gain
209           dur += it->dynamic_->get_duration ();
210           vol = start_vol + gain * (dur / change_duration_);
211         }
212       it->dynamic_->set_volume (prev_vol, vol);
213     }
214 }
215
216 // Return a volume which is reasonably distant from the given start and end
217 // volumes in the given direction, for use as a peak volume in a passage with a
218 // crescendo followed by a decrescendo (or vice versa).  If the given volumes
219 // are equal, the returned volume is a also reasonable target volume for a
220 // single (de)crescendo.
221 //
222 // The given minimum and maximum volumes are the allowable dynamic range.
223 Real
224 Dynamic_performer::compute_departure_volume (Direction depart_dir,
225                                              Real start_vol,
226                                              Real end_vol,
227                                              Real min_vol,
228                                              Real max_vol)
229 {
230   if (depart_dir == CENTER)
231     return start_vol;
232
233   // Try to find a volume that is a minimum distance from the starting and
234   // ending volumes.  If the endpoint volumes differ, the nearer one is padded
235   // less than the farther one.
236   //
237   // Example: mf < ... > p.  The legacy behavior was to use a 25% of the
238   // dynamic range for a (de)crescendo to an unspecified target, and this tries
239   // to preserve that, but is not possible to use a 25% change for both the
240   // crescendo and the decrescendo and meet the constraints of this example.
241   // The decrescendo is a greater change than the crescendo.  Believing that
242   // 25% is already more than enough for either, pad using 25% for the greater
243   // change and 7% for the lesser change.
244   //
245   // Idea: Use a context property or callback, e.g. the difference between two
246   // dynamics in dynamicAbsoluteVolumeFunction.  0.25 is the default difference
247   // between "p" and "ff". (Isn't that rather wide for this purpose?)  0.07 is
248   // the default difference between "mp" and "mf".
249   const Real far_padding = 0.25;
250   const Real near_padding = 0.07;
251
252   // If for some reason one of the endpoints is already below the supposed
253   // minimum or maximum, just accept it.
254   min_vol = min (min (min_vol, start_vol), end_vol);
255   max_vol = max (max (max_vol, start_vol), end_vol);
256
257   const Real vol_range = max_vol - min_vol;
258
259   const Real near_vol = minmax (depart_dir, start_vol, end_vol)
260                     + depart_dir * near_padding * vol_range;
261   const Real far_vol = minmax (-depart_dir, start_vol, end_vol)
262                    + depart_dir * far_padding * vol_range;
263   const Real depart_vol = minmax (depart_dir, near_vol, far_vol);
264   return max (min (depart_vol, max_vol), min_vol);
265 }
266
267 Real
268 Dynamic_performer::finish_queued_spans (Real next_vol)
269 {
270   if (depart_queue_.spans_.empty ())
271     {
272       programming_error ("no dynamic span to finish");
273       return next_vol;
274     }
275
276   const Real start_vol = depart_queue_.spans_.front ().dynamic_->get_start_volume ();
277
278   if (return_queue_.spans_.empty ())
279     {
280       Real depart_vol = next_vol;
281
282       // If the next dynamic is not specified or is inconsistent with the
283       // direction of growth, choose a reasonable target.
284       if ((next_vol < 0) || (depart_dir_ != sign (next_vol - start_vol)))
285         {
286           depart_vol = compute_departure_volume (depart_dir_,
287                                                  start_vol, start_vol,
288                                                  depart_queue_.min_target_vol_,
289                                                  depart_queue_.max_target_vol_);
290         }
291
292       depart_queue_.set_volume (start_vol, depart_vol);
293       depart_queue_.clear ();
294       return (next_vol >= 0) ? next_vol : depart_vol;
295     }
296   else
297     {
298       // If the next dynamic is not specified, return to the starting volume.
299       const Real return_vol = (next_vol >= 0) ? next_vol : start_vol;
300       Real depart_vol = compute_departure_volume (depart_dir_,
301                                                   start_vol, return_vol,
302                                                   depart_queue_.min_target_vol_,
303                                                   depart_queue_.max_target_vol_);
304       depart_queue_.set_volume (start_vol, depart_vol);
305       depart_queue_.clear ();
306       return_queue_.set_volume (depart_vol, return_vol);
307       return_queue_.clear ();
308       return return_vol;
309     }
310 }
311
312 Real
313 Dynamic_performer::equalize_volume (Real volume)
314 {
315   /*
316     properties override default equaliser setting
317   */
318   SCM min = get_property ("midiMinimumVolume");
319   SCM max = get_property ("midiMaximumVolume");
320   if (scm_is_number (min) || scm_is_number (max))
321     {
322       Interval iv (Audio_span_dynamic::MINIMUM_VOLUME,
323                    Audio_span_dynamic::MAXIMUM_VOLUME);
324       if (scm_is_number (min))
325         iv[MIN] = scm_to_double (min);
326       if (scm_is_number (max))
327         iv[MAX] = scm_to_double (max);
328       volume = iv[MIN] + iv.length () * volume;
329     }
330   else
331     {
332       /*
333         urg, code duplication:: staff_performer
334       */
335       SCM s = get_property ("midiInstrument");
336
337       if (!scm_is_string (s))
338         s = get_property ("instrumentName");
339
340       if (!scm_is_string (s))
341         s = scm_from_ascii_string ("piano");
342
343       SCM eq = get_property ("instrumentEqualizer");
344       if (ly_is_procedure (eq))
345         s = scm_call_1 (eq, s);
346
347       if (is_number_pair (s))
348         {
349           Interval iv = ly_scm2interval (s);
350           volume = iv[MIN] + iv.length () * volume;
351         }
352     }
353   return std::max (std::min (volume, Audio_span_dynamic::MAXIMUM_VOLUME),
354                    Audio_span_dynamic::MINIMUM_VOLUME);
355 }
356
357 void
358 Dynamic_performer::finalize ()
359 {
360   if (open_span_.dynamic_)
361     close_and_enqueue_span ();
362   finish_queued_spans ();
363 }
364
365 Real
366 Dynamic_performer::look_up_absolute_volume (SCM dynamicString,
367                                             Real defaultValue)
368 {
369   SCM proc = get_property ("dynamicAbsoluteVolumeFunction");
370
371   SCM svolume = SCM_EOL;
372   if (ly_is_procedure (proc))
373     svolume = scm_call_1 (proc, dynamicString);
374
375   return robust_scm2double (svolume, defaultValue);
376 }
377
378 void
379 Dynamic_performer::process_music ()
380 {
381   Real volume = -1;
382
383   if (script_event_) // explicit dynamic
384     {
385       volume = look_up_absolute_volume (script_event_->get_property ("text"),
386                                         Audio_span_dynamic::DEFAULT_VOLUME);
387       volume = equalize_volume (volume);
388     }
389   else if (!open_span_.dynamic_) // first time only
390     {
391       // Idea: look_up_absolute_volume (ly_symbol2scm ("mf")).
392       // It is likely to change regtests.
393       volume = equalize_volume (Audio_span_dynamic::DEFAULT_VOLUME);
394     }
395
396   // end the current span at relevant points
397   if (open_span_.dynamic_
398       && (span_events_[START] || span_events_[STOP] || script_event_))
399     {
400       close_and_enqueue_span ();
401       if (script_event_)
402         {
403           state_ = STATE_INITIAL;
404           volume = finish_queued_spans (volume);
405         }
406     }
407
408   // start a new span so that some dynamic is always in effect
409   if (!open_span_.dynamic_)
410     {
411       if (drive_state_machine (next_grow_dir_))
412         volume = finish_queued_spans (volume);
413
414       // if not known by now, use a default volume for robustness
415       if (volume < 0)
416         volume = equalize_volume (Audio_span_dynamic::DEFAULT_VOLUME);
417
418       Stream_event *cause
419       = span_events_[START] ? span_events_[START]
420         : script_event_ ? script_event_
421         : span_events_[STOP];
422
423       open_span_.dynamic_ = new Audio_span_dynamic (now_mom (), volume);
424       open_span_.grow_dir_ = next_grow_dir_;
425       announce_element (Audio_element_info (open_span_.dynamic_, cause));
426     }
427 }
428
429 void
430 Dynamic_performer::stop_translation_timestep ()
431 {
432   script_event_ = 0;
433   span_events_[LEFT]
434   = span_events_[RIGHT] = 0;
435   next_grow_dir_ = CENTER;
436 }
437
438 void
439 Dynamic_performer::listen_decrescendo (Stream_event *r)
440 {
441   Direction d = to_dir (r->get_property ("span-direction"));
442   if (ASSIGN_EVENT_ONCE (span_events_[d], r) && (d == START))
443     next_grow_dir_ = SMALLER;
444 }
445
446 void
447 Dynamic_performer::listen_crescendo (Stream_event *r)
448 {
449   Direction d = to_dir (r->get_property ("span-direction"));
450   if (ASSIGN_EVENT_ONCE (span_events_[d], r) && (d == START))
451     next_grow_dir_ = BIGGER;
452 }
453
454 void
455 Dynamic_performer::listen_absolute_dynamic (Stream_event *r)
456 {
457   ASSIGN_EVENT_ONCE (script_event_, r);
458 }
459
460 void
461 Dynamic_performer::boot ()
462 {
463   ADD_LISTENER (Dynamic_performer, decrescendo);
464   ADD_LISTENER (Dynamic_performer, crescendo);
465   ADD_LISTENER (Dynamic_performer, absolute_dynamic);
466 }
467
468 ADD_TRANSLATOR (Dynamic_performer,
469                 /* doc */
470                 "",
471
472                 /* create */
473                 "",
474
475                 /* read */
476                 "dynamicAbsoluteVolumeFunction "
477                 "instrumentEqualizer "
478                 "midiMaximumVolume "
479                 "midiMinimumVolume "
480                 "midiInstrument ",
481
482                 /* write */
483                 ""
484                );