]> git.donarmstrong.com Git - lilypond.git/blob - lily/dynamic-performer.cc
Issue 4048 (2/5) Dynamic_performer: represent dynamics as a piecewise
[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   // next_vol < 0 means select a target dynamic based on growth direction.
44   // return actual next volume (computed if not provided)
45   Real end_span (Real next_vol = -1.0);
46
47 private:
48   Stream_event *script_event_;
49   Drul_array<Stream_event *> span_events_;
50   Direction next_grow_dir_;
51   Audio_span_dynamic *span_dynamic_;
52   Direction grow_dir_; // of span_dynamic_
53 };
54
55 Dynamic_performer::Dynamic_performer ()
56 {
57   script_event_ = 0;
58   span_events_[LEFT]
59     = span_events_[RIGHT] = 0;
60   next_grow_dir_ = CENTER;
61   span_dynamic_ = 0;
62   grow_dir_ = CENTER;
63 }
64
65 Real Dynamic_performer::end_span (Real next_vol)
66 {
67   if (!span_dynamic_)
68     {
69       programming_error("no dynamic span to end");
70       return next_vol;
71     }
72
73   Real start_vol = span_dynamic_->get_start_volume ();
74   Real target_vol = start_vol;
75
76   if (grow_dir_ != CENTER) {
77     // If the target dynamic is not specified, grow to a reasonable target
78     // in the desired direction.  Do the same for cases like mf < p.
79     //
80     // TODO To improve on this, keep a queue of Audio_span_dynamics and compute
81     // multiple intermediate targets based on the next explicit dynamic.
82     // Consider cases like mf < ... < ff with only mf and ff specified.
83     // Consider growing in proportion to the duration of each (de)crescendo in
84     // the sequence, which may be separated by spans with no change in volume.
85     if ((next_vol < 0) || (sign(next_vol - start_vol) != grow_dir_))
86       {
87         Real min_vol = equalize_volume (0.1);
88         Real max_vol = equalize_volume (Audio_span_dynamic::MAXIMUM_VOLUME);
89         target_vol = max (min (start_vol + grow_dir_ * (max_vol - min_vol) * 0.25, max_vol), min_vol);
90       }
91     else
92       {
93         target_vol = next_vol;
94       }
95   }
96
97   span_dynamic_->set_end_moment (now_mom ());
98   span_dynamic_->set_volume (start_vol, target_vol);
99   span_dynamic_ = 0;
100
101   return (next_vol >= 0) ? next_vol : target_vol;
102 }
103
104 Real
105 Dynamic_performer::equalize_volume (Real volume)
106 {
107   /*
108     properties override default equaliser setting
109   */
110   SCM min = get_property ("midiMinimumVolume");
111   SCM max = get_property ("midiMaximumVolume");
112   if (scm_is_number (min) || scm_is_number (max))
113     {
114       Interval iv (Audio_span_dynamic::MINIMUM_VOLUME,
115                    Audio_span_dynamic::MAXIMUM_VOLUME);
116       if (scm_is_number (min))
117         iv[MIN] = scm_to_double (min);
118       if (scm_is_number (max))
119         iv[MAX] = scm_to_double (max);
120       volume = iv[MIN] + iv.length () * volume;
121     }
122   else
123     {
124       /*
125         urg, code duplication:: staff_performer
126       */
127       SCM s = get_property ("midiInstrument");
128
129       if (!scm_is_string (s))
130         s = get_property ("instrumentName");
131
132       if (!scm_is_string (s))
133         s = scm_from_ascii_string ("piano");
134
135       SCM eq = get_property ("instrumentEqualizer");
136       if (ly_is_procedure (eq))
137         s = scm_call_1 (eq, s);
138
139       if (is_number_pair (s))
140         {
141           Interval iv = ly_scm2interval (s);
142           volume = iv[MIN] + iv.length () * volume;
143         }
144     }
145   return std::max (std::min (volume, Audio_span_dynamic::MAXIMUM_VOLUME),
146                    Audio_span_dynamic::MINIMUM_VOLUME);
147 }
148
149 void
150 Dynamic_performer::finalize ()
151 {
152   if (span_dynamic_)
153     {
154       end_span ();
155     }
156 }
157
158 void
159 Dynamic_performer::process_music ()
160 {
161   Real volume = -1;
162
163   if (script_event_)
164     {
165       // Explicit dynamic script event: determine the volume.
166       SCM proc = get_property ("dynamicAbsoluteVolumeFunction");
167
168       SCM svolume = SCM_EOL;
169       if (ly_is_procedure (proc))
170         {
171           // urg
172           svolume = scm_call_1 (proc, script_event_->get_property ("text"));
173         }
174
175       volume = equalize_volume (robust_scm2double (svolume, Audio_span_dynamic::DEFAULT_VOLUME));
176     }
177   else if (!span_dynamic_) // first time through
178     {
179       volume = equalize_volume (Audio_span_dynamic::DEFAULT_VOLUME);
180     }
181
182   // end the current span at relevant points
183   if (span_dynamic_
184       && (span_events_[START] || span_events_[STOP] || script_event_))
185     {
186       volume = end_span (volume);
187     }
188
189   // start a new span so that some dynamic is always in effect
190   if (!span_dynamic_)
191     {
192       Stream_event *cause =
193         span_events_[START] ? span_events_[START] :
194         script_event_ ? script_event_ :
195         span_events_[STOP];
196
197       span_dynamic_ = new Audio_span_dynamic (now_mom (), volume);
198       grow_dir_ = next_grow_dir_;
199       announce_element (Audio_element_info (span_dynamic_, cause));
200     }
201 }
202
203 void
204 Dynamic_performer::stop_translation_timestep ()
205 {
206   script_event_ = 0;
207   span_events_[LEFT]
208     = span_events_[RIGHT] = 0;
209   next_grow_dir_ = CENTER;
210 }
211
212 void
213 Dynamic_performer::listen_decrescendo (Stream_event *r)
214 {
215   Direction d = to_dir (r->get_property ("span-direction"));
216   if (ASSIGN_EVENT_ONCE (span_events_[d], r) && (d == START))
217     next_grow_dir_ = SMALLER;
218 }
219
220 void
221 Dynamic_performer::listen_crescendo (Stream_event *r)
222 {
223   Direction d = to_dir (r->get_property ("span-direction"));
224   if (ASSIGN_EVENT_ONCE (span_events_[d], r) && (d == START))
225     next_grow_dir_ = BIGGER;
226 }
227
228 void
229 Dynamic_performer::listen_absolute_dynamic (Stream_event *r)
230 {
231   ASSIGN_EVENT_ONCE (script_event_, r);
232 }
233
234 void
235 Dynamic_performer::boot ()
236 {
237   ADD_LISTENER (Dynamic_performer, decrescendo);
238   ADD_LISTENER (Dynamic_performer, crescendo);
239   ADD_LISTENER (Dynamic_performer, absolute_dynamic);
240 }
241
242 ADD_TRANSLATOR (Dynamic_performer,
243                 /* doc */
244                 "",
245
246                 /* create */
247                 "",
248
249                 /* read */
250                 "dynamicAbsoluteVolumeFunction "
251                 "instrumentEqualizer "
252                 "midiMaximumVolume "
253                 "midiMinimumVolume "
254                 "midiInstrument ",
255
256                 /* write */
257                 ""
258                );