summaryrefslogtreecommitdiff
path: root/src/loris/Dilator.C
diff options
context:
space:
mode:
authorJohn Glover <glover.john@gmail.com>2011-07-08 18:06:21 +0100
committerJohn Glover <glover.john@gmail.com>2011-07-08 18:06:21 +0100
commitd6073e01c933c77f1e2bc3c3fe1126d617003549 (patch)
tree695d23677c5b84bf3a0f88fbd4959b4f7cbc0e90 /src/loris/Dilator.C
parent641688b252da468eb374674a0dbaae1bbac70b2b (diff)
downloadsimpl-d6073e01c933c77f1e2bc3c3fe1126d617003549.tar.gz
simpl-d6073e01c933c77f1e2bc3c3fe1126d617003549.tar.bz2
simpl-d6073e01c933c77f1e2bc3c3fe1126d617003549.zip
Start adding Loris files
Diffstat (limited to 'src/loris/Dilator.C')
-rw-r--r--src/loris/Dilator.C282
1 files changed, 282 insertions, 0 deletions
diff --git a/src/loris/Dilator.C b/src/loris/Dilator.C
new file mode 100644
index 0000000..733915f
--- /dev/null
+++ b/src/loris/Dilator.C
@@ -0,0 +1,282 @@
+/*
+ * 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
+ *
+ *
+ * Dilator.C
+ *
+ * Implementation of class Dilator.
+ *
+ * Kelly Fitz, 26 Oct 1999
+ * loris@cerlsoundgroup.org
+ *
+ * http://www.cerlsoundgroup.org/Loris/
+ *
+ */
+
+#if HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#include "Dilator.h"
+#include "Breakpoint.h"
+#include "LorisExceptions.h"
+#include "Marker.h"
+#include "Notifier.h"
+#include "Partial.h"
+#include "PartialList.h"
+
+#include <algorithm>
+
+// begin namespace
+namespace Loris {
+
+// ---------------------------------------------------------------------------
+// constructor
+// ---------------------------------------------------------------------------
+//! Construct a new Dilator with
+//! no time points.
+Dilator::Dilator( void )
+{
+}
+
+// ---------------------------------------------------------------------------
+// insert
+// ---------------------------------------------------------------------------
+//! Insert a pair of initial and target time points.
+//!
+//! Specify a pair of initial and target time points to be used
+//! by this Dilator, corresponding, for example, to the initial
+//! and desired time of a particular temporal feature in an
+//! analyzed sound.
+//!
+//! \param i is an initial, or source, time point
+//! \param t is a target time point
+//!
+//! The time points will be sorted before they are used.
+//! If, in the sequences of initial and target time points, there are
+//! exactly the same number of initial time points preceding i as
+//! target time points preceding t, then time i will be warped to
+//! time t in the dilation process.
+//
+void
+Dilator::insert( double i, double t )
+{
+ _initial.push_back(i);
+ _target.push_back(t);
+
+ // sort the time points before dilating:
+ std::sort( _initial.begin(), _initial.end() );
+ std::sort( _target.begin(), _target.end() );
+}
+
+// ---------------------------------------------------------------------------
+// warpTime
+// --------------------------------------------------------------------------
+//! Return the dilated time value corresponding to the specified initial time.
+//!
+//! \param currentTime is a pre-dilated time.
+//! \return the dilated time corresponding to the initial time currentTime
+//
+double
+Dilator::warpTime( double currentTime ) const
+{
+ int idx = std::distance( _initial.begin(),
+ std::lower_bound( _initial.begin(), _initial.end(), currentTime ) );
+ Assert( idx == _initial.size() || currentTime <= _initial[idx] );
+
+ // compute a new time for the Breakpoint at pIter:
+ double newtime = 0;
+ if ( idx == 0 )
+ {
+ // all time points in _initial are later than
+ // the currentTime; stretch if no zero time
+ // point has been specified, otherwise, shift:
+ if ( _initial[idx] != 0. )
+ newtime = currentTime * _target[idx] / _initial[idx];
+ else
+ newtime = _target[idx] + (currentTime - _initial[idx]);
+ }
+ else if ( idx == _initial.size() )
+ {
+ // all time points in _initial are earlier than
+ // the currentTime; shift:
+ //
+ // note: size is already known to be > 0, so
+ // idx-1 is safe
+ newtime = _target[idx-1] + (currentTime - _initial[idx-1]);
+ }
+ else
+ {
+ // currentTime is between the time points at idx and
+ // idx-1 in _initial; shift and stretch:
+ //
+ // note: size is already known to be > 0, so
+ // idx-1 is safe
+ Assert( _initial[idx-1] < _initial[idx] ); // currentTime can't wind up
+ // between two equal times
+
+ double stretch = (_target[idx] - _target[idx-1]) / (_initial[idx] - _initial[idx-1]);
+ newtime = _target[idx-1] + ((currentTime - _initial[idx-1]) * stretch);
+ }
+
+ return newtime;
+}
+
+// ---------------------------------------------------------------------------
+// dilate
+// ---------------------------------------------------------------------------
+//! Replace the Partial envelope with a new envelope having the
+//! same Breakpoints at times computed to align temporal features
+//! in the sorted sequence of initial time points with their
+//! counterparts the sorted sequence of target time points.
+//!
+//! Depending on the specification of initial and target time
+//! points, the dilated Partial may have Breakpoints at times
+//! less than 0, even if the original Partial did not.
+//!
+//! It is possible to have duplicate time points in either sequence.
+//! Duplicate initial time points result in very localized stretching.
+//! Duplicate target time points result in very localized compression.
+//!
+//! If all initial time points are greater than 0, then an implicit
+//! time point at 0 is assumed in both initial and target sequences,
+//! so the onset of a sound can be stretched without explcitly specifying a
+//! zero point in each vector. (This seems most intuitive, and only looks
+//! like an inconsistency if clients are using negative time points in
+//! their Dilator, or Partials having Breakpoints before time 0, both
+//! of which are probably unusual circumstances.)
+//!
+//! \param p is the Partial to dilate.
+//
+void
+Dilator::dilate( Partial & p ) const
+{
+ debugger << "dilating Partial having " << p.numBreakpoints()
+ << " Breakpoints" << endl;
+
+ // sanity check:
+ Assert( _initial.size() == _target.size() );
+
+ // don't dilate if there's no time points, or no Breakpoints:
+ if ( 0 == _initial.size() ||
+ 0 == p.numBreakpoints() )
+ {
+ return;
+ }
+
+ // create the new Partial:
+ Partial newp;
+ newp.setLabel( p.label() );
+
+ // timepoint index:
+ int idx = 0;
+ for ( Partial::const_iterator iter = p.begin(); iter != p.end(); ++iter )
+ {
+ // find the first initial time point later
+ // than the currentTime:
+ double currentTime = iter.time();
+ idx = std::distance( _initial.begin(),
+ std::lower_bound( _initial.begin(), _initial.end(), currentTime ) );
+ Assert( idx == _initial.size() || currentTime <= _initial[idx] );
+
+ // compute a new time for the Breakpoint at pIter:
+ double newtime = 0;
+ if ( idx == 0 )
+ {
+ // all time points in _initial are later than
+ // the currentTime; stretch if no zero time
+ // point has been specified, otherwise, shift:
+ if ( _initial[idx] != 0. )
+ newtime = currentTime * _target[idx] / _initial[idx];
+ else
+ newtime = _target[idx] + (currentTime - _initial[idx]);
+ }
+ else if ( idx == _initial.size() )
+ {
+ // all time points in _initial are earlier than
+ // the currentTime; shift:
+ //
+ // note: size is already known to be > 0, so
+ // idx-1 is safe
+ newtime = _target[idx-1] + (currentTime - _initial[idx-1]);
+ }
+ else
+ {
+ // currentTime is between the time points at idx and
+ // idx-1 in _initial; shift and stretch:
+ //
+ // note: size is already known to be > 0, so
+ // idx-1 is safe
+ Assert( _initial[idx-1] < _initial[idx] ); // currentTime can't wind up
+ // between two equal times
+
+ double stretch = (_target[idx] - _target[idx-1]) / (_initial[idx] - _initial[idx-1]);
+ newtime = _target[idx-1] + ((currentTime - _initial[idx-1]) * stretch);
+ }
+
+ // add a Breakpoint at the computed time:
+ newp.insert( newtime, iter.breakpoint() );
+ }
+
+ // new Breakpoints need to be added to the Partial at times corresponding
+ // to all target time points that are after the first Breakpoint and
+ // before the last, otherwise, Partials may be briefly out of tune with
+ // each other, since our Breakpoints are non-uniformly distributed in time:
+ for ( idx = 0; idx < _initial.size(); ++ idx )
+ {
+ if ( _initial[idx] <= p.startTime() )
+ {
+ continue;
+ }
+ else if ( _initial[idx] >= p.endTime() )
+ {
+ break;
+ }
+ else
+ {
+ newp.insert( _target[idx],
+ Breakpoint( p.frequencyAt(_initial[idx]), p.amplitudeAt(_initial[idx]),
+ p.bandwidthAt(_initial[idx]), p.phaseAt(_initial[idx]) ) );
+ }
+ }
+
+ // store the new Partial:
+ p = newp;
+}
+
+
+// ---------------------------------------------------------------------------
+// dilate
+// ---------------------------------------------------------------------------
+//! Compute a new time for the specified Marker using
+//! warpTime(), exactly as Partial Breakpoint times are
+//! recomputed. This can be used to dilate the Markers
+//! corresponding to a collection of Partials.
+//!
+//! \param m is the Marker whose time should be recomputed.
+//
+void
+Dilator::dilate( Marker & m ) const
+{
+ m.setTime( warpTime( m.time() ) );
+}
+
+} // end of namespace Loris