/*
This file is part of LilyPond, the GNU music typesetter.
- Copyright (C) 2000--2014 Jan Nieuwenhuizen <janneke@gnu.org>
+ Copyright (C) 2000--2015 Jan Nieuwenhuizen <janneke@gnu.org>
LilyPond is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include "performer.hh"
#include "audio-item.hh"
+#include "std-vector.hh"
#include "stream-event.hh"
#include "international.hh"
public:
TRANSLATOR_DECLARATIONS (Dynamic_performer);
protected:
+ virtual void finalize ();
void stop_translation_timestep ();
void process_music ();
Real equalize_volume (Real);
- DECLARE_TRANSLATOR_LISTENER (decrescendo);
- DECLARE_TRANSLATOR_LISTENER (crescendo);
- DECLARE_TRANSLATOR_LISTENER (absolute_dynamic);
+ void listen_decrescendo (Stream_event *);
+ void listen_crescendo (Stream_event *);
+ void listen_absolute_dynamic (Stream_event *);
+
+private:
+ // next_vol < 0 means select a target dynamic based on growth direction.
+ // return actual next volume (computed if not provided)
+ Real end_span (Real next_vol = -1.0);
+
private:
Stream_event *script_event_;
Drul_array<Stream_event *> span_events_;
- Drul_array<Direction> grow_dir_;
- Real last_volume_;
- bool last_volume_initialized_;
- Audio_dynamic *absolute_;
+ Direction next_grow_dir_;
Audio_span_dynamic *span_dynamic_;
- Audio_span_dynamic *finished_span_dynamic_;
+ Direction grow_dir_; // of span_dynamic_
};
Dynamic_performer::Dynamic_performer ()
{
- last_volume_ = 0.0;
- last_volume_initialized_ = false;
script_event_ = 0;
- absolute_ = 0;
span_events_[LEFT]
= span_events_[RIGHT] = 0;
+ next_grow_dir_ = CENTER;
+ span_dynamic_ = 0;
+ grow_dir_ = CENTER;
+}
+
+Real Dynamic_performer::end_span (Real next_vol)
+{
+ if (!span_dynamic_)
+ {
+ programming_error("no dynamic span to end");
+ return next_vol;
+ }
+
+ Real start_vol = span_dynamic_->get_start_volume ();
+ Real target_vol = start_vol;
+
+ if (grow_dir_ != CENTER) {
+ // If the target dynamic is not specified, grow to a reasonable target
+ // in the desired direction. Do the same for cases like mf < p.
+ //
+ // TODO To improve on this, keep a queue of Audio_span_dynamics and compute
+ // multiple intermediate targets based on the next explicit dynamic.
+ // Consider cases like mf < ... < ff with only mf and ff specified.
+ // Consider growing in proportion to the duration of each (de)crescendo in
+ // the sequence, which may be separated by spans with no change in volume.
+ if ((next_vol < 0) || (sign(next_vol - start_vol) != grow_dir_))
+ {
+ Real min_vol = equalize_volume (0.1);
+ Real max_vol = equalize_volume (Audio_span_dynamic::MAXIMUM_VOLUME);
+ target_vol = max (min (start_vol + grow_dir_ * (max_vol - min_vol) * 0.25, max_vol), min_vol);
+ }
+ else
+ {
+ target_vol = next_vol;
+ }
+ }
+
+ span_dynamic_->set_end_moment (now_mom ());
+ span_dynamic_->set_volume (start_vol, target_vol);
span_dynamic_ = 0;
- finished_span_dynamic_ = 0;
+
+ return (next_vol >= 0) ? next_vol : target_vol;
}
Real
SCM max = get_property ("midiMaximumVolume");
if (scm_is_number (min) || scm_is_number (max))
{
- Interval iv (0, 1);
+ Interval iv (Audio_span_dynamic::MINIMUM_VOLUME,
+ Audio_span_dynamic::MAXIMUM_VOLUME);
if (scm_is_number (min))
iv[MIN] = scm_to_double (min);
if (scm_is_number (max))
s = get_property ("instrumentName");
if (!scm_is_string (s))
- s = scm_from_locale_string ("piano");
+ s = scm_from_ascii_string ("piano");
SCM eq = get_property ("instrumentEqualizer");
if (ly_is_procedure (eq))
volume = iv[MIN] + iv.length () * volume;
}
}
- return volume;
+ return std::max (std::min (volume, Audio_span_dynamic::MAXIMUM_VOLUME),
+ Audio_span_dynamic::MINIMUM_VOLUME);
}
void
-Dynamic_performer::process_music ()
+Dynamic_performer::finalize ()
{
- if (span_events_[STOP] || script_event_)
+ if (span_dynamic_)
{
- // End of a dynamic spanner, or an explicit dynamic script event.
- finished_span_dynamic_ = span_dynamic_;
- span_dynamic_ = 0;
+ end_span ();
}
+}
- if (span_events_[START])
- {
- // Start of a dynamic spanner. Create a new Audio_span_dynamic for
- // collecting changes in dynamics within this spanner.
- span_dynamic_ = new Audio_span_dynamic (equalize_volume (0.1), equalize_volume (1.0));
- announce_element (Audio_element_info (span_dynamic_, span_events_[START]));
-
- span_dynamic_->grow_dir_ = grow_dir_[START];
-
- if (!last_volume_initialized_ && !script_event_)
- {
- // No explicit dynamic script events have occurred yet, but there is
- // nevertheless a dynamic spanner. Initialize last_volume_ to a
- // value within the available range.
- span_events_[START]->origin ()->warning (_ ("(De)crescendo with unspecified starting volume in MIDI."));
- last_volume_ = equalize_volume (0.5);
- last_volume_initialized_ = true;
- }
- }
+void
+Dynamic_performer::process_music ()
+{
+ Real volume = -1;
- if (script_event_
- || span_dynamic_
- || finished_span_dynamic_)
+ if (script_event_)
{
- // New change in dynamics.
- absolute_ = new Audio_dynamic ();
+ // Explicit dynamic script event: determine the volume.
+ SCM proc = get_property ("dynamicAbsoluteVolumeFunction");
- if (script_event_)
+ SCM svolume = SCM_EOL;
+ if (ly_is_procedure (proc))
{
- // Explicit dynamic script event: determine the volume.
- SCM proc = get_property ("dynamicAbsoluteVolumeFunction");
-
- SCM svolume = SCM_EOL;
- if (ly_is_procedure (proc))
- {
- // urg
- svolume = scm_call_1 (proc, script_event_->get_property ("text"));
- }
-
- Real volume = robust_scm2double (svolume, 0.5);
-
- last_volume_
- = absolute_->volume_ = equalize_volume (volume);
- last_volume_initialized_ = true;
+ // urg
+ svolume = scm_call_1 (proc, script_event_->get_property ("text"));
}
- Audio_element_info info (absolute_, script_event_);
- announce_element (info);
+ volume = equalize_volume (robust_scm2double (svolume, Audio_span_dynamic::DEFAULT_VOLUME));
}
-
- if (!last_volume_initialized_)
+ else if (!span_dynamic_) // first time through
{
- absolute_ = new Audio_dynamic ();
-
- last_volume_
- = absolute_->volume_ = equalize_volume (0.71); // Backward compatible
- last_volume_initialized_ = true;
-
- Audio_element_info info (absolute_, script_event_);
- announce_element (info);
+ volume = equalize_volume (Audio_span_dynamic::DEFAULT_VOLUME);
}
- if (span_dynamic_)
- span_dynamic_->add_absolute (absolute_);
-
- if (finished_span_dynamic_)
- finished_span_dynamic_->add_absolute (absolute_);
-}
-
-void
-Dynamic_performer::stop_translation_timestep ()
-{
- if (finished_span_dynamic_)
+ // end the current span at relevant points
+ if (span_dynamic_
+ && (span_events_[START] || span_events_[STOP] || script_event_))
{
- finished_span_dynamic_->render ();
- finished_span_dynamic_ = 0;
+ volume = end_span (volume);
}
- if (absolute_ && absolute_->volume_ < 0)
- {
- absolute_->volume_ = last_volume_;
- }
- else if (absolute_)
+ // start a new span so that some dynamic is always in effect
+ if (!span_dynamic_)
{
- last_volume_ = absolute_->volume_;
- last_volume_initialized_ = true;
+ Stream_event *cause =
+ span_events_[START] ? span_events_[START] :
+ script_event_ ? script_event_ :
+ span_events_[STOP];
+
+ span_dynamic_ = new Audio_span_dynamic (now_mom (), volume);
+ grow_dir_ = next_grow_dir_;
+ announce_element (Audio_element_info (span_dynamic_, cause));
}
+}
- absolute_ = 0;
+void
+Dynamic_performer::stop_translation_timestep ()
+{
script_event_ = 0;
span_events_[LEFT]
= span_events_[RIGHT] = 0;
+ next_grow_dir_ = CENTER;
}
-IMPLEMENT_TRANSLATOR_LISTENER (Dynamic_performer, decrescendo);
void
Dynamic_performer::listen_decrescendo (Stream_event *r)
{
Direction d = to_dir (r->get_property ("span-direction"));
- span_events_[d] = r;
- grow_dir_[d] = SMALLER;
+ if (ASSIGN_EVENT_ONCE (span_events_[d], r) && (d == START))
+ next_grow_dir_ = SMALLER;
}
-IMPLEMENT_TRANSLATOR_LISTENER (Dynamic_performer, crescendo);
void
Dynamic_performer::listen_crescendo (Stream_event *r)
{
Direction d = to_dir (r->get_property ("span-direction"));
- span_events_[d] = r;
- grow_dir_[d] = BIGGER;
+ if (ASSIGN_EVENT_ONCE (span_events_[d], r) && (d == START))
+ next_grow_dir_ = BIGGER;
}
-IMPLEMENT_TRANSLATOR_LISTENER (Dynamic_performer, absolute_dynamic);
void
Dynamic_performer::listen_absolute_dynamic (Stream_event *r)
{
- if (!script_event_)
- script_event_ = r;
+ ASSIGN_EVENT_ONCE (script_event_, r);
+}
+
+void
+Dynamic_performer::boot ()
+{
+ ADD_LISTENER (Dynamic_performer, decrescendo);
+ ADD_LISTENER (Dynamic_performer, crescendo);
+ ADD_LISTENER (Dynamic_performer, absolute_dynamic);
}
ADD_TRANSLATOR (Dynamic_performer,