Example #1
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;
    }
}
Example #2
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;
}