summaryrefslogtreecommitdiff
path: root/lily
diff options
context:
space:
mode:
authorDan Eble <nine.fierce.ballads@gmail.com>2016-06-15 18:40:01 -0400
committerDan Eble <nine.fierce.ballads@gmail.com>2016-07-19 07:50:08 -0400
commit14f464d957b6a68710f0364fc7507b63eb0fcee6 (patch)
tree38892a876676d77382c9afa92f16a022e7338c8b /lily
parent3c35dd1538ca2b3a3a446e60ae9dc6b06234d78b (diff)
Issue 4048 (2/5) Dynamic_performer: represent dynamics as a piecewise
linear function rather than a collection of discrete points
Diffstat (limited to 'lily')
-rw-r--r--lily/audio-item.cc95
-rw-r--r--lily/dynamic-performer.cc181
-rw-r--r--lily/include/audio-item.hh36
-rw-r--r--lily/include/midi-item.hh11
-rw-r--r--lily/midi-item.cc41
-rw-r--r--lily/staff-performer.cc78
6 files changed, 202 insertions, 240 deletions
diff --git a/lily/audio-item.cc b/lily/audio-item.cc
index 97a15d552a..06603584b9 100644
--- a/lily/audio-item.cc
+++ b/lily/audio-item.cc
@@ -21,6 +21,7 @@
#include "midi-item.hh"
#include "audio-column.hh"
+#include "international.hh"
Audio_instrument::Audio_instrument (string instrument_string)
{
@@ -101,24 +102,15 @@ Audio_key::Audio_key (int acc, bool major)
major_ = major;
}
-Audio_dynamic::Audio_dynamic ()
- : volume_ (-1),
- silent_ (false)
-{
-}
-
-Audio_span_dynamic::Audio_span_dynamic (Real min_volume, Real max_volume)
-{
- grow_dir_ = CENTER;
- min_volume_ = min_volume;
- max_volume_ = max_volume;
-}
+const Real Audio_span_dynamic::MINIMUM_VOLUME;
+const Real Audio_span_dynamic::MAXIMUM_VOLUME;
+const Real Audio_span_dynamic::DEFAULT_VOLUME;
-void
-Audio_span_dynamic::add_absolute (Audio_dynamic *d)
+Audio_span_dynamic::Audio_span_dynamic (Moment mom, Real volume)
+ : start_moment_ (mom),
+ duration_ (0)
{
- assert (d);
- dynamics_.push_back (d);
+ set_volume (volume, volume);
}
Moment
@@ -140,53 +132,60 @@ moment_to_ticks (Moment m)
return int (moment_to_real (m) * 384 * 4);
}
-void
-Audio_span_dynamic::render ()
+void Audio_span_dynamic::set_end_moment(Moment mom)
{
- if (dynamics_.size () <= 1)
- return;
-
- assert (dynamics_[0]->volume_ >= 0);
-
- while (dynamics_.back ()->volume_ > 0
- && dynamics_.size () > 1
- && sign (dynamics_.back ()->volume_ - dynamics_[0]->volume_) != grow_dir_)
+ if (mom < start_moment_)
{
- dynamics_.erase (dynamics_.end () - 1);
+ programming_error (_f ("end moment (%s) < start moment (%s)",
+ mom.to_string ().c_str (),
+ start_moment_.to_string ().c_str ()));
+ mom = start_moment_;
}
- if (dynamics_.size () <= 1)
+ duration_ = moment_to_real (mom - start_moment_);
+}
+
+void
+Audio_span_dynamic::set_volume (Real start, Real target)
+{
+ if (!(start >= 0))
{
- programming_error ("Impossible or ambiguous (de)crescendo in MIDI.");
- return;
+ programming_error (_f ("invalid start volume: %f", start));
+ start = DEFAULT_VOLUME;
}
- Real start_v = dynamics_[0]->volume_;
- if (dynamics_.back ()->volume_ < 0)
+ if (!(target >= 0))
{
- // The dynamic spanner does not end with an explicit dynamic script
- // event. Adjust the end volume by at most 1/4 of the available
- // volume range in this case.
- dynamics_.back ()->volume_ = max (min (start_v + grow_dir_ * (max_volume_ - min_volume_) * 0.25, max_volume_), min_volume_);
+ programming_error (_f ("invalid target volume: %f", target));
+ target = start;
}
- Real delta_v = dynamics_.back ()->volume_ - dynamics_[0]->volume_;
-
- Moment start = dynamics_[0]->get_column ()->when ();
+ start_volume_ = start;
+ gain_ = target - start;
+}
- Real total_t = moment_to_real (dynamics_.back ()->get_column ()->when () - start);
+Real Audio_span_dynamic::get_volume (Moment mom) const
+{
+ const Real when = moment_to_real (mom - start_moment_);
- for (vsize i = 1; i < dynamics_.size (); i++)
+ if (when <= 0)
{
- Moment dt_moment = dynamics_[i]->get_column ()->when ()
- - start;
-
- Real dt = moment_to_real (dt_moment);
-
- Real v = start_v + delta_v * (dt / total_t);
+ if (when < 0)
+ programming_error (_f ("asked to compute volume at %f for dynamic span of duration %f starting at %s",
+ when, duration_,
+ start_moment_.to_string ().c_str ()));
+ return start_volume_;
+ }
- dynamics_[i]->volume_ = v;
+ if (when >= duration_)
+ {
+ programming_error (_f ("asked to compute volume at +%f for dynamic span of duration %f starting at %s",
+ when, duration_,
+ start_moment_.to_string ().c_str ()));
+ return start_volume_ + gain_;
}
+
+ return start_volume_ + gain_ * (when / duration_);
}
Audio_tempo::Audio_tempo (int per_minute_4)
diff --git a/lily/dynamic-performer.cc b/lily/dynamic-performer.cc
index 5ce67f0463..ab0005b8f4 100644
--- a/lily/dynamic-performer.cc
+++ b/lily/dynamic-performer.cc
@@ -19,6 +19,7 @@
#include "performer.hh"
#include "audio-item.hh"
+#include "std-vector.hh"
#include "stream-event.hh"
#include "international.hh"
@@ -29,6 +30,7 @@ class Dynamic_performer : public Performer
public:
TRANSLATOR_DECLARATIONS (Dynamic_performer);
protected:
+ virtual void finalize ();
void stop_translation_timestep ();
void process_music ();
Real equalize_volume (Real);
@@ -36,25 +38,67 @@ protected:
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_;
- 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_ = -1;
script_event_ = 0;
- absolute_ = 0;
span_events_[LEFT]
= span_events_[RIGHT] = 0;
+ next_grow_dir_ = CENTER;
span_dynamic_ = 0;
- finished_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;
+
+ return (next_vol >= 0) ? next_vol : target_vol;
}
Real
@@ -67,7 +111,8 @@ Dynamic_performer::equalize_volume (Real volume)
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))
@@ -97,125 +142,93 @@ Dynamic_performer::equalize_volume (Real volume)
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_[START] || span_events_[STOP] || script_event_)
+ if (span_dynamic_)
{
- // End the previous spanner when a new one begins or at an explicit stop
- // or absolute dynamic.
- 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];
- }
+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);
+ // 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_ < 0)
+ else if (!span_dynamic_) // first time through
{
- absolute_ = new Audio_dynamic ();
-
- last_volume_
- = absolute_->volume_ = equalize_volume (0.71); // Backward compatible
-
- 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_)
+ // start a new span so that some dynamic is always in effect
+ if (!span_dynamic_)
{
- if (absolute_->volume_ < 0)
- {
- absolute_->volume_ = last_volume_;
- }
- else
- {
- last_volume_ = absolute_->volume_;
- }
+ 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;
}
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;
}
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;
}
void
Dynamic_performer::listen_absolute_dynamic (Stream_event *r)
{
- if (!script_event_)
- script_event_ = r;
+ ASSIGN_EVENT_ONCE (script_event_, r);
}
void
diff --git a/lily/include/audio-item.hh b/lily/include/audio-item.hh
index 8c41d18a52..ae5c96ae95 100644
--- a/lily/include/audio-item.hh
+++ b/lily/include/audio-item.hh
@@ -40,26 +40,30 @@ private:
Audio_item &operator = (Audio_item const &);
};
-class Audio_dynamic : public Audio_item
+// Audio_span_dynamic is open at the end of the interval, so the volume
+// grows/diminshes toward a target, but whether it reaches it depends on the
+// next Audio_span_dynamic in the performance. For example, a crescendo
+// notated as mf < p is represented as [mf < x) [p ...) i.e. growth to some
+// volume louder than mf followed by an abrupt change to p.
+class Audio_span_dynamic : public Audio_element
{
public:
- Audio_dynamic ();
+ static const Real MINIMUM_VOLUME = 0.0;
+ static const Real MAXIMUM_VOLUME = 1.0;
+ static const Real DEFAULT_VOLUME = 90.0 / 127.0;
- Real volume_;
- bool silent_;
-};
+private:
+ Moment start_moment_;
+ Real start_volume_;
+ Real duration_; // = target moment - start moment
+ Real gain_; // = target volume - start volume
-class Audio_span_dynamic : public Audio_element
-{
public:
- Direction grow_dir_;
- vector<Audio_dynamic *> dynamics_;
- Real min_volume_;
- Real max_volume_;
-
- virtual void render ();
- void add_absolute (Audio_dynamic *);
- Audio_span_dynamic (Real min_volume, Real max_volume);
+ Real get_start_volume () const { return start_volume_; }
+ void set_end_moment (Moment);
+ void set_volume (Real start, Real target);
+ Real get_volume (Moment) const;
+ Audio_span_dynamic (Moment mom, Real volume);
};
class Audio_key : public Audio_item
@@ -92,7 +96,7 @@ public:
Pitch pitch_;
Moment length_mom_;
Pitch transposing_;
- Audio_dynamic *dynamic_;
+ Audio_span_dynamic *dynamic_;
int extra_velocity_;
Audio_note *tied_;
diff --git a/lily/include/midi-item.hh b/lily/include/midi-item.hh
index b593ce1527..dff3f4df9b 100644
--- a/lily/include/midi-item.hh
+++ b/lily/include/midi-item.hh
@@ -157,17 +157,6 @@ public:
Audio_text *audio_;
};
-class Midi_dynamic : public Midi_channel_item
-{
-public:
- Midi_dynamic (Audio_dynamic *);
- DECLARE_CLASSNAME (Midi_dynamic);
-
- virtual string to_string () const;
-
- Audio_dynamic *audio_;
-};
-
class Midi_piano_pedal : public Midi_channel_item
{
public:
diff --git a/lily/midi-item.cc b/lily/midi-item.cc
index 33dd9f11bd..7f064e24a8 100644
--- a/lily/midi-item.cc
+++ b/lily/midi-item.cc
@@ -19,6 +19,7 @@
#include "midi-item.hh"
+#include "audio-column.hh"
#include "duration.hh"
#include "international.hh"
#include "libc-extension.hh"
@@ -42,8 +43,6 @@ Midi_item::get_midi (Audio_item *a)
return i->str_.length () ? new Midi_instrument (i) : 0;
else if (Audio_note *i = dynamic_cast<Audio_note *> (a))
return new Midi_note (i);
- else if (Audio_dynamic *i = dynamic_cast<Audio_dynamic *> (a))
- return new Midi_dynamic (i);
else if (Audio_piano_pedal *i = dynamic_cast<Audio_piano_pedal *> (a))
return new Midi_piano_pedal (i);
else if (Audio_tempo *i = dynamic_cast<Audio_tempo *> (a))
@@ -193,8 +192,8 @@ Midi_time_signature::to_string () const
Midi_note::Midi_note (Audio_note *a)
: Midi_channel_item (a),
audio_ (a),
- dynamic_byte_ (min (max (Byte ((a->dynamic_ && a->dynamic_->volume_ >= 0
- ? a->dynamic_->volume_ * 0x7f : 0x5a)
+ dynamic_byte_ (min (max (Byte ((a->dynamic_
+ ? a->dynamic_->get_volume (a->audio_column_->when ()) * 0x7f : 0x5a)
+ a->extra_velocity_),
Byte (0)), Byte (0x7f)))
{
@@ -275,40 +274,6 @@ Midi_note_off::to_string () const
return str;
}
-Midi_dynamic::Midi_dynamic (Audio_dynamic *a)
- : Midi_channel_item (a),
- audio_ (a)
-{
-}
-
-string
-Midi_dynamic::to_string () const
-{
- Byte status_byte = (char) (0xB0 + channel_);
- string str = ::to_string ((char)status_byte);
-
- /*
- Main volume controller (per channel):
- 07 MSB
- 27 LSB
- */
- static Real const full_scale = 127;
-
- int volume = (int) (audio_->volume_ * full_scale);
- if (volume <= 0)
- volume = 1;
- if (volume > full_scale)
- volume = (int)full_scale;
-
- int const volume_default = 100;
- if (audio_->volume_ < 0 || audio_->silent_)
- volume = volume_default;
-
- str += ::to_string ((char)0x07);
- str += ::to_string ((char)volume);
- return str;
-}
-
Midi_piano_pedal::Midi_piano_pedal (Audio_piano_pedal *a)
: Midi_channel_item (a),
audio_ (a)
diff --git a/lily/staff-performer.cc b/lily/staff-performer.cc
index 3a05cbd0e2..505da3adba 100644
--- a/lily/staff-performer.cc
+++ b/lily/staff-performer.cc
@@ -54,7 +54,7 @@ private:
int get_channel (const string &instrument);
Audio_staff *get_audio_staff (const string &voice);
Audio_staff *new_audio_staff (const string &voice);
- Audio_dynamic *get_dynamic (const string &voice);
+ Audio_span_dynamic *get_dynamic (const string &voice);
string instrument_string_;
int channel_;
@@ -65,7 +65,7 @@ private:
map<string, deque<Audio_note *> > note_map_;
map<string, Audio_staff *> staff_map_;
map<string, int> channel_map_;
- map<string, Audio_dynamic *> dynamic_map_;
+ map<string, Audio_span_dynamic *> dynamic_map_;
// Would prefer to have the following two items be
// members of the containing class Performance,
// so they can be reset for each new midi file output.
@@ -184,10 +184,10 @@ Staff_performer::get_audio_staff (const string &voice)
return new_audio_staff (voice);
}
-Audio_dynamic *
+Audio_span_dynamic *
Staff_performer::get_dynamic (const string &voice)
{
- map<string, Audio_dynamic *>::const_iterator i = dynamic_map_.find (voice);
+ map<string, Audio_span_dynamic *>::const_iterator i = dynamic_map_.find (voice);
if (i != dynamic_map_.end ())
return i->second;
return 0;
@@ -227,13 +227,12 @@ Staff_performer::stop_translation_timestep ()
instrument_name_ = 0;
instrument_ = 0;
// For each voice with a note played in the current translation time step,
- // check if the voice has an Audio_dynamic registered: if yes, apply this
- // dynamic to every note played in the voice in the current translation time
- // step.
+ // check if the voice has a dynamic registered: if yes, apply the dynamic
+ // to every note played in the voice in the current translation time step.
for (map<string, deque<Audio_note *> >::iterator vi = note_map_.begin ();
vi != note_map_.end (); ++vi)
{
- Audio_dynamic *d = get_dynamic (vi->first);
+ Audio_span_dynamic *d = get_dynamic (vi->first);
if (d)
{
for (deque<Audio_note *>::iterator ni = vi->second.begin ();
@@ -316,47 +315,40 @@ Staff_performer::get_channel (const string &instrument)
void
Staff_performer::acknowledge_audio_element (Audio_element_info inf)
{
- if (Audio_item *ai = dynamic_cast<Audio_item *> (inf.elem_))
+ /* map each context (voice) to its own track */
+ Context *c = inf.origin_contexts (this)[0];
+ string voice;
+ if (c->is_alias (ly_symbol2scm ("Voice")))
+ voice = c->id_string ();
+ SCM channel_mapping = get_property ("midiChannelMapping");
+ string str = new_instrument_string ();
+ if (!scm_is_eq (channel_mapping, ly_symbol2scm ("instrument")))
+ channel_ = get_channel (voice);
+ else if (channel_ < 0 && str.empty ())
+ channel_ = get_channel (str);
+ if (str.length ())
{
- /* map each context (voice) to its own track */
- Context *c = inf.origin_contexts (this)[0];
- string voice;
- if (c->is_alias (ly_symbol2scm ("Voice")))
- voice = c->id_string ();
- SCM channel_mapping = get_property ("midiChannelMapping");
- string str = new_instrument_string ();
- if (!scm_is_eq (channel_mapping, ly_symbol2scm ("instrument")))
- channel_ = get_channel (voice);
- else if (channel_ < 0 && str.empty ())
+ if (!scm_is_eq (channel_mapping, ly_symbol2scm ("voice")))
channel_ = get_channel (str);
- if (str.length ())
- {
- if (!scm_is_eq (channel_mapping, ly_symbol2scm ("voice")))
- channel_ = get_channel (str);
- set_instrument (channel_, voice);
- set_instrument_name (voice);
- }
+ set_instrument (channel_, voice);
+ set_instrument_name (voice);
+ }
+ Audio_staff *audio_staff = get_audio_staff (voice);
+ if (Audio_item *ai = dynamic_cast<Audio_item *> (inf.elem_))
+ {
ai->channel_ = channel_;
- Audio_staff *audio_staff = get_audio_staff (voice);
- bool encode_dynamics_as_velocity_ = true;
- if (encode_dynamics_as_velocity_)
+ if (Audio_note *n = dynamic_cast<Audio_note *> (inf.elem_))
{
- if (Audio_note *n = dynamic_cast<Audio_note *> (inf.elem_))
- {
- // Keep track of the notes played in the current voice in this
- // translation time step (for adjusting their dynamics later in
- // stop_translation_timestep).
- note_map_[voice].push_back (n);
- }
- else if (Audio_dynamic *d = dynamic_cast<Audio_dynamic *> (inf.elem_))
- {
- dynamic_map_[voice] = d;
- // Output volume as velocity: skip Midi_dynamic output for the
- // current element.
- return;
- }
+ // Keep track of the notes played in the current voice in this
+ // translation time step (for adjusting their dynamics later in
+ // stop_translation_timestep).
+ note_map_[voice].push_back (n);
}
audio_staff->add_audio_item (ai);
}
+ else if (Audio_span_dynamic *d = dynamic_cast<Audio_span_dynamic *> (inf.elem_))
+ {
+ dynamic_map_[voice] = d;
+ }
}