// ---------------------------------------------------------------------------
//	selectMagnitudePeaks (private)
// ---------------------------------------------------------------------------
Peaks
SpectralPeakSelector::selectMagnitudePeaks( ReassignedSpectrum & spectrum,
                                            double minFrequency )
{
	using namespace std; // for abs and fabs

	const double sampsToHz = mSampleRate / spectrum.size();
	const double oneOverSR = 1. / mSampleRate;
	const double minFreqSample = minFrequency / sampsToHz;
	const double maxCorrectionSamples = mMaxTimeOffset * mSampleRate;
	
	Peaks peaks;
	
	int start_j = 1, end_j = (spectrum.size() / 2) - 2;
	
	double fsample = start_j;
	do 
	{
	    fsample = spectrum.reassignedFrequency( start_j++ );
	} while( fsample < minFreqSample );
	
	for ( int j = start_j; j < end_j; ++j ) 
	{	 
		if ( spectrum.reassignedMagnitude(j) > spectrum.reassignedMagnitude(j-1) && 
			 spectrum.reassignedMagnitude(j) > spectrum.reassignedMagnitude(j+1) ) 
		{				
			//	skip low-frequency peaks:
			double fsample = spectrum.reassignedFrequency( j );
			if ( fsample < minFreqSample )
				continue;

			//	skip peaks with large time corrections:
			double timeCorrectionSamps = spectrum.reassignedTime( j );
			if ( fabs(timeCorrectionSamps) > maxCorrectionSamples )
				continue;
				
			double mag = spectrum.reassignedMagnitude( j );
			double phase = spectrum.reassignedPhase( j );
			
			//	this will be overwritten later in analysis, 
			//	might be ignored altogether, only used if the
			//	mixed derivative convergence indicator is stored
			//	as bandwidth in Analyzer:
			double bw = spectrum.convergence( j );
			
			//	also store the corrected peak time in seconds, won't
			//	be able to compute it later:
			double time = timeCorrectionSamps * oneOverSR;
			Breakpoint bp ( fsample * sampsToHz, mag, bw, phase );
			peaks.push_back( SpectralPeak( time, bp ) );
						
		}	//	end if itsa peak
	}
	
    /*
	debugger << "SpectralPeakSelector::selectMagnitudePeaks: found " 
             << peaks.size() << " peaks" << endl;
    */         		
	return peaks;
}
Exemple #2
0
// ---------------------------------------------------------------------------
//  FundamentalBuilder::build
// ---------------------------------------------------------------------------
//
void FundamentalBuilder::build( const Peaks & peaks, double frameTime )
{
    amplitudes.clear();
    frequencies.clear();
    for ( Peaks::const_iterator spkpos = peaks.begin(); spkpos != peaks.end(); ++spkpos )
    {
        if ( spkpos->amplitude() > mAmpThresh &&
             spkpos->frequency() < mFreqThresh )
        {
            amplitudes.push_back( spkpos->amplitude() );
            frequencies.push_back( spkpos->frequency() );
        }
    }
    if ( ! amplitudes.empty() )
    {
        const double fmin = mFminEnv->valueAt( frameTime );
        const double fmax = mFmaxEnv->valueAt( frameTime );
        
        //  estimate f0
        F0Estimate est( amplitudes, frequencies, fmin, fmax, 0.1 );
        
        if ( est.confidence() >= mMinConfidence &&
             est.frequency() > fmin && est.frequency() < fmax  )
        {
            // notifier << "f0 is " << est.frequency << endl;
            //  add breakpoint to fundamental envelope
            mEnvelope.insert( frameTime, est.frequency() );
        }
    }
    
}
Exemple #3
0
// ---------------------------------------------------------------------------
//	fixBandwidth (HELPER)
// ---------------------------------------------------------------------------
//	Fix the bandwidth value stored in the specified Peaks. 
//	This function is invoked if the spectral residue method is
//	not used to compute bandwidth (that method overwrites the
//	bandwidth already). If the convergence method is used to 
//	compute bandwidth, the appropriate scaling is applied
//	to the stored mixed phase derivative. Otherwise, the
//	Peak bandwidth is set to zero.
//
//  The convergence value is on the range [0,1], 0 for a sinusoid, 
//  and 1 for an impulse. If convergence tolerance is specified (as
//  a negative value in m_bwAssocParam), it should be positive and 
//  less than 1, and specifies the convergence value that is to 
//  correspond to bandwidth equal to 1.0. This is achieved by scaling
//  the convergence by the inverse of the tolerance, and saturating
//  at 1.0.
void Analyzer::fixBandwidth( Peaks & peaks )
{
	
	if ( m_bwAssocParam < 0 )
	{
		double scale = 1.0 / (- m_bwAssocParam);	
			// m_bwAssocParam stores negative tolerance
	
		for ( Peaks::iterator it = peaks.begin(); it != peaks.end(); ++it )
		{
            SpectralPeak & pk = *it;
			pk.setBandwidth( std::min( 1.0, scale * pk.bandwidth() ) );
		}
	}
	else if ( m_bwAssocParam == 0 )
	{
		for ( Peaks::iterator it = peaks.begin(); it != peaks.end(); ++it )
		{
            SpectralPeak & pk = *it;
			pk.setBandwidth( 0 );
		}
	}
}
// ---------------------------------------------------------------------------
//	buildPartials
// ---------------------------------------------------------------------------
//	Append spectral peaks, extracted from a reassigned time-frequency
//	spectrum, to eligible Partials, where possible. Peaks that cannot
//	be used to extend eliglble Partials spawn new Partials.
//
//	This is similar to the basic MQ partial formation strategy, except that
//	before matching, all frequencies are normalized by the value of the 
//	warping envelope at the time of the current frame. This means that
//	the frequency envelopes of all the Partials are warped, and need to 
//	be un-normalized by calling finishBuilding at the end of the building
//  process.
//
void 
PartialBuilder::buildPartials( Peaks & peaks, double frameTime )
{	
	mNewlyEligible.clear();
	
	unsigned int matchCount = 0;	//	for debugging
		
	//	frequency-sort the spectral peaks:
	//	(the eligible partials are always sorted by
	//	increasing frequency if we always sort the
	//	peaks this way)
	std::sort( peaks.begin(), peaks.end(), SpectralPeak::sort_increasing_freq );
	
	PartialPtrs::iterator eligible = mEligiblePartials.begin();
	for ( Peaks::iterator bpIter = peaks.begin(); bpIter != peaks.end(); ++bpIter ) 
	{
		//const Breakpoint & bp = bpIter->breakpoint;
		const double peakTime = frameTime + bpIter->time();
		
		// 	find the Partial that is nearest in frequency to the Peak:
		PartialPtrs::iterator nextEligible = eligible;
		if ( eligible != mEligiblePartials.end() &&
			 end_frequency( **eligible ) < bpIter->frequency() )
		{
			++nextEligible;
			while ( nextEligible != mEligiblePartials.end() &&
					end_frequency( **nextEligible ) < bpIter->frequency() )
			{
				++nextEligible;
				++eligible;
			}
			
			if ( nextEligible != mEligiblePartials.end() &&
				 better_match( **nextEligible, **eligible, *bpIter ) )
			{
				eligible = nextEligible;
			}
		}
		
		// 	INVARIANT:
		//
		//	eligible is the position of the nearest (in frequency)
		//	eligible Partial (pointer) or it is mEligiblePartials.end().
		//
		//	nextEligible is the eligible Partial with frequency 
		//	greater than bp, or it is mEligiblePartials.end().  
              
#if defined(Debug_Loris) && Debug_Loris
        /*
		if ( nextEligible != mEligiblePartials.end() )
		{
			debugger << matchFrequency << "( " << end_frequency( **eligible )
					 << ", " << end_frequency( **nextEligible ) << ")" << endl;
		}
        */
#endif
								
		//	create a new Partial if there is no eligible Partial,
		//	or the frequency difference to the eligible Partial is 
		//	too great, or the next peak is a better match for the 
		//	eligible Partial, otherwise add this peak to the eligible
		//	Partial:
		Peaks::iterator nextPeak = //Peaks::iterator( bpIter ); ++nextPeak;
								   ++Peaks::iterator( bpIter ); //  some compilers choke on this?

        //  decide whether this match should be made:
        //  - can only make the match if eligible is not the end of the list
        //  - the match is only good if it is close enough in frequency
        //  - even if the match is good, only match if the next one is not better
        bool makeMatch = false;
        if ( eligible != mEligiblePartials.end() )
        {
            bool matchIsGood =  mFreqDrift > 
                std::fabs( end_frequency( **eligible ) - bpIter->frequency() );
            if ( matchIsGood )
            {
                bool nextIsBetter = ( nextPeak != peaks.end() &&
                                      better_match( **eligible, *nextPeak, *bpIter ) ); 
                if ( ! nextIsBetter )
                {
                    makeMatch = true;
                }
            }                       
        }
        
        Breakpoint bp = bpIter->createBreakpoint();
        
        if ( makeMatch )
        {
            //  invariant:
            //  if makeMatch is true, then eligible is the position of a valid Partial
            (*eligible)->insert( peakTime, bp );
			mNewlyEligible.push_back( *eligible );
			
			++matchCount;
        }
        else
        {
            Partial p;
            p.insert( peakTime, bp );
            mCollectedPartials.push_back( p );
            mNewlyEligible.push_back( & mCollectedPartials.back() );
        }
        
		//	update eligible, nextEligible is the eligible Partial
		//	with frequency greater than bp, or it is mEligiblePartials.end():
		eligible = nextEligible;
	}			 
	 	
	mEligiblePartials = mNewlyEligible;
	
    /*
	debugger << "PartialBuilder::buildPartials: matched " << matchCount << endl;
	debugger << "PartialBuilder::buildPartials: " << mNewlyEligible.size() << " newly eligible partials" << endl;
    */
}
// ---------------------------------------------------------------------------
//	selectReassignmentMinima (private)
// ---------------------------------------------------------------------------
Peaks
SpectralPeakSelector::selectReassignmentMinima( ReassignedSpectrum & spectrum, 
                                                double minFrequency )
{
	using namespace std; // for abs and fabs

	const double sampsToHz = mSampleRate / spectrum.size();
	const double oneOverSR = 1. / mSampleRate;
	const double minFreqSample = minFrequency / sampsToHz;
	const double maxCorrectionSamples = mMaxTimeOffset * mSampleRate;
	
	Peaks peaks;
	
	int start_j = 1, end_j = (spectrum.size() / 2) - 2;
	
	double fsample = start_j;
	do 
	{
	    fsample = spectrum.reassignedFrequency( start_j++ );
	} while( fsample < minFreqSample );
	
	for ( int j = start_j; j < end_j; ++j ) 
	{	 

	    // look for changes in the frequency reassignment,
	    // from positive to negative correction, indicating
	    // a concentration of energy in the spectrum:
	    double next_fsample = spectrum.reassignedFrequency( j+1 );
	    if ( fsample > j && next_fsample < j + 1 )
	    {
	        //  choose the smaller correction of fsample or next_fsample:
	        // (could also choose the larger magnitude?)
	        double freq;
	        int peakidx;
	        if ( (fsample-j) < (j+1-next_fsample) )
	        {
	            freq = fsample * sampsToHz;
	            peakidx = j;
	        }
	        else
	        {
	            freq = next_fsample * sampsToHz;
	            peakidx = j+1;
	        }
            
            //  still possible that the frequency winds up being
            //  below the specified minimum
            if ( freq >= minFrequency )
            {            	         
                //	keep only peaks with small time corrections:
                double timeCorrectionSamps = spectrum.reassignedTime( peakidx );
                if ( fabs(timeCorrectionSamps) < maxCorrectionSamples )
                {
                    double mag = spectrum.reassignedMagnitude( peakidx );
                    double phase = spectrum.reassignedPhase( peakidx );    			

                    //	this will be overwritten later in analysis, 
                    //	might be ignored altogether, only used if the
                    //	mixed derivative convergence indicator is stored
                    //	as bandwidth in Analyzer:
                    double bw = spectrum.convergence( j );


                    //	also store the corrected peak time in seconds, won't
                    //	be able to compute it later:
                    double time = timeCorrectionSamps * oneOverSR;
                    Breakpoint bp( freq, mag, bw, phase );
                    peaks.push_back( SpectralPeak( time, bp ) );
                }
            }
                	        
	    }
	    fsample = next_fsample;
	}
    
	/*
	debugger << "SpectralPeakSelector::selectReassignmentMinima: found " 
             << peaks.size() << " peaks" << endl;
	*/
    	
	return peaks;

}
Exemple #6
0
// ---------------------------------------------------------------------------
//  analyze
// ---------------------------------------------------------------------------
//! Analyze a range of (mono) samples at the given sample rate      
//! (in Hz) and store the extracted Partials in the Analyzer's
//! PartialList (std::list of Partials). Use the specified envelope
//! as a frequency reference for Partial tracking.
//! 
//! \param bufBegin is a pointer to a buffer of floating point samples
//! \param bufEnd is (one-past) the end of a buffer of floating point 
//! samples
//! \param srate is the sample rate of the samples in the buffer
//! \param reference is an Envelope having the approximate
//! frequency contour expected of the resulting Partials.
//
void 
Analyzer::analyze( const double * bufBegin, const double * bufEnd, double srate,
                   const Envelope & reference )
{ 
    //  configure the reassigned spectral analyzer, 
    //  always use odd-length windows:

    //  Kaiser window
    double winshape = KaiserWindow::computeShape( sidelobeLevel() );
    long winlen = KaiserWindow::computeLength( windowWidth() / srate, winshape );    
    if (! (winlen % 2)) 
    {
        ++winlen;
    }
    debugger << "Using Kaiser window of length " << winlen << endl;
    
    std::vector< double > window( winlen );
    KaiserWindow::buildWindow( window, winshape );
    
    std::vector< double > windowDeriv( winlen );
    KaiserWindow::buildTimeDerivativeWindow( windowDeriv, winshape );
       
    ReassignedSpectrum spectrum( window, windowDeriv );   
    
    //  configure the peak selection and partial formation policies:
    SpectralPeakSelector selector( srate, m_cropTime );
    PartialBuilder builder( m_freqDrift, reference );
    
    //  configure bw association policy, unless
    //  bandwidth association is disabled:
    std::unique_ptr< AssociateBandwidth > bwAssociator;
    if( m_bwAssocParam > 0 )
    {
        debugger << "Using bandwidth association regions of width " 
                 << bwRegionWidth() << " Hz" << endl;
        bwAssociator.reset( new AssociateBandwidth( bwRegionWidth(), srate ) );
    }
    else
    {
        debugger << "Bandwidth association disabled" << endl;
    }

    //  reset envelope builders:
    m_ampEnvBuilder->reset();
    m_f0Builder->reset();
    
    m_partials.clear();
        
    try 
    { 
        const double * winMiddle = bufBegin; 

        //  loop over short-time analysis frames:
        while ( winMiddle < bufEnd )
        {
            //  compute the time of this analysis frame:
            const double currentFrameTime = long(winMiddle - bufBegin) / srate;
            
            //  compute reassigned spectrum:
            //  sampsBegin is the position of the first sample to be transformed,
            //  sampsEnd is the position after the last sample to be transformed.
            //  (these computations work for odd length windows only)
            const double * sampsBegin = std::max( winMiddle - (winlen / 2), bufBegin );
            const double * sampsEnd = std::min( winMiddle + (winlen / 2) + 1, bufEnd );
            spectrum.transform( sampsBegin, winMiddle, sampsEnd );
            
             
            //  extract peaks from the spectrum, and thin
            Peaks peaks = selector.selectPeaks( spectrum, m_freqFloor ); 
			Peaks::iterator rejected = thinPeaks( peaks, currentFrameTime );

            //	fix the stored bandwidth values
            //	KLUDGE: need to do this before the bandwidth
            //	associator tries to do its job, because the mixed
            //	derivative is temporarily stored in the Breakpoint 
            //	bandwidth!!! FIX!!!!
            fixBandwidth( peaks );
            
            if ( m_bwAssocParam > 0 )
            {
                bwAssociator->associateBandwidth( peaks.begin(), rejected, peaks.end() );
            }
            
            //  remove rejected Breakpoints (needed above to 
            //  compute bandwidth envelopes):
            peaks.erase( rejected, peaks.end() );
            
            //  estimate the amplitude in this frame:
            m_ampEnvBuilder->build( peaks, currentFrameTime );
                        
            //  collect amplitudes and frequencies and try to 
            //  estimate the fundamental
            m_f0Builder->build( peaks, currentFrameTime );          

            //  form Partials from the extracted Breakpoints:
            builder.buildPartials( peaks, currentFrameTime );
            
            //  slide the analysis window:
            winMiddle += long( m_hopTime * srate ); //  hop in samples, truncated

        }   //  end of loop over short-time frames
        
        //  unwarp the Partial frequency envelopes:
        builder.finishBuilding( m_partials );
        
        //  fix the frequencies and phases to be consistent.
        if ( m_phaseCorrect )
        {
            fixFrequency( m_partials.begin(), m_partials.end() );
        }
        
        
        //  for debugging:
        /*
        if ( ! m_ampEnv.empty() )
        {
            LinearEnvelope::iterator peakpos = 
                std::max_element( m_ampEnv.begin(), m_ampEnv.end(), 
                                  compare2nd<LinearEnvelope::iterator::value_type> );
            notifier << "Analyzer found amp peak at time : " << peakpos->first
                     << " value: " << peakpos->second << endl;
        }
        */
    }
    catch ( Exception & ex ) 
    {
        ex.append( "analysis failed." );
        throw;
    }
}
Exemple #7
0
// ---------------------------------------------------------------------------
//  AmpEnvBuilder::build
// ---------------------------------------------------------------------------
//
void AmpEnvBuilder::build( const Peaks & peaks, double frameTime )
{
    double x = std::accumulate( peaks.begin(), peaks.end(), 0.0, accumPeakSquaredAmps );
    mEnvelope.insert( frameTime, std::sqrt( x ) );
}
Exemple #8
0
// ---------------------------------------------------------------------------
//	thinPeaks (HELPER)
// ---------------------------------------------------------------------------
//	Reject peaks that are too quiet (low amplitude). Peaks that are retained,
//	but are quiet enough to be in the specified fadeRange should be faded.
//	Peaks having negative times are also rejected.
//
//	This is exactly the same as the basic peak selection strategy, there
//	is no tracking here.
//	
//	Rejected peaks are placed at the end of the peak collection.
//	Return the first position in the collection containing a rejected peak,
//	or the end of the collection if no peaks are rejected.
//
//	This used to be part of SpectralPeakSelector, but it really had no place
//	there. It _should_ remove the rejected peaks, but for now, those are needed
//	by the bandwidth association strategy.
//
Peaks::iterator 
Analyzer::thinPeaks( Peaks & peaks, double frameTime  )
{
	const double ampFloordB = m_ampFloor;

	//  fade quiet peaks out over 10 dB:
	const double fadeRangedB = 10.0;

	//	compute absolute magnitude thresholds:
	const double threshold = std::pow( 10., 0.05 * ampFloordB );
	const double beginFade = std::pow( 10., 0.05 * (ampFloordB+fadeRangedB) );

	//	louder peaks are preferred, so consider them 
	//	in order of louder magnitude:
	std::sort( peaks.begin(), peaks.end(), SpectralPeak::sort_greater_amplitude );
	
    //  negative times are not real, but still might represent
    //  a noisy part of the spectrum...
	Peaks::iterator bogusTimes = 
		std::remove_if( peaks.begin(), peaks.end(), negative_time( frameTime ) );
		
	//	...get rid of them anyway
    peaks.erase( bogusTimes, peaks.end() );
    bogusTimes = peaks.end();
        
    
	Peaks::iterator it = peaks.begin();
	Peaks::iterator beginRejected = it;

    const double freqResolution = 
    	std::max( m_freqResolutionEnv->valueAt( frameTime ), 0.0 ); 
    
    
	while ( it != peaks.end() ) 
	{
		SpectralPeak & pk = *it;
		
		//	keep this peak if it is loud enough and not
		//	 too near in frequency to a louder one:
		double lower = pk.frequency() - freqResolution;
		double upper = pk.frequency() + freqResolution;
		if ( pk.amplitude() > threshold &&
			 beginRejected == std::find_if( peaks.begin(), beginRejected, can_mask(lower, upper) ) )
		{
			//	this peak is a keeper, fade its
			//	amplitude if it is too quiet:
			if ( pk.amplitude() < beginFade )
			{
				double alpha = (beginFade - pk.amplitude())/(beginFade - threshold);
				pk.setAmplitude( pk.amplitude() * (1. - alpha) );
			}
			
			//	keep retained peaks at the front of the collection:
			if ( it != beginRejected )
			{
				std::swap( *it, *beginRejected );
			}
			++beginRejected;
		}
		++it;
	}
	
	// debugger << "thinPeaks retained " << std::distance( peaks.begin(), beginRejected ) << endl;

	//  remove rejected Breakpoints:
	//peaks.erase( beginRejected, peaks.end() );
	
	return beginRejected;
}