Example #1
0
// ---------------------------------------------------------------------------
//	frequencyAt
// ---------------------------------------------------------------------------
//!	Return the interpolated frequency (in Hz) of this Partial at the
//!	specified time. At times beyond the ends of the Partial, return
//!	the frequency at the nearest envelope endpoint. Throw an
//!	InvalidPartial exception if this Partial has no Breakpoints.
//
double
Partial::frequencyAt( double time ) const
{
	if ( numBreakpoints() == 0 )
	{
		Throw( InvalidPartial, "Tried to interpolate a Partial with no Breakpoints." );
	}
	
	//	lower_bound returns a reference to the lowest
	//	position that would be higher than an element
	//	having key equal to time:
	Partial::const_iterator it = findAfter( time );
		
	if ( it == begin() ) 
	{
	//	time is before the onset of the Partial:
		return it.breakpoint().frequency();
	}
	else if ( it == end() ) 
	{
	//	time is past the end of the Partial:
		return (--it).breakpoint().frequency();
	}
	else 
	{
	//	interpolate between it and its predeccessor
	//	(we checked already that it is not begin):
		const Breakpoint & hi = it.breakpoint();
		double hitime = it.time();
		const Breakpoint & lo = (--it).breakpoint();
		double lotime = it.time();
		double alpha = (time - lotime) / (hitime - lotime);
		return (alpha * hi.frequency()) + ((1. - alpha) * lo.frequency());
	}
}
Example #2
0
// ---------------------------------------------------------------------------
//	bandwidthAt
// ---------------------------------------------------------------------------
//!	Return the interpolated bandwidth (noisiness) coefficient of
//!	this Partial at the specified time. At times beyond the ends of
//!	the Partial, return the bandwidth coefficient at the nearest
//!	envelope endpoint. Throw an InvalidPartial exception if this
//!	Partial has no Breakpoints.
//	
double
Partial::bandwidthAt( double time ) const
{
	if ( numBreakpoints() == 0 )
	{
		Throw( InvalidPartial, "Tried to interpolate a Partial with no Breakpoints." );
	}
	
	//	findAfter returns the position of the earliest
	//	Breakpoint later than time, or the end
	//	position if no such Breakpoint exists:
	Partial::const_iterator it = findAfter( time );
		
	if ( it == begin() ) 
	{
	//	time is before the onset of the Partial:
		return it.breakpoint().bandwidth();
	}
	else if (it == end() ) 
	{
	//	time is past the end of the Partial:
		return (--it).breakpoint().bandwidth();
	}
	else 
	{
	//	interpolate between it and its predeccessor
	//	(we checked already that it is not begin):
		const Breakpoint & hi = it.breakpoint();
		double hitime = it.time();
		const Breakpoint & lo = (--it).breakpoint();
		double lotime = it.time();
		double alpha = (time - lotime) / (hitime - lotime);
		return (alpha * hi.bandwidth()) + ((1. - alpha) * lo.bandwidth());
	}
}
Example #3
0
// ---------------------------------------------------------------------------
//	phaseAt
// ---------------------------------------------------------------------------
//!	Return the interpolated phase (in radians) of this Partial at
//!	the specified time. At times beyond the ends of the Partial,
//!	return the extrapolated from the nearest envelope endpoint
//!	(assuming constant frequency, as reported by frequencyAt()).
//!	Throw an InvalidPartial exception if this Partial has no
//!	Breakpoints.
//	
double
Partial::phaseAt( double time ) const
{
	if ( numBreakpoints() == 0 )
	{
		Throw( InvalidPartial, "Tried to interpolate a Partial with no Breakpoints." );
	}
	
	//	findAfter returns the position of the earliest
	//	Breakpoint later than time, or the end
	//	position if no such Breakpoint exists:
	Partial::const_iterator it = findAfter( time );
		
	//	compute phase:
	if ( it == begin() ) 
	{
	//	time is before the onset of the Partial:
		double dp = 2. * Pi * (it.time() - time) * it.breakpoint().frequency();
		return std::fmod( it.breakpoint().phase() - dp, 2. * Pi);
	}
	else if (it == end() ) 
	{
	//	time is past the end of the Partial:
	//	( first decrement iterator to get the tail Breakpoint)
		--it;
		
		double dp = 2. * Pi * (time - it.time()) * it.breakpoint().frequency();
		return std::fmod( it.breakpoint().phase() + dp, 2. * Pi );
	}
	else 
	{
	//	interpolate between it and its predeccessor
	//	(we checked already that it is not begin):
		const Breakpoint & hi = it.breakpoint();
		double hitime = it.time();
		const Breakpoint & lo = (--it).breakpoint();
		double lotime = it.time();
		double alpha = (time - lotime) / (hitime - lotime);
		double finterp = ( alpha * hi.frequency() ) + 
						 ( ( 1. - alpha ) * lo.frequency() );

		//	need to keep fmod in here because other stuff 
		//	(Spc export and sdif export, for example) rely 
		//	on it:
		if ( alpha < 0.5 )
		{
			double favg = 0.5 * ( lo.frequency() + finterp );
			double dp = 2. * Pi * (time - lotime) * favg;
			return std::fmod( lo.phase() + dp, 2. * Pi );
		}
		else
		{
			double favg = 0.5 * ( hi.frequency() + finterp );
			double dp = 2. * Pi * (hitime - time) * favg;
			return std::fmod( hi.phase() - dp, 2. * Pi );
		}

	}
}
Example #4
0
// ---------------------------------------------------------------------------
//	phaseTravel
//
//	Compute the sinusoidal phase travel between two Breakpoints.
//	Return the total unwrapped phase travel.
//
static double phaseTravel( Partial::const_iterator bp0, Partial::const_iterator bp1 )
{
	return phaseTravel( bp0.breakpoint(), bp1.breakpoint(), 
	                    bp1.time() - bp0.time() );
	/*
	double f0 = bp0->frequency();
	double t0 = bp0.time();
	double f1 = bp1->frequency();
	double t1 = bp1.time();
	double favg = .5 * ( f0 + f1 );
	double dt = t1 - t0;
	return 2 * Pi * favg * dt;
	*/
}
// ---------------------------------------------------------------------------
//	channelize (one Partial)
// ---------------------------------------------------------------------------
//!	Label a Partial with the number of the frequency channel corresponding to
//!	the average frequency over all the Partial's Breakpoints.
//!	
//!	\param partial is the Partial to label.
//
void
Channelizer::channelize( Partial & partial ) const
{
	debugger << "channelizing Partial with " << partial.numBreakpoints() << " Breakpoints" << endl;
			
	//	compute an amplitude-weighted average channel
	//	label for each Partial:
	double ampsum = 0.;
	double weightedlabel = 0.;
	Partial::const_iterator bp;
	for ( bp = partial.begin(); bp != partial.end(); ++bp )
	{
		//	use sinusoidal amplitude:
		double a = bp.breakpoint().amplitude() * std::sqrt( 1. - bp.breakpoint().bandwidth() );
		
		//  This used to be an amplitude-weighted avg, but for many sounds, 
		//  particularly those for which the weighted avg would be very
		//  different from the simple avg, the amplitude-weighted avg
		//  emphasized the part of the sound in which the frequency estimates
		//  are least reliable (e.g. a piano tone). The unweighted 
		//  average should give more intuitive results in most cases.
				
		double f = bp.breakpoint().frequency();
		double t = bp.time();
		
		double refFreq = _refChannelFreq->valueAt( t ) / _refChannelLabel;
		// weightedlabel += a * (f / refFreq);
		weightedlabel += a * giveMeN( f, refFreq, _stretchFactor );
		ampsum += a;
	}
	
	int label;
	if ( ampsum > 0. )	
	// if ( 0 < partial.numBreakpoints() )
	{
		label = (int)((weightedlabel / ampsum) + 0.5);
	}
	else	//	this should never happen, but just in case:
	{
		label = 0;
	}
	Assert( label >= 0 );
			
	//	assign label, and remember it, but
	//	only if it is a valid (positive) 
	//	distillation label:
	partial.setLabel( label );

}
Example #6
0
// ---------------------------------------------------------------------------
//	channelize (one Partial)
// ---------------------------------------------------------------------------
//!	Label a Partial with the number of the frequency channel corresponding to
//!	the average frequency over all the Partial's Breakpoints.
//!	
//!	\param partial is the Partial to label.
//
void
Channelizer::channelize( Partial & partial ) const
{
    using std::pow;

	debugger << "channelizing Partial with " << partial.numBreakpoints() << " Breakpoints" << endl;
			
	//	compute an amplitude-weighted average channel
	//	label for each Partial:
	//double ampsum = 0.;
	double weightedlabel = 0.;
	Partial::const_iterator bp;
	for ( bp = partial.begin(); bp != partial.end(); ++bp )
	{
				
		double f = bp.breakpoint().frequency();
		double t = bp.time();
		
        double weight = 1;
        if ( 0 != _ampWeighting )
        {
            //  This used to be an amplitude-weighted avg, but for many sounds, 
            //  particularly those for which the weighted avg would be very
            //  different from the simple avg, the amplitude-weighted avg
            //  emphasized the part of the sound in which the frequency estimates
            //  are least reliable (e.g. a piano tone). The unweighted 
            //  average should give more intuitive results in most cases.

            //	use sinusoidal amplitude:
            double a = bp.breakpoint().amplitude() * std::sqrt( 1. - bp.breakpoint().bandwidth() );                
            weight = pow( a, _ampWeighting );
        }
        
        weightedlabel += weight * computeFractionalChannelNumber( t, f );
	}
	
	int label = 0;
	if ( 0 < partial.numBreakpoints() ) //  should always be the case
	{        
		label = (int)((weightedlabel / partial.numBreakpoints()) + 0.5);
	}
	Assert( label >= 0 );
			
	//	assign label, and remember it, but
	//	only if it is a valid (positive) 
	//	distillation label:
	partial.setLabel( label );

}
Example #7
0
// ---------------------------------------------------------------------------
//	amplitudeAt
// ---------------------------------------------------------------------------
//!	Return the interpolated amplitude of this Partial at the
//!	specified time. Throw an InvalidPartial exception if this 
//!	Partial has no Breakpoints. If non-zero fadeTime is specified, 
//!	then the amplitude at the ends of the Partial is coomputed using
//!	a linear fade. The default fadeTime is ShortestSafeFadeTime,
//!	see the definition of ShortestSafeFadeTime, above.
//	
double
Partial::amplitudeAt( double time, double fadeTime ) const
{
	if ( numBreakpoints() == 0 )
		Throw( InvalidPartial, "Tried to interpolate a Partial with no Breakpoints." );

	//	findAfter returns the position of the earliest
	//	Breakpoint later than time, or the end
	//	position if no such Breakpoint exists:
	Partial::const_iterator it = findAfter( time );
		
	if ( it == begin() ) 
	{
		double alpha =  (time < it.time()) ? 0. : 1.;
		if ( fadeTime > 0 )
		{
			//	fade in ampltude if time is before the onset of the Partial:
			alpha = std::max(0., 1. - ((it.time() - time) / fadeTime) );
		}
		return alpha * it.breakpoint().amplitude();
	}
	else if ( it == end() ) 
	{
		//	( first decrement iterator to get the tail Breakpoint)
		--it;
		
		double alpha =  (time > it.time()) ? 0. : 1.;
		if ( fadeTime > 0 )
		{
			//	fade out ampltude if time is past the end of the Partial:
			alpha = std::max(0., 1. - ((time - it.time()) / fadeTime) );
		}
		return alpha * it.breakpoint().amplitude();
	}
	else 
	{
	//	interpolate between it and its predeccessor
	//	(we checked already that it is not begin):
		const Breakpoint & hi = it.breakpoint();
		double hitime = it.time();
		const Breakpoint & lo = (--it).breakpoint();
		double lotime = it.time();
		double alpha = (time - lotime) / (hitime - lotime);
		return (alpha * hi.amplitude()) + ((1. - alpha) * lo.amplitude());
	}
}
Example #8
0
// ---------------------------------------------------------------------------
//  setup
// ---------------------------------------------------------------------------
//!	Prepare internal structures for synthesis. PartialList is transformed to
//! to more conveniant structure for real-time processing. reset() is also called.
//! Fade in/out Breakpoints are inserted at either end of the Partial.
//! Partials with start times earlier than the Partial fade
//! time will have shorter onset fades. 
//!
//! \param  partials The Partials to synthesize.
//! \param  pitch original pitch of the partials
//! \return Nothing.
//! \post   This RealTimeSynthesizer's is ready for synthesise the sound specified
//!         by given partials.
void RealTimeSynthesizer::setup(PartialList & partials, double pitch) noexcept
{
    this->partials.clear();
    this->pitch = pitch;
    clearPartialsBeingProcessed();
    
    // assuming I am getting sorted partials by time
    for (auto it : partials)
    {
        if (it.numBreakpoints() <= 0) continue;
        
        PartialStruct pStruct;
        
        pStruct.numBreakpoints = it.numBreakpoints() + 2;// + fade in + fade out

        pStruct.breakpoints.reserve(pStruct.numBreakpoints);
        pStruct.label = it.label();
        
        pStruct.startTime = ( m_fadeTimeSec < it.startTime() ) ? ( it.startTime() - m_fadeTimeSec ) : 0.;// compute fade in bp time
        pStruct.endTime = it.endTime() + m_fadeTimeSec;// compute fade out bp time

        
        // breakpoints
        Partial::const_iterator jt = it.begin();
        // fade in breakpoint, compute fade in time
        pStruct.breakpoints.push_back(std::make_pair(pStruct.startTime, BreakpointUtils::makeNullBefore( jt.breakpoint(), it.startTime() - pStruct.startTime)));
        

        
        double sumF = 0;
        
        for (; jt != it.end(); jt++)
        {
            sumF += jt->frequency();
            pStruct.breakpoints.push_back(std::make_pair(jt.time(), jt.breakpoint()));
        }
        
        pStruct.avgFrequency = sumF / (float) it.numBreakpoints();
        
        // fade out breakpoint
        jt--;
        pStruct.breakpoints.push_back(std::make_pair(jt.time() + m_fadeTimeSec, BreakpointUtils::makeNullAfter( jt.breakpoint(), m_fadeTimeSec )));
        
        this->partials.push_back(pStruct);
//        pStruct.breakpoints[0].second.setAmplitude(pStruct.breakpoints[1].second.amplitude());
    }

    
    reset();
}
// ---------------------------------------------------------------------------
//	merge	(STATIC)
// ---------------------------------------------------------------------------
//	Merge the Breakpoints in the specified iterator range into the
//	distilled Partial. The beginning of the range may overlap, and 
//	will replace, some non-zero-amplitude portion of the distilled
//	Partial. Assume that there is no such overlap at the end of the 
//	range (could check), because findContribution only leaves overlap
//  at the beginning of the range.
//
static void merge( Partial::const_iterator beg, 
				   Partial::const_iterator end, 
				   Partial & destPartial, double fadeTime, 
				   double gapTime = 0. )
{	
	//	absorb energy in distilled Partial that overlaps the
	//	range to merge:
	Partial toMerge( beg, end );
	toMerge.absorb( destPartial );  
    fadeInAndOut( toMerge, fadeTime );

		
    //  find the first Breakpoint in destPartial that is after the
    //  range of merged Breakpoints, plus the required gap:
	Partial::iterator removeEnd = destPartial.findAfter( toMerge.endTime() + gapTime );
    
    //  if this Breakpoint has non-zero amplitude, need to leave time
    //  for a fade in:
	while ( removeEnd != destPartial.end() &&
            removeEnd.breakpoint().amplitude() != 0 &&
            removeEnd.time() < toMerge.endTime() + gapTime + fadeTime )
    {
        ++removeEnd;
    }   
    
	//	find the first Breakpoint in the destination Partial that needs
    //  to be removed because it is in the overlap region:
	Partial::iterator removeBegin = destPartial.findAfter( toMerge.startTime() - gapTime );
        
    //  if beforeMerge has non-zero amplitude, need to leave time
    //  for a fade out:
    if ( removeBegin != destPartial.begin() )
	{
        Partial::iterator beforeMerge = --Partial::iterator(removeBegin);
        
        while ( removeBegin != destPartial.begin() &&
                beforeMerge.breakpoint().amplitude() != 0 &&
                beforeMerge.time() > toMerge.startTime() - gapTime - fadeTime )
        {
            --removeBegin;
            if ( beforeMerge != destPartial.begin() )
            {
                --beforeMerge;
            }
        }   
        
	}
	
	//	remove the Breakpoints in the merge range from destPartial:
    double rbt = (removeBegin != destPartial.end())?(removeBegin.time()):(destPartial.endTime());
    double ret = (removeEnd != destPartial.end())?(removeEnd.time()):(destPartial.endTime());
    Assert( rbt <= ret );
	destPartial.erase( removeBegin, removeEnd );

    //  how about doing the fades here instead?
    //  fade in if necessary:
	if ( removeEnd != destPartial.end() &&
         removeEnd.breakpoint().amplitude() != 0 )
	{
        Assert( removeEnd.time() - fadeTime > toMerge.endTime() );

        //	update removeEnd so that we don't remove this 
        //	null we are inserting:
        destPartial.insert( 
            removeEnd.time() - fadeTime, 
            BreakpointUtils::makeNullBefore( removeEnd.breakpoint(), fadeTime ) );
	}

    if ( removeEnd != destPartial.begin() )
    {
        Partial::iterator beforeMerge = --Partial::iterator(removeEnd);
        if ( beforeMerge.breakpoint().amplitude() > 0 )
        {
            Assert( beforeMerge.time() + fadeTime < toMerge.startTime() );
            
            destPartial.insert( 
                beforeMerge.time() + fadeTime, 
                BreakpointUtils::makeNullAfter( beforeMerge.breakpoint(), fadeTime ) );
        }
    }		
    
    //	insert the Breakpoints in the range:
	for ( Partial::const_iterator insert = toMerge.begin(); insert != toMerge.end(); ++insert )
	{
		destPartial.insert( insert.time(), insert.breakpoint() );
	}
}
Example #10
0
// ---------------------------------------------------------------------------
//	parametersAt
// ---------------------------------------------------------------------------
//!	Return the interpolated parameters of this Partial at
//!	the specified time, same as building a Breakpoint from
//!	the results of frequencyAt, ampitudeAt, bandwidthAt, and
//!	phaseAt, but performs only one Breakpoint envelope search.
//!	Throw an InvalidPartial exception if this Partial has no
//!	Breakpoints. If non-zero fadeTime is specified, then the
//!	amplitude at the ends of the Partial is coomputed using a 
//!	linear fade. The default fadeTime is ShortestSafeFadeTime.
//
Breakpoint
Partial::parametersAt( double time, double fadeTime ) const 
{
	if ( numBreakpoints() == 0 )
	{
		Throw( InvalidPartial, "Tried to interpolate a Partial with no Breakpoints." );
	}
	
	//	findAfter returns the position of the earliest
	//	Breakpoint later than time, or the end
	//	position if no such Breakpoint exists:
	Partial::const_iterator it = findAfter( time );
		
	if ( it == begin() ) 
	{
		//	time is before the onset of the Partial:
		//	frequency is starting frequency, 
		//	amplitude is 0 (or fading), bandwidth is starting 
		//	bandwidth, and phase is rolled back.
		double alpha =  (time < it.time()) ? 0. : 1.;
		if ( fadeTime > 0 )
		{
			//	fade in ampltude if time is before the onset of the Partial:
			alpha = std::max(0., 1. - ((it.time() - time) / fadeTime) );
		}
		double amp = alpha * it.breakpoint().amplitude();

		double dp = 2. * Pi * (it.time() - time) * it.breakpoint().frequency();
		double ph = std::fmod( it.breakpoint().phase() - dp, 2. * Pi);
		
		return Breakpoint( it.breakpoint().frequency(), amp, 
						   it.breakpoint().bandwidth(), ph );
	}
	else if (it == end() ) 
	{
		//	time is past the end of the Partial:
		//	frequency is ending frequency, 
		//	amplitude is 0 (or fading), bandwidth is ending 
		//	bandwidth, and phase is rolled forward.
		--it; 
		
		double alpha =  (time > it.time()) ? 0. : 1.;
		if ( fadeTime > 0 )
		{
			//	fade out ampltude if time is past the end of the Partial:
			alpha = std::max(0., 1. - ((time - it.time()) / fadeTime) );
		}
		double amp = alpha * it.breakpoint().amplitude();

		double dp = 2. * Pi * (time - it.time()) * it.breakpoint().frequency();
		double ph = std::fmod( it.breakpoint().phase() + dp, 2. * Pi );

		return Breakpoint( it.breakpoint().frequency(), amp, 
						   it.breakpoint().bandwidth(), ph );
	}
	else 
	{
	//	interpolate between it and its predeccessor
	//	(we checked already that it is not begin):
		const Breakpoint & hi = it.breakpoint();
		double hitime = it.time();
		const Breakpoint & lo = (--it).breakpoint();
		double lotime = it.time();
		double alpha = (time - lotime) / (hitime - lotime);

		double finterp = ( alpha * hi.frequency() ) + 
						 ( ( 1. - alpha ) * lo.frequency() );

		//	need to keep fmod in here because other stuff 
		//	(Spc export and sdif export, for example) rely 
		//	on it:
		double ph = 0;
		if ( alpha < 0.5 )
		{
			double favg = 0.5 * ( lo.frequency() + finterp );
			double dp = 2. * Pi * (time - lotime) * favg;
			ph = std::fmod( lo.phase() + dp, 2. * Pi );
		}
		else
		{
			double favg = 0.5 * ( hi.frequency() + finterp );
			double dp = 2. * Pi * (hitime - time) * favg;
			ph = std::fmod( hi.phase() - dp, 2. * Pi );
		}

		return Breakpoint( (alpha * hi.frequency()) + ((1. - alpha) * lo.frequency()),
						   (alpha * hi.amplitude()) + ((1. - alpha) * lo.amplitude()),
						   (alpha * hi.bandwidth()) + ((1. - alpha) * lo.bandwidth()),
						   ph );
				
	}
}
Example #11
0
// ---------------------------------------------------------------------------
//  synthesize
// ---------------------------------------------------------------------------
//! Synthesize a bandwidth-enhanced sinusoidal Partial. Zero-amplitude
//! Breakpoints are inserted at either end of the Partial to reduce
//! turn-on and turn-off artifacts, as described above. The synthesizer
//! will resize the buffer as necessary to accommodate all the samples,
//! including the fade out. Previous contents of the buffer are not
//! overwritten. Partials with start times earlier than the Partial fade
//! time will have shorter onset fades. Partials are not rendered at
//! frequencies above the half-sample rate. 
//!
//! \param  p The Partial to synthesize.
//! \return Nothing.
//! \pre    The partial must have non-negative start time.
//! \post   This Synthesizer's sample buffer (vector) has been 
//!         resized to accommodate the entire duration of the 
//!         Partial, p, including fade out at the end.
//! \throw  InvalidPartial if the Partial has negative start time.
//  
void
Synthesizer::synthesize( Partial p ) 
{
    if ( p.numBreakpoints() == 0 )
    {
        debugger << "Synthesizer ignoring a partial that contains no Breakpoints" << endl;
        return;
    }
    
    if ( p.startTime() < 0 )
    {
        Throw( InvalidPartial, "Tried to synthesize a Partial having start time less than 0." );
    }

    debugger << "synthesizing Partial from " << p.startTime() * m_srateHz 
             << " to " << p.endTime() * m_srateHz << " starting phase "
             << p.initialPhase() << " starting frequency " 
             << p.first().frequency() << endl;
             
    //  better to compute this only once:
    const double OneOverSrate = 1. / m_srateHz;
    
             
    //  use a Resampler to quantize the Breakpoint times and 
    //  correct the phases:
    Resampler quantizer( OneOverSrate );
    quantizer.setPhaseCorrect( true );
    quantizer.quantize( p );
    

    //  resize the sample buffer if necessary:
    typedef unsigned long index_type;
    index_type endSamp = index_type( ( p.endTime() + m_fadeTimeSec ) * m_srateHz );
    if ( endSamp+1 > m_sampleBuffer->size() )
    {
        //  pad by one sample:
        m_sampleBuffer->resize( endSamp+1 );
    }
    
    //  compute the starting time for synthesis of this Partial,
    //  m_fadeTimeSec before the Partial's startTime, but not before 0:
    double itime = ( m_fadeTimeSec < p.startTime() ) ? ( p.startTime() - m_fadeTimeSec ) : 0.;
    index_type currentSamp = index_type( (itime * m_srateHz) + 0.5 );   //  cheap rounding
    
    //  reset the oscillator:
    //  all that really needs to happen here is setting the frequency
    //  correctly, the phase will be reset again in the loop over 
    //  Breakpoints below, and the amp and bw can start at 0.
    m_osc.resetEnvelopes( BreakpointUtils::makeNullBefore( p.first(), p.startTime() - itime ), m_srateHz );

    //  cache the previous frequency (in Hz) so that it
    //  can be used to reset the phase when necessary
    //  in the sample computation loop below (this saves
    //  having to recompute from the oscillator's radian
    //  frequency):
    double prevFrequency = p.first().frequency();   
    
    //  synthesize linear-frequency segments until 
    //  there aren't any more Breakpoints to make segments:
    double * bufferBegin = &( m_sampleBuffer->front() );
    for ( Partial::const_iterator it = p.begin(); it != p.end(); ++it )
    {
        index_type tgtSamp = index_type( (it.time() * m_srateHz) + 0.5 );   //  cheap rounding
        Assert( tgtSamp >= currentSamp );
        
        //  if the current oscillator amplitude is
        //  zero, and the target Breakpoint amplitude
        //  is not, reset the oscillator phase so that
        //  it matches exactly the target Breakpoint 
        //  phase at tgtSamp:
        if ( m_osc.amplitude() == 0. )
        {
            //  recompute the phase so that it is correct
            //  at the target Breakpoint (need to do this
            //  because the null Breakpoint phase was computed
            //  from an interval in seconds, not samples, so
            //  it might be inaccurate):
            //
            //  double favg = 0.5 * ( prevFrequency + it.breakpoint().frequency() );
            //  double dphase = 2 * Pi * favg * ( tgtSamp - currentSamp ) / m_srateHz;
            //
            double dphase = Pi * ( prevFrequency + it.breakpoint().frequency() ) 
                               * ( tgtSamp - currentSamp ) * OneOverSrate;
            m_osc.setPhase( it.breakpoint().phase() - dphase );
        }

        m_osc.oscillate( bufferBegin + currentSamp, bufferBegin + tgtSamp,
                         it.breakpoint(), m_srateHz );
        
        currentSamp = tgtSamp;
        
        //  remember the frequency, may need it to reset the 
        //  phase if a Null Breakpoint is encountered:
        prevFrequency = it.breakpoint().frequency();
    }

    //  render a fade out segment:  
    m_osc.oscillate( bufferBegin + currentSamp, bufferBegin + endSamp,
                     BreakpointUtils::makeNullAfter( p.last(), m_fadeTimeSec ), m_srateHz );
    
}
Example #12
0
// ---------------------------------------------------------------------------
//	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;
}