2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2000--2015 Jan Nieuwenhuizen <janneke@gnu.org>
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.
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.
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/>.
20 #include "performer.hh"
21 #include "audio-item.hh"
22 #include "std-vector.hh"
23 #include "stream-event.hh"
24 #include "international.hh"
26 #include "translator.icc"
28 class Dynamic_performer : public Performer
31 TRANSLATOR_DECLARATIONS (Dynamic_performer);
33 virtual void finalize ();
34 void stop_translation_timestep ();
35 void process_music ();
36 Real equalize_volume (Real);
38 void listen_decrescendo (Stream_event *);
39 void listen_crescendo (Stream_event *);
40 void listen_absolute_dynamic (Stream_event *);
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);
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_
55 Dynamic_performer::Dynamic_performer ()
59 = span_events_[RIGHT] = 0;
60 next_grow_dir_ = CENTER;
65 Real Dynamic_performer::end_span (Real next_vol)
69 programming_error("no dynamic span to end");
73 Real start_vol = span_dynamic_->get_start_volume ();
74 Real target_vol = start_vol;
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.
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_))
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);
93 target_vol = next_vol;
97 span_dynamic_->set_end_moment (now_mom ());
98 span_dynamic_->set_volume (start_vol, target_vol);
101 return (next_vol >= 0) ? next_vol : target_vol;
105 Dynamic_performer::equalize_volume (Real volume)
108 properties override default equaliser setting
110 SCM min = get_property ("midiMinimumVolume");
111 SCM max = get_property ("midiMaximumVolume");
112 if (scm_is_number (min) || scm_is_number (max))
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;
125 urg, code duplication:: staff_performer
127 SCM s = get_property ("midiInstrument");
129 if (!scm_is_string (s))
130 s = get_property ("instrumentName");
132 if (!scm_is_string (s))
133 s = scm_from_ascii_string ("piano");
135 SCM eq = get_property ("instrumentEqualizer");
136 if (ly_is_procedure (eq))
137 s = scm_call_1 (eq, s);
139 if (is_number_pair (s))
141 Interval iv = ly_scm2interval (s);
142 volume = iv[MIN] + iv.length () * volume;
145 return std::max (std::min (volume, Audio_span_dynamic::MAXIMUM_VOLUME),
146 Audio_span_dynamic::MINIMUM_VOLUME);
150 Dynamic_performer::finalize ()
159 Dynamic_performer::process_music ()
165 // Explicit dynamic script event: determine the volume.
166 SCM proc = get_property ("dynamicAbsoluteVolumeFunction");
168 SCM svolume = SCM_EOL;
169 if (ly_is_procedure (proc))
172 svolume = scm_call_1 (proc, script_event_->get_property ("text"));
175 volume = equalize_volume (robust_scm2double (svolume, Audio_span_dynamic::DEFAULT_VOLUME));
177 else if (!span_dynamic_) // first time through
179 volume = equalize_volume (Audio_span_dynamic::DEFAULT_VOLUME);
182 // end the current span at relevant points
184 && (span_events_[START] || span_events_[STOP] || script_event_))
186 volume = end_span (volume);
189 // start a new span so that some dynamic is always in effect
192 Stream_event *cause =
193 span_events_[START] ? span_events_[START] :
194 script_event_ ? script_event_ :
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));
204 Dynamic_performer::stop_translation_timestep ()
208 = span_events_[RIGHT] = 0;
209 next_grow_dir_ = CENTER;
213 Dynamic_performer::listen_decrescendo (Stream_event *r)
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;
221 Dynamic_performer::listen_crescendo (Stream_event *r)
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;
229 Dynamic_performer::listen_absolute_dynamic (Stream_event *r)
231 ASSIGN_EVENT_ONCE (script_event_, r);
235 Dynamic_performer::boot ()
237 ADD_LISTENER (Dynamic_performer, decrescendo);
238 ADD_LISTENER (Dynamic_performer, crescendo);
239 ADD_LISTENER (Dynamic_performer, absolute_dynamic);
242 ADD_TRANSLATOR (Dynamic_performer,
250 "dynamicAbsoluteVolumeFunction "
251 "instrumentEqualizer "