summaryrefslogtreecommitdiff
path: root/src/loris/Partial.C
diff options
context:
space:
mode:
Diffstat (limited to 'src/loris/Partial.C')
-rw-r--r--src/loris/Partial.C860
1 files changed, 860 insertions, 0 deletions
diff --git a/src/loris/Partial.C b/src/loris/Partial.C
new file mode 100644
index 0000000..b6ea15c
--- /dev/null
+++ b/src/loris/Partial.C
@@ -0,0 +1,860 @@
+/*
+ * This is the Loris C++ Class Library, implementing analysis,
+ * manipulation, and synthesis of digitized sounds using the Reassigned
+ * Bandwidth-Enhanced Additive Sound Model.
+ *
+ * Loris is Copyright (c) 1999-2010 by Kelly Fitz and Lippold Haken
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Partial.C
+ *
+ * Implementation of class Loris::Partial.
+ *
+ * Kelly Fitz, 16 Aug 1999
+ * loris@cerlsoundgroup.org
+ *
+ * http://www.cerlsoundgroup.org/Loris/
+ *
+ */
+
+#if HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#include "Partial.h"
+#include "Breakpoint.h"
+#include "LorisExceptions.h"
+#include "Notifier.h"
+
+#include <algorithm>
+#include <cmath>
+
+#if defined(HAVE_M_PI) && (HAVE_M_PI)
+ const double Pi = M_PI;
+#else
+ const double Pi = 3.14159265358979324;
+#endif
+
+// begin namespace
+namespace Loris {
+
+//long Partial::DebugCounter = 0L;
+
+// comparitor for elements in Partial::container_type
+typedef Partial::container_type::value_type Partial_value_type;
+static
+bool order_by_time( const Partial_value_type & x, const Partial_value_type & y )
+{
+ // Partial_value_type is a (time,Breakpoint) pair
+ return x.first < y.first;
+}
+
+// --- concering the type of Partial::container_type
+//
+// On the surface, it would seem that a vector of (time,Breakpoint)
+// pairs would be a more efficient container for the Partial
+// parameter envelope points, and the changes required to implement
+// Partial using vector instead of map are minimal and simple.
+//
+// However, the crucial factor in that change is the expiration of
+// Partial::iterators. With map, iterators remain valid after
+// insertions and removals, but with vector they do not. So it
+// is easy to change the container type, but it is a much harder
+// project to find all the places in Loris that rely on iterators
+// that remain valid after insertions and removals.
+#undef USE_VECTOR
+
+
+// -- construction --
+
+// ---------------------------------------------------------------------------
+// Partial constructor
+// ---------------------------------------------------------------------------
+//! Retun a new empty (no Breakpoints) unlabeled Partial.
+//
+Partial::Partial( void ) :
+ _label( 0 )
+{
+// ++DebugCounter;
+}
+
+// ---------------------------------------------------------------------------
+// Partial initialized constructor
+// ---------------------------------------------------------------------------
+//! Retun a new Partial from a half-open (const) iterator range
+//! of time, Breakpoint pairs.
+//
+Partial::Partial( const_iterator beg, const_iterator end ) :
+ _breakpoints( beg._iter, end._iter ),
+ _label( 0 )
+{
+// ++DebugCounter;
+}
+
+// ---------------------------------------------------------------------------
+// Partial copy constructor
+// ---------------------------------------------------------------------------
+//! Return a new Partial that is an exact copy (has an identical set
+//! of Breakpoints, at identical times, and the same label) of another
+//! Partial.
+//
+Partial::Partial( const Partial & other ) :
+ _breakpoints( other._breakpoints ),
+ _label( other._label )
+{
+// ++DebugCounter;
+}
+
+// ---------------------------------------------------------------------------
+// Partial destructor
+// ---------------------------------------------------------------------------
+//! Destroy this Partial.
+//
+Partial::~Partial( void )
+{
+// --DebugCounter;
+}
+
+// ---------------------------------------------------------------------------
+// operator=
+// ---------------------------------------------------------------------------
+//! Make this Partial an exact copy (has an identical set of
+//! Breakpoints, at identical times, and the same label) of another
+//! Partial.
+//
+Partial &
+Partial::operator=( const Partial & rhs )
+{
+ if ( this != &rhs )
+ {
+ _breakpoints = rhs._breakpoints;
+ _label = rhs._label;
+ }
+ return *this;
+}
+
+// -- container-dependent implementation --
+
+// ---------------------------------------------------------------------------
+// begin
+// ---------------------------------------------------------------------------
+//! Return a const iterator refering to the position of the first
+//! Breakpoint in this Partial's envelope.
+//
+Partial::const_iterator Partial::begin( void ) const
+{
+ return _breakpoints.begin();
+}
+
+//! Return an iterator refering to the position of the first
+//! Breakpoint in this Partial's envelope.
+//
+Partial::iterator Partial::begin( void )
+{
+ return _breakpoints.begin();
+}
+
+// ---------------------------------------------------------------------------
+// end
+// ---------------------------------------------------------------------------
+//! Return a const iterator refering to the position past the last
+//! Breakpoint in this Partial's envelope. The iterator returned by
+//! end() (like the iterator returned by the end() member of any STL
+//! container) does not refer to a valid Breakpoint.
+//
+Partial::const_iterator
+Partial::end( void ) const
+{
+ return _breakpoints.end();
+}
+
+//! Return an iterator refering to the position past the last
+//! Breakpoint in this Partial's envelope. The iterator returned by
+//! end() (like the iterator returned by the end() member of any STL
+//! container) does not refer to a valid Breakpoint.
+//
+Partial::iterator
+Partial::end( void )
+{
+ return _breakpoints.end();
+}
+
+// ---------------------------------------------------------------------------
+// erase
+// ---------------------------------------------------------------------------
+//! Breakpoint removal: erase the Breakpoints in the specified range,
+//! and return an iterator referring to the position after the,
+//! erased range.
+//
+Partial::iterator
+Partial::erase( Partial::iterator beg, Partial::iterator end )
+{
+ _breakpoints.erase( beg._iter, end._iter );
+ return end;
+}
+
+// ---------------------------------------------------------------------------
+// findAfter
+// ---------------------------------------------------------------------------
+//! Return a const iterator refering to the insertion position for a
+//! Breakpoint at the specified time (that is, the position of the first
+//! Breakpoint at a time not earlier than the specified time).
+//
+Partial::const_iterator
+Partial::findAfter( double time ) const
+{
+#if defined(USE_VECTOR)
+ // see note above
+ Partial_value_type dummy( time, Breakpoint() );
+ return std::upper_bound( _breakpoints.begin(), _breakpoints.end(), dummy, order_by_time );
+#else
+ return _breakpoints.lower_bound( time );
+#endif
+}
+
+//! Return an iterator refering to the insertion position for a
+//! Breakpoint at the specified time (that is, the position of the first
+//! Breakpoint at a time later than the specified time).
+//
+Partial::iterator
+Partial::findAfter( double time )
+{
+#if defined(USE_VECTOR)
+ // see note above
+ Partial_value_type dummy( time, Breakpoint() );
+ return std::upper_bound( _breakpoints.begin(), _breakpoints.end(), dummy, order_by_time );
+#else
+ return _breakpoints.lower_bound( time );
+#endif
+}
+
+// ---------------------------------------------------------------------------
+// insert
+// ---------------------------------------------------------------------------
+//! Breakpoint insertion: insert a copy of the specified Breakpoint in the
+//! parameter envelope at time (seconds), and return an iterator
+//! refering to the position of the inserted Breakpoint.
+//
+Partial::iterator
+Partial::insert( double time, const Breakpoint & bp )
+{
+#if defined(USE_VECTOR)
+ // see note above
+ // find the position at which to insert the new Breakpoint:
+ Partial_value_type dummy( time, Breakpoint() );
+ Partial::container_type::iterator insertHere =
+ std::lower_bound( _breakpoints.begin(), _breakpoints.end(), dummy, order_by_time );
+
+ // if the time at insertHere is equal to the insertion time,
+ // simply replace the Breakpoint, otherwise insert:
+ if ( insertHere->first == time )
+ {
+ insertHere->second = bp;
+ }
+ else
+ {
+ insertHere = _breakpoints.insert( insertHere, Partial_value_type(time, bp) );
+ }
+ return insertHere;
+#else
+ /*
+ // this allows Breakpoints to be inserted arbitrarily
+ // close together, which is no good, can cause trouble later:
+
+ std::pair< container_type::iterator, bool > result =
+ _breakpoints.insert( container_type::value_type(time, bp) );
+ if ( ! result.second )
+ {
+ result.first->second = bp;
+ }
+ return result.first;
+ */
+
+ // do not insert a Breakpoint closer than 1ns away
+ // from the nearest existing Breakpoint:
+ static const double MinTimeDif = 1.0E-9; // 1 ns
+
+ // find the insertion point for this time
+ container_type::iterator pos = _breakpoints.lower_bound( time );
+
+ // the time of pos is either equal to or greater
+ // than the insertion time, if this is too close,
+ // remove the Breakpoint at pos:
+ if ( _breakpoints.end() != pos && MinTimeDif > pos->first - time )
+ {
+ _breakpoints.erase( pos++ );
+ }
+ // otherwise, if the preceding position is too clase,
+ // remove the Breakpoint at that position
+ else if ( _breakpoints.begin() != pos && MinTimeDif > time - (--pos)->first )
+ {
+ _breakpoints.erase( pos++ );
+ }
+
+ // now pos is at most one position away from the insertion point
+ // so insertion can be performed in constant time, and the new
+ // Breakpoint is at least 1ns away from any other Breakpoint:
+ pos = _breakpoints.insert( pos, container_type::value_type(time, bp) );
+
+ Assert( pos->first == time );
+
+ return pos;
+
+#endif
+}
+
+// ---------------------------------------------------------------------------
+// numBreakpoints
+// ---------------------------------------------------------------------------
+//! Same as size(). Return the number of Breakpoints in this Partial.
+//
+Partial::size_type
+Partial::numBreakpoints( void ) const
+{
+ return _breakpoints.size();
+}
+// ---------------------------------------------------------------------------
+// size
+// ---------------------------------------------------------------------------
+//! Return the number of Breakpoints in this Partial.
+//
+Partial::size_type
+Partial::size( void ) const
+{
+ return _breakpoints.size();
+}
+
+// ---------------------------------------------------------------------------
+// label
+// ---------------------------------------------------------------------------
+//! Return the 32-bit label for this Partial as an integer.
+//
+Partial::label_type
+Partial::label( void ) const
+{
+ return _label;
+}
+
+// ---------------------------------------------------------------------------
+// first
+// ---------------------------------------------------------------------------
+//! Return a reference to the first Breakpoint in the Partial's
+//! envelope. Raises InvalidPartial exception if there are no
+//! Breakpoints.
+//
+Breakpoint &
+Partial::first( void )
+{
+ if ( size() == 0 )
+ {
+ Throw( InvalidPartial, "Tried find first Breakpoint in a Partial with no Breakpoints." );
+ }
+#if defined(USE_VECTOR)
+ // see note above
+ return _breakpoints.front().second;
+#else
+ return begin().breakpoint();
+#endif
+}
+
+// ---------------------------------------------------------------------------
+// first
+// ---------------------------------------------------------------------------
+//! Return a const reference to the first Breakpoint in the Partial's
+//! envelope. Raises InvalidPartial exception if there are no
+//! Breakpoints.
+//
+const Breakpoint &
+Partial::first( void ) const
+{
+ if ( size() == 0 )
+ {
+ Throw( InvalidPartial, "Tried find first Breakpoint in a Partial with no Breakpoints." );
+ }
+#if defined(USE_VECTOR)
+ // see note above
+ return _breakpoints.front().second;
+#else
+ return begin().breakpoint();
+#endif
+}
+
+// ---------------------------------------------------------------------------
+// last
+// ---------------------------------------------------------------------------
+//! Return a reference to the last Breakpoint in the Partial's
+//! envelope. Raises InvalidPartial exception if there are no
+//! Breakpoints.
+//
+Breakpoint &
+Partial::last( void )
+{
+ if ( size() == 0 )
+ {
+ Throw( InvalidPartial, "Tried find last Breakpoint in a Partial with no Breakpoints." );
+ }
+#if defined(USE_VECTOR)
+ // see note above
+ return _breakpoints.back().second;
+#else
+ return (--end()).breakpoint();
+#endif
+}
+
+// ---------------------------------------------------------------------------
+// last
+// ---------------------------------------------------------------------------
+//! Return a const reference to the last Breakpoint in the Partial's
+//! envelope. Raises InvalidPartial exception if there are no
+//! Breakpoints.
+//
+const Breakpoint &
+Partial::last( void ) const
+{
+ if ( size() == 0 )
+ {
+ Throw( InvalidPartial, "Tried find last Breakpoint in a Partial with no Breakpoints." );
+ }
+#if defined(USE_VECTOR)
+ // see note above
+ return _breakpoints.back().second;
+#else
+ return (--end()).breakpoint();
+#endif
+}
+
+// -- container-independent implementation --
+
+// ---------------------------------------------------------------------------
+// initialPhase
+// ---------------------------------------------------------------------------
+//! Return starting phase in radians, except (InvalidPartial) if there
+//! are no Breakpoints.
+//
+double
+Partial::initialPhase( void ) const
+{
+ if ( numBreakpoints() == 0 )
+ {
+ Throw( InvalidPartial, "Tried find intial phase of a Partial with no Breakpoints." );
+ }
+ return first().phase();
+}
+
+// ---------------------------------------------------------------------------
+// startTime
+// ---------------------------------------------------------------------------
+//! Return start time in seconds, except (InvalidPartial) if there
+//! are no Breakpoints.
+//
+double
+Partial::startTime( void ) const
+{
+ if ( numBreakpoints() == 0 )
+ {
+ Throw( InvalidPartial, "Tried to find start time of a Partial with no Breakpoints." );
+ }
+ return begin().time();
+}
+
+// ---------------------------------------------------------------------------
+// endTime
+// ---------------------------------------------------------------------------
+//! Return end time in seconds, except (InvalidPartial) if there
+//! are no Breakpoints.
+//
+double
+Partial::endTime( void ) const
+{
+ if ( numBreakpoints() == 0 )
+ {
+ Throw( InvalidPartial, "Tried to find end time of a Partial with no Breakpoints." );
+ }
+ return (--end()).time();
+}
+
+// ---------------------------------------------------------------------------
+// absorb
+// ---------------------------------------------------------------------------
+//! Absorb another Partial's energy as noise (bandwidth),
+//! by accumulating the other's energy as noise energy
+//! in the portion of this Partial's envelope that overlaps
+//! (in time) with the other Partial's envelope.
+//
+void
+Partial::absorb( const Partial & other )
+{
+ Partial::iterator it = findAfter( other.startTime() );
+ while ( it != end() && !(it.time() > other.endTime()) )
+ {
+ // only non-null (non-zero-amplitude) Breakpoints
+ // abosrb noise energy because null Breakpoints
+ // are used especially to reset the Partial phase,
+ // and are not part of the normal analyasis data:
+ if ( it->amplitude() > 0 )
+ {
+ // absorb energy from other at the time
+ // of this Breakpoint:
+ double a = other.amplitudeAt( it.time() );
+ it->addNoiseEnergy( a * a );
+ }
+ ++it;
+ }
+}
+
+// ---------------------------------------------------------------------------
+// setLabel
+// ---------------------------------------------------------------------------
+//! Set the label for this Partial to the specified 32-bit value.
+//
+void
+Partial::setLabel( label_type l )
+{
+ _label = l;
+}
+
+// ---------------------------------------------------------------------------
+// duration
+// ---------------------------------------------------------------------------
+//! Return time, in seconds, spanned by this Partial, or 0. if there
+//! are no Breakpoints.
+//
+double
+Partial::duration( void ) const
+{
+ if ( numBreakpoints() == 0 )
+ {
+ return 0.;
+ }
+ return endTime() - startTime();
+}
+
+// ---------------------------------------------------------------------------
+// erase
+// ---------------------------------------------------------------------------
+//! Erase the Breakpoint at the position of the
+//! given iterator (invalidating the iterator), and
+//! return an iterator referring to the next position,
+//! or end if pos is the last Breakpoint in the Partial.
+//
+Partial::iterator
+Partial::erase( iterator pos )
+{
+ if ( pos != end() )
+ {
+ iterator b= pos;
+ iterator e = ++pos;
+ pos = erase( b, e );
+ }
+ return pos;
+}
+
+// ---------------------------------------------------------------------------
+// split
+// ---------------------------------------------------------------------------
+//! Break this Partial at the specified position (iterator).
+//! The Breakpoint at the specified position becomes the first
+//! Breakpoint in a new Partial. Breakpoints at the specified
+//! position and subsequent positions are removed from this
+//! Partial and added to the new Partial, which is returned.
+//
+Partial
+Partial::split( iterator pos )
+{
+ Partial res( pos, end() );
+ erase( pos, end() );
+ return res;
+}
+
+// ---------------------------------------------------------------------------
+// findNearest (const version)
+// ---------------------------------------------------------------------------
+//! Return the insertion position for the Breakpoint nearest
+//! the specified time. Always returns a valid iterator (the
+//! position of the nearest-in-time Breakpoint) unless there
+//! are no Breakpoints.
+//
+Partial::const_iterator
+Partial::findNearest( double time ) const
+{
+ // if there are no Breakpoints, return end:
+ if ( numBreakpoints() == 0 )
+ {
+ return end();
+ }
+
+ // get the position of the first Breakpoint after time:
+ Partial::const_iterator pos = findAfter( time );
+
+ // if there is an earlier Breakpoint that is closer in
+ // time, prefer that one:
+ if ( pos != begin() )
+ {
+ Partial::const_iterator prev = pos;
+ --prev;
+ if ( pos == end() || pos.time() - time > time - prev.time() )
+ {
+ return prev;
+ }
+ }
+
+ // failing all else:
+ return pos;
+}
+
+// ---------------------------------------------------------------------------
+// findNearest (non-const version)
+// ---------------------------------------------------------------------------
+//! Return the insertion position for the Breakpoint nearest
+//! the specified time. Always returns a valid iterator (the
+//! position of the nearest-in-time Breakpoint) unless there
+//! are no Breakpoints.
+//
+Partial::iterator
+Partial::findNearest( double time )
+{
+ // if there are no Breakpoints, return end:
+ if ( numBreakpoints() == 0 )
+ {
+ return end();
+ }
+ // get the position of the first Breakpoint after time:
+ Partial::iterator pos = findAfter( time );
+
+ // if there is an earlier Breakpoint that is closer in
+ // time, prefer that one:
+ if ( pos != begin() )
+ {
+ Partial::iterator prev = pos;
+ --prev;
+ if ( pos == end() || pos.time() - time > time - prev.time() )
+ {
+ return prev;
+ }
+ }
+
+ // failing all else:
+ return pos;
+}
+
+// ---------------------------------------------------------------------------
+// frequencyAt
+// ---------------------------------------------------------------------------
+//! Return the interpolated frequency (in Hz) of this Partial at the
+//! specified time. At times beyond the ends of the Partial, return
+//! the frequency at the nearest envelope endpoint. Throw an
+//! InvalidPartial exception if this Partial has no Breakpoints.
+//
+double
+Partial::frequencyAt( double time ) const
+{
+ Breakpoint bp = parametersAt( time );
+ return bp.frequency();
+}
+
+// ---------------------------------------------------------------------------
+// ShortestSafeFadeTime
+// ---------------------------------------------------------------------------
+//! Define the default fade time for computing amplitude at the ends
+//! of a Partial. Floating point round-off errors make fadeTime == 0.0
+//! dangerous and unpredictable. 1 ns is short enough to prevent rounding
+//! errors in the least significant bit of a 48-bit mantissa for times
+//! up to ten hours.
+//
+const double Partial::ShortestSafeFadeTime = 1.0E-9;
+
+// ---------------------------------------------------------------------------
+// amplitudeAt
+// ---------------------------------------------------------------------------
+//! Return the interpolated amplitude of this Partial at the
+//! specified time. Throw an InvalidPartial exception if this
+//! Partial has no Breakpoints. If non-zero fadeTime is specified,
+//! then the amplitude at the ends of the Partial is coomputed using
+//! a linear fade. The default fadeTime is ShortestSafeFadeTime,
+//! see the definition of ShortestSafeFadeTime, above.
+//
+double
+Partial::amplitudeAt( double time, double fadeTime ) const
+{
+ Breakpoint bp = parametersAt( time, fadeTime );
+ return bp.amplitude();
+}
+
+
+// ---------------------------------------------------------------------------
+// phaseAt
+// ---------------------------------------------------------------------------
+//! Return the interpolated phase (in radians) of this Partial at
+//! the specified time. At times beyond the ends of the Partial,
+//! return the extrapolated from the nearest envelope endpoint
+//! (assuming constant frequency, as reported by frequencyAt()).
+//!
+//! \param time is the time in seconds at which to evaluate the phase
+//!
+//! \throw Throw an InvalidPartial exception if this Partial has no
+//! Breakpoints.
+//
+double
+Partial::phaseAt( double time ) const
+{
+ Breakpoint bp = parametersAt( time );
+ return bp.phase();
+}
+
+// ---------------------------------------------------------------------------
+// bandwidthAt
+// ---------------------------------------------------------------------------
+//! Return the interpolated bandwidth (noisiness) coefficient of
+//! this Partial at the specified time. At times beyond the ends of
+//! the Partial, return the bandwidth coefficient at the nearest
+//! envelope endpoint. Throw an InvalidPartial exception if this
+//! Partial has no Breakpoints.
+//
+double
+Partial::bandwidthAt( double time ) const
+{
+ Breakpoint bp = parametersAt( time );
+ return bp.bandwidth();
+}
+
+// ---------------------------------------------------------------------------
+// wrapPi
+// ---------------------------------------------------------------------------
+// O'Donnell's phase wrapping function.
+//
+static inline double wrapPi( double x )
+{
+ using namespace std; // floor should be in std
+ #define ROUND(x) (floor(.5 + (x)))
+ const double TwoPi = 2.0*Pi;
+ return x + ( TwoPi * ROUND(-x/TwoPi) );
+}
+
+// ---------------------------------------------------------------------------
+// parametersAt
+// ---------------------------------------------------------------------------
+//! Return the interpolated parameters of this Partial at
+//! the specified time. If non-zero fadeTime is specified, then the
+//! amplitude at the ends of the Partial is coomputed using a
+//! linear fade. The default fadeTime is ShortestSafeFadeTime.
+//! Throw an InvalidPartial exception if this Partial has no
+//! Breakpoints.
+//
+Breakpoint
+Partial::parametersAt( double time, double fadeTime ) const
+{
+ if ( numBreakpoints() == 0 )
+ {
+ Throw( InvalidPartial, "Tried to interpolate a Partial with no Breakpoints." );
+ }
+
+ double freq, amp, bw, ph;
+ if ( startTime() >= time )
+ {
+ // time is before the onset of the Partial:
+ // frequency is starting frequency,
+ // amplitude is 0 (or fading), bandwidth is starting
+ // bandwidth, and phase is rolled back.
+
+ const Breakpoint & bp = first();
+ double tstart = startTime();
+
+ // frequency:
+ freq = bp.frequency();
+
+ // amplitude:
+ amp = 0;
+ if ( (fadeTime > 0) && ((tstart - time) < fadeTime) )
+ {
+ // fade in ampltude if time is before the onset of the Partial:
+ double alpha = 1. - ((tstart - time) / fadeTime);
+ amp = alpha * bp.amplitude();
+ }
+
+ // bandwidth:
+ bw = bp.bandwidth();
+
+ // phase:
+ double dp = 2. * Pi * (startTime() - time) * bp.frequency();
+ ph = wrapPi( bp.phase() - dp );
+
+ }
+ else if ( endTime() <= time )
+ {
+ // time is past the end of the Partial:
+ // frequency is ending frequency,
+ // amplitude is 0 (or fading), bandwidth is ending
+ // bandwidth, and phase is rolled forward.
+ const Breakpoint & bp = last();
+ double tend = endTime();
+
+ // frequency:
+ freq = bp.frequency();
+
+ // amplitude:
+ amp = 0;
+ if ( (fadeTime > 0) && ((time - tend) < fadeTime) )
+ {
+ // fade out ampltude if time is past the end of the Partial:
+ double alpha = 1. - ((time - tend) / fadeTime);
+ amp = alpha * bp.amplitude();
+ }
+
+ // bandwidth:
+ bw = bp.bandwidth();
+
+ // phase:
+ double dp = 2. * Pi * (time - endTime()) * bp.frequency();
+ ph = wrapPi( bp.phase() + dp );
+ }
+ else
+ {
+ // findAfter returns the position of the earliest
+ // Breakpoint later than time, or the end
+ // position if no such Breakpoint exists:
+ Partial::const_iterator it = findAfter( time );
+
+ // interpolate between it and its predeccessor
+ // (we checked already that it is not begin or end):
+ const Breakpoint & hi = it.breakpoint();
+ double hitime = it.time();
+ const Breakpoint & lo = (--it).breakpoint();
+ double lotime = it.time();
+
+ double alpha = (time - lotime) / (hitime - lotime);
+
+ // frequency:
+ freq = (alpha * hi.frequency()) + ((1. - alpha) * lo.frequency());
+
+ // amplitude:
+ amp = (alpha * hi.amplitude()) + ((1. - alpha) * lo.amplitude());
+
+ // bandwidth:
+ bw = (alpha * hi.bandwidth()) + ((1. - alpha) * lo.bandwidth());
+
+ // phase:
+ // interpolated phase is computed from the interpolated frequency
+ // and offset from the phase of the preceding Breakpoint:
+ double favg = 0.5 * ( lo.frequency() + freq ); // + hi.frequency() );
+ double dp = 2. * Pi * (time - lotime) * favg;
+ ph = wrapPi( lo.phase() + dp );
+ }
+
+ return Breakpoint( freq, amp, bw, ph );
+}
+
+} // end of namespace Loris