diff options
Diffstat (limited to 'src/loris/FrequencyReference.C')
-rw-r--r-- | src/loris/FrequencyReference.C | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/src/loris/FrequencyReference.C b/src/loris/FrequencyReference.C new file mode 100644 index 0000000..3561e15 --- /dev/null +++ b/src/loris/FrequencyReference.C @@ -0,0 +1,293 @@ +/* + * 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 + * + * + * FrequencyReference.C + * + * Implementation of class FrequencyReference. + * + * Kelly Fitz, 3 Dec 2001 + * loris@cerlsoundgroup.org + * + * http://www.cerlsoundgroup.org/Loris/ + * + */ + +#if HAVE_CONFIG_H + #include "config.h" +#endif + +#include "FrequencyReference.h" + +#include "Breakpoint.h" +#include "Fundamental.h" +#include "LinearEnvelope.h" +#include "Notifier.h" +#include "Partial.h" +#include "PartialList.h" +#include "PartialUtils.h" + +#include <algorithm> +#include <cmath> + +// begin namespace +namespace Loris { + + +// --------------------------------------------------------------------------- +// createEstimator (static) +// --------------------------------------------------------------------------- +// This class is now a wrapper providing the legacy interface to an improved +// and more flexible fundamental frequency estimator. This function +// constructs and configures an instance of the new estimator that +// provides the functionality of the older (Loris 1.4 through 1.5.2) +// FrequencyReference class. +// + +static const double Range = 50; +static const double Ceiling = 20000; +static const double Floor = -60; +static const double Precision = 0.1; +static const double Confidence = 0.9; +static const double Interval = 0.01; + +FundamentalFromPartials createEstimator( void ) +{ + FundamentalFromPartials eparts; + + eparts.setAmpFloor( Floor ); + eparts.setAmpRange( Range ); + eparts.setFreqCeiling( Ceiling ); + eparts.setPrecision( Precision ); + + return eparts; +} + +// --------------------------------------------------------------------------- +// construction +// --------------------------------------------------------------------------- +//! Construct a new fundamental FrequencyReference derived from the +//! specified half-open (STL-style) range of Partials that lies +//! within the speficied average frequency range. Construct the +//! reference envelope with approximately numSamps points. +//! +//! \param begin The beginning of a range of Partials from which to +//! construct a frequency refence envelope. +//! \param end The end of a range of Partials from which to +//! construct a frequency refence envelope. +//! \param minFreq The minimum expected fundamental frequency. +//! \param maxFreq The maximum expected fundamental frequency. +//! \param numSamps The approximate number of estimate of the +//! fundamental frequency from which to construct the +//! frequency reference envelope. +// +FrequencyReference::FrequencyReference( PartialList::const_iterator begin, + PartialList::const_iterator end, + double minFreq, double maxFreq, + long numSamps ) : + _env( new LinearEnvelope() ) +{ + if ( numSamps < 1 ) + { + Throw( InvalidArgument, "A frequency reference envelope must have a positive number of samples." ); + } + + // sanity: + if ( maxFreq < minFreq ) + { + std::swap( minFreq, maxFreq ); + } + +#ifdef Loris_Debug + debugger << "Finding frequency reference envelope in range " << + debugger << minFreq << " to " << maxFreq << " Hz, from " << + debugger << std::distance(begin,end) << " Partials" << std::endl; +#endif + + + FundamentalFromPartials est = createEstimator(); + std::pair< double, double > span = PartialUtils::timeSpan( begin, end ); + double dt = ( span.second - span.first ) / ( numSamps + 1 ); + *_env = est.buildEnvelope( begin, end, + span.first, span.second, dt, + minFreq, maxFreq, + Confidence ); +} + +// --------------------------------------------------------------------------- +// construction +// --------------------------------------------------------------------------- +//! Construct a new fundamental FrequencyReference derived from the +//! specified half-open (STL-style) range of Partials that lies +//! within the speficied average frequency range. Construct the +//! reference envelope from fundamental estimates taken every +//! five milliseconds. +//! +//! \param begin The beginning of a range of Partials from which to +//! construct a frequency refence envelope. +//! \param end The end of a range of Partials from which to +//! construct a frequency refence envelope. +//! \param minFreq The minimum expected fundamental frequency. +//! \param maxFreq The maximum expected fundamental frequency. +// +FrequencyReference::FrequencyReference( PartialList::const_iterator begin, + PartialList::const_iterator end, + double minFreq, double maxFreq ) : + _env( new LinearEnvelope() ) +{ + // sanity: + if ( maxFreq < minFreq ) + { + std::swap( minFreq, maxFreq ); + } + +#ifdef Loris_Debug + debugger << "Finding frequency reference envelope in range " << + debugger << minFreq << " to " << maxFreq << " Hz, from " << + debugger << std::distance(begin,end) << " Partials" << std::endl; +#endif + + FundamentalFromPartials est = createEstimator(); + std::pair< double, double > span = PartialUtils::timeSpan( begin, end ); + *_env = est.buildEnvelope( begin, end, + span.first, span.second, Interval, + minFreq, maxFreq, + Confidence ); + +} + +// --------------------------------------------------------------------------- +// copy construction +// --------------------------------------------------------------------------- +// +FrequencyReference::FrequencyReference( const FrequencyReference & other ) : + _env( other._env->clone() ) +{ +} + +// --------------------------------------------------------------------------- +// destruction +// --------------------------------------------------------------------------- +// +FrequencyReference::~FrequencyReference() +{ +} + +// --------------------------------------------------------------------------- +// assignment +// --------------------------------------------------------------------------- +// +FrequencyReference & +FrequencyReference::operator = ( const FrequencyReference & rhs ) +{ + if ( &rhs != this ) + { + _env.reset( rhs._env->clone() ); + } + return *this; +} + +// --------------------------------------------------------------------------- +// clone +// --------------------------------------------------------------------------- +// +FrequencyReference * +FrequencyReference::clone( void ) const +{ + return new FrequencyReference( *this ); +} + +// --------------------------------------------------------------------------- +// valueAt +// --------------------------------------------------------------------------- +// +double +FrequencyReference::valueAt( double x ) const +{ + return _env->valueAt(x); +} + +// --------------------------------------------------------------------------- +// envelope +// --------------------------------------------------------------------------- +// Conversion to LinearEnvelope return a BreakpointEnvelope that +// evaluates indentically to this FrequencyReference at all time. +// +LinearEnvelope +FrequencyReference::envelope( void ) const +{ + return *_env; +} + +// --------------------------------------------------------------------------- +// timeOfPeakEnergy (static helper function) +// --------------------------------------------------------------------------- +// Return the time at which the given Partial attains its +// maximum sinusoidal energy. +// +static double timeOfPeakEnergy( const Partial & p ) +{ + Partial::const_iterator partialIter = p.begin(); + double maxAmp = + partialIter->amplitude() * std::sqrt( 1. - partialIter->bandwidth() ); + double time = partialIter.time(); + + for ( ++partialIter; partialIter != p.end(); ++partialIter ) + { + double a = partialIter->amplitude() * + std::sqrt( 1. - partialIter->bandwidth() ); + if ( a > maxAmp ) + { + maxAmp = a; + time = partialIter.time(); + } + } + + return time; +} +// --------------------------------------------------------------------------- +// IsInFrequencyRange +// --------------------------------------------------------------------------- +// Function object for finding Partials that attain their maximum +// sinusoidal energy at a frequency within a specified range. +// +struct IsInFrequencyRange +{ + double minFreq, maxFreq; + IsInFrequencyRange( double min, double max ) : + minFreq( min ), + maxFreq( max ) + { + // sanity: + if ( maxFreq < minFreq ) + std::swap( minFreq, maxFreq ); + } + + bool operator() ( const Partial & p ) + { + double compareFreq = p.frequencyAt( timeOfPeakEnergy( p ) ); + return compareFreq >= minFreq && compareFreq <= maxFreq; + } +}; + + + +} // end of namespace Loris |