/* * 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 #include // 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