bool PartialBuilder::better_match( const Partial & part1, 
                                   const Partial & part2, const SpectralPeak & pk )
{
	Assert( part1.numBreakpoints() > 0 );
	Assert( part2.numBreakpoints() > 0 );
	
	return freq_distance( part1, pk ) < freq_distance( part2, pk );
}	
Beispiel #2
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 );

}
bool PartialBuilder::better_match( const Partial & part, const SpectralPeak & pk1,
                                   const SpectralPeak & pk2 )
{
	Assert( part.numBreakpoints() > 0 );
	
	return freq_distance( part, pk1 ) < freq_distance( part, pk2 );
}	                                   
Beispiel #4
0
// ---------------------------------------------------------------------------
//	fixPhaseBefore
//
//! Recompute phases of all Breakpoints earlier than the specified time 
//! so that the synthesize phases of those earlier Breakpoints matches 
//! the stored phase, and the synthesized phase at the specified
//! time matches the stored (not recomputed) phase.
//! 
//! Backward phase-fixing stops if a null (zero-amplitude) Breakpoint
//! is encountered, because nulls are interpreted as phase reset points
//! in Loris. If a null is encountered, the remainder of the Partial
//! (the front part) is fixed in the forward direction, beginning at
//! the start of the Partial.
//!
//! \param p    The Partial whose phases should be fixed.
//! \param t    The time before which phases should be adjusted.
//
void fixPhaseBefore( Partial & p, double t )
{
    if ( 1 < p.numBreakpoints() )
    {
        Partial::iterator pos = p.findNearest( t );
        Assert( pos != p.end() );

        fixPhaseBackward( p.begin(), pos );
    }
}
Beispiel #5
0
// ---------------------------------------------------------------------------
//	fixPhaseAfter
//
//! Recompute phases of all Breakpoints later than the specified time 
//! so that the synthesize phases of those later Breakpoints matches 
//! the stored phase, as long as the synthesized phase at the specified
//! time matches the stored (not recomputed) phase.
//! 
//! Phase fixing is only applied to non-null (nonzero-amplitude) Breakpoints,
//! because null Breakpoints are interpreted as phase reset points in 
//! Loris. If a null is encountered, its phase is simply left unmodified,
//! and future phases wil be recomputed from that one.
//!
//! \param p    The Partial whose phases should be fixed.
//! \param t    The time after which phases should be adjusted.
//
void fixPhaseAfter( Partial & p, double t )
{
    //  nothing to do it there are not at least
    //  two Breakpoints in the Partial   
    if ( 1 < p.numBreakpoints() )
    {
        Partial::iterator pos = p.findNearest( t );
        Assert( pos != p.end() );

        fixPhaseForward( pos, --p.end() );
    }
}
// ---------------------------------------------------------------------------
//	fixFrequency
//
//!	Adjust frequencies of the Breakpoints in the 
//! specified Partial such that the rendered Partial 
//!	achieves (or matches as nearly as possible, within 
//!	the constraint of the maximum allowable frequency
//! alteration) the analyzed phases. 
//!
//! This just iterates over the Partial calling
//! matchPhaseFwd, should probably name those similarly.
//!
//!  \param     partial The Partial whose frequencies,
//!             and possibly phases (if the frequencies
//!             cannot be sufficiently altered to match
//!             the phases), will be recomputed.
//!  \param     maxFixPct The maximum allowable frequency 
//!             alteration, default is 0.2%.
//
void fixFrequency( Partial & partial, double maxFixPct )
{	
	if ( partial.numBreakpoints() > 1 )
	{	
		Partial::iterator next = partial.begin();
		Partial::iterator prev = next++;
		while ( next != partial.end() )		
		{
			matchPhaseFwd( prev.breakpoint(), next.breakpoint(), 
						   next.time() - prev.time(), 0.5, maxFixPct );
			prev = next++;
		}
    }
}
// ---------------------------------------------------------------------------
//	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 );

}
Beispiel #8
0
// ---------------------------------------------------------------------------
//	avgFrequency
// ---------------------------------------------------------------------------
//! Return the average frequency over all Breakpoints in this Partial.
//! Return zero if the Partial has no Breakpoints.
//!  
//! \param  p is the Partial to evaluate
//! \return the average frequency (Hz) of Breakpoints in the Partial p
//
double avgFrequency( const Partial & p )
{
    double avg = 0;
    for ( Partial::const_iterator it = p.begin();
          it != p.end();
          ++it )
    {
        avg += it->frequency();
    }
    
    if ( avg != 0 )
    {
        avg /= p.numBreakpoints();
    }
    
    return avg;
}
Beispiel #9
0
// ---------------------------------------------------------------------------
//	fixPhaseForward
//
//! Recompute phases of all Breakpoints later than the specified time 
//! so that the synthesize phases of those later Breakpoints matches 
//! the stored phase, as long as the synthesized phase at the specified
//! time matches the stored (not recomputed) phase. Breakpoints later than
//! tend are unmodified.
//! 
//! Phase fixing is only applied to non-null (nonzero-amplitude) Breakpoints,
//! because null Breakpoints are interpreted as phase reset points in 
//! Loris. If a null is encountered, its phase is simply left unmodified,
//! and future phases wil be recomputed from that one.
//!
//! HEY Is this interesting, in general? Why would you want to do this?
//!
//! \param p    The Partial whose phases should be fixed.
//! \param tbeg The phases and frequencies of Breakpoints later than the 
//!             one nearest this time will be modified.
//! \param tend The phases and frequencies of Breakpoints earlier than the 
//!             one nearest this time will be modified. Should be greater 
//!             than tbeg, or else they will be swapped.
//
void fixPhaseForward( Partial & p, double tbeg, double tend )
{
    if ( tbeg > tend )
    {
        std::swap( tbeg, tend );
    }
    
    //  nothing to do it there are not at least
    //  two Breakpoints in the Partial   
    if ( 1 < p.numBreakpoints() )
    {
        //  find the positions nearest tbeg and tend
        Partial::iterator posbeg = p.findNearest( tbeg );
        Partial::iterator posend = p.findNearest( tend );
        
        //  if the positions are different, and tend is
        //  the end, back it up
        if ( posbeg != posend && posend == p.end() )
        {
            --posend;
        }
        fixPhaseForward( posbeg, posend );
    }
}
Beispiel #10
0
// ----------- test_distill_overlapping3 -----------
//
static void test_distill_overlapping3( void )
{
    std::cout << "\t--- testing distill on three "
                 "temporally-overlapping Partials... ---\n\n";

    //  Fabricate three Partials, overlapping temporally, give
    //  them the same label, and distill them.
    Partial p1;
    p1.insert( 0, Breakpoint( 100, 0.4, 0, 0 ) );
    p1.insert( 0.28, Breakpoint( 100, 0.4, 0, .1 ) );
    p1.setLabel( 123 );
    
    Partial p2;
    p2.insert( 0.2, Breakpoint( 200, 0.3, 0.2, 0 ) );
    p2.insert( 0.29, Breakpoint( 200, 0.3, 0.2, .1 ) );
    p2.insert( 0.35, Breakpoint( 200, 0.3, 0.2, .1 ) );
    p2.setLabel( 123 );

    Partial p3;
    p3.insert( 0.32, Breakpoint( 300, 0.3, 0, 0 ) );
    p3.insert( 0.4, Breakpoint( 310, 0.3, 0.2, .1 ) );
    p3.insert( 0.7, Breakpoint( 310, 0.3, 0.2, .1 ) );
    p3.setLabel( 123 );

    PartialList l;
    l.push_back( p3 );
    l.push_back( p1 );
    l.push_back( p2 );

    const double fade = .008; // 8 ms
    Distiller d( fade );
    d.distill( l );
    
    //  Fabricate the Partial that the distillation should 
    //  produce.
    Partial compare;
    
    //  first Breakpoint from p1
    compare.insert( 0, Breakpoint( 100, 0.4, 0, 0 ) );

    //  null Breakpoint at 0+fade
    double t = 0 + fade;
    compare.insert( t, Breakpoint( p1.frequencyAt(t), 0, 
                                   p1.bandwidthAt(t), p1.phaseAt(t) ) );
    //  interpolated Breakpoint at .18 (.2 - 2*fade)
    //double t = 0.2 - (2*fade);
    //compare.insert( t, p1.parametersAt( t ) );

    //  null Breakpoint at .19 (.2-fade)
    //  bandwidth introduced in the overlap region:
    //  (0.4^2 + 0.2*0.3^2) / (0.3^2 + 0.4^2)) = 0.712
    //  amp = sqrt(0.3^2 + 0.4^2) = .5
    //  no, actually zero-amplitude Breakpoints are
    //  introduced with zero bandwidth.
    t = 0.2 - fade;
    compare.insert( t, Breakpoint( p2.frequencyAt(t), 0, 
                                   0, // 0.712, 
                                   p2.phaseAt(t) ) );
                                   
    //  first Breakpoint from p2:
    compare.insert( 0.2, Breakpoint( 200, 0.5, 0.712, 0 ) );
                                   
    //  second Breakpoint from p2:
    compare.insert( 0.29, Breakpoint( 200, 0.3, 0.2, 0.1 ) );

    //  null Breakpoint at .29 + fade
    t = 0.29 + fade;
    compare.insert( t, Breakpoint( p2.frequencyAt(t), 0, 
                                   0, // p2.bandwidthAt(t), 
                                   p2.phaseAt(t) ) );

    //  null Breakpoint at .31 (.32-fade)
    t = 0.32 - fade;
    compare.insert( t, Breakpoint( p3.frequencyAt(t), 0, 
                                   0, p3.phaseAt(t) ) );

    //  first Breakpoint from p3 (with bandwidth):
    compare.insert( 0.32, Breakpoint( 300, std::sqrt(0.18), 0.5, 0 ) );
                                   
    //  second Breakpoint from p3:
    compare.insert( 0.4, Breakpoint( 310, 0.3, 0.2, .1 ) );
                                   
    //  third Breakpoint from p3:
    compare.insert( 0.7, Breakpoint( 310, 0.3, 0.2, .1 ) );
    compare.setLabel( 123 );

    //  compare Partials (distilled Partials
    //  should be in label order):
    TEST_VALUE( l.size(), 1 );
    TEST_VALUE( l.begin()->numBreakpoints(), compare.numBreakpoints() );
    
    Partial::iterator distit = l.begin()->begin();
    Partial::iterator compareit = compare.begin();
    while ( compareit != compare.end() )
    {
        SAME_PARAM_VALUES( distit.time(), compareit.time() );
        SAME_PARAM_VALUES( distit->frequency(), compareit->frequency() );
        SAME_PARAM_VALUES( distit->amplitude(), compareit->amplitude() );
        SAME_PARAM_VALUES( distit->bandwidth(), compareit->bandwidth() );
        SAME_PARAM_VALUES( wrapPi( distit->phase() ), wrapPi( compareit->phase() ) );
        
        ++compareit;
        ++distit;
    }
}
Beispiel #11
0
// ----------- test_distill_overlapping2 -----------
//
static void test_distill_overlapping2( void )
{
    std::cout << "\t--- testing distill on two "
                 "temporally-overlapping Partials... ---\n\n";

    //  Fabricate two Partials, overlapping temporally, give
    //  them the same label, and distill them.
    Partial p1;
    p1.insert( 0, Breakpoint( 100, 0.4, 0, 0 ) );
    p1.insert( 0.3, Breakpoint( 100, 0.4, 0, .1 ) );
    p1.setLabel( 12 );
    
    Partial p2;
    p2.insert( 0.2, Breakpoint( 200, 0.3, 0, 0 ) );
    p2.insert( 0.35, Breakpoint( 210, 0.3, 0.2, .1 ) );
    p2.setLabel( 12 );

    PartialList l;
    l.push_back( p1 );
    l.push_back( p2 );

    const double fade = .01; // 10 ms
    Distiller d( fade );
    d.distill( l );
    
    for ( Partial::iterator distit = l.front().begin(); distit != l.front().end(); ++distit )
    {
        cout << distit.time() << " " << distit.breakpoint().frequency() << endl;
    }
    
    //  Fabricate the Partial that the distillation should 
    //  produce.
    Partial compare;
    
    //  first Breakpoint from p1
    compare.insert( 0, Breakpoint( 100, 0.4, 0, 0 ) );
    
    //  null Breakpoint at 0+fade
    double t = 0 + fade;
    compare.insert( t, Breakpoint( p1.frequencyAt(t), 0, 
                                   p1.bandwidthAt(t), p1.phaseAt(t) ) );

    //  interpolated Breakpoint at .18 (.2 - 2*fade)
    //double t = 0.2 - (2*fade);
    //compare.insert( t, p1.parametersAt( t ) );
    
    //  null Breakpoint at .19 (.2-fade)
    //  bandwidth introduced in the overlap region:
    //  0.4^2 / (0.3^2 + 0.4^2) = 0.64
    //  amp = sqrt(0.3^2 + 0.4^2) = .5
    //  no, actually zero-amplitude Breakpoints are
    //  introduced with zero bandwidth.
    t = 0.2 - fade;
    compare.insert( t, Breakpoint( p2.frequencyAt(t), 0, 
                                   0, // 0.64, 
                                   p2.phaseAt(t) ) );
                                   
    //  first Breakpoint from p2:
    compare.insert( 0.2, Breakpoint( 200, 0.5, 0.64, 0 ) );
    
    //  second Breakpoint from p2
    compare.insert( 0.35, Breakpoint( 210, 0.3, 0.2, .1 ) );
    compare.setLabel( 12 );

    //  compare Partials (distilled Partials
    //  should be in label order):
    TEST( l.size() == 1 );
    TEST( l.begin()->numBreakpoints() == compare.numBreakpoints() );
    TEST( l.begin()->label() == compare.label() );
    
    Partial::iterator distit = l.begin()->begin();
    Partial::iterator compareit = compare.begin();
    while ( compareit != compare.end() )
    {
        SAME_PARAM_VALUES( distit.time(), compareit.time() );
        SAME_PARAM_VALUES( distit->frequency(), compareit->frequency() );
        SAME_PARAM_VALUES( distit->amplitude(), compareit->amplitude() );
        SAME_PARAM_VALUES( distit->bandwidth(), compareit->bandwidth() );
        SAME_PARAM_VALUES( distit->phase(), compareit->phase() );
        
        ++compareit;
        ++distit;
    }
}
Beispiel #12
0
// ----------- test_distill_nonoverlapping -----------
//
static void test_distill_nonoverlapping( void )
{
    std::cout << "\t--- testing distill on "
                 "non-overlapping Partials... ---\n\n";
                 
    //  Fabricate three non-overlapping Partials, give
    //  them all the same label, and distill them. Also
    //  add a fourth Partial with a different label, verify
    //  that it remains unaffacted.
    Partial p1;
    p1.insert( 0, Breakpoint( 100, 0.1, 0, 0 ) );
    p1.insert( .1, Breakpoint( 110, 0.2, 0.2, .1 ) );
    p1.setLabel( 123 );
    
    Partial p2;
    p2.insert( 0.2, Breakpoint( 200, 0.1, 0, 0 ) );
    p2.insert( 0.3, Breakpoint( 210, 0.2, 0.2, .1 ) );
    p2.setLabel( 123 );
    
    Partial p3;
    p3.insert( 0.4, Breakpoint( 300, 0.1, 0, 0 ) );
    p3.insert( 0.5, Breakpoint( 310, 0.2, 0.2, .1 ) );
    p3.setLabel( 123 );
    
    Partial p4;
    p4.insert( 0, Breakpoint( 400, 0.1, 0, 0 ) );
    p4.insert( 0.5, Breakpoint( 410, 0.2, 0.2, .1 ) );
    p4.setLabel( 4 );
    
    PartialList l;
    l.push_back( p1 );
    l.push_back( p3 );
    l.push_back( p4 );
    l.push_back( p2 );
    
    const double fade = .01; // 10 ms
    Distiller d( fade );
    d.distill( l );
    
    //  Fabricate the Partial that the distillation should 
    //  produce.
    Partial compare;
    compare.insert( 0, Breakpoint( 100, 0.1, 0, 0 ) );
    compare.insert( 0.1, Breakpoint( 110, 0.2, 0.2, .1 ) );
    double t = 0.1 + fade;
    compare.insert( t, Breakpoint( p1.frequencyAt(t), 0, 
                                   0, // p1.bandwidthAt(t), 
                                   p1.phaseAt(t) ) );
    t = 0.2 - fade;
    compare.insert( t, Breakpoint( p2.frequencyAt(t), 0, 
                                   0, // p2.bandwidthAt(t), 
                                   p2.phaseAt(t) ) );
    compare.insert( 0.2, Breakpoint( 200, 0.1, 0, 0 ) );
    compare.insert( 0.3, Breakpoint( 210, 0.2, 0.2, .1 ) );
    t = 0.3 + fade;
    compare.insert( t, Breakpoint( p2.frequencyAt(t), 0, 
                                   0, // p2.bandwidthAt(t), 
                                   p2.phaseAt(t) ) );
    t = 0.4 - fade;
    compare.insert( t, Breakpoint( p3.frequencyAt(t), 0, 
                                   0, // p3.bandwidthAt(t), 
                                   p3.phaseAt(t) ) );    
    compare.insert( 0.4, Breakpoint( 300, 0.1, 0, 0 ) );
    compare.insert( 0.5, Breakpoint( 310, 0.2, 0.2, .1 ) );
    compare.setLabel( 123 );
    
    //  compare Partials (distilled Partials
    //  should be in label order):
    TEST( l.size() == 2 );
    PartialList::iterator it = l.begin();
    TEST( it->label() == p4.label() );
    TEST( it->numBreakpoints() == p4.numBreakpoints() );
    ++it;
    
    for ( Partial::iterator distit = it->begin(); distit != it->end(); ++distit )
    {
        cout << distit.time() << " " << distit.breakpoint().frequency() << endl;
    }
    
    TEST( it->numBreakpoints() == compare.numBreakpoints() );
    
    Partial::iterator distit = it->begin();
    Partial::iterator compareit = compare.begin();
    while ( compareit != compare.end() )
    {
        SAME_PARAM_VALUES( distit.time(), compareit.time() );
        SAME_PARAM_VALUES( distit->frequency(), compareit->frequency() );
        SAME_PARAM_VALUES( distit->amplitude(), compareit->amplitude() );
        SAME_PARAM_VALUES( distit->bandwidth(), compareit->bandwidth() );
        SAME_PARAM_VALUES( distit->phase(), compareit->phase() );
        
        ++compareit;
        ++distit;
    }
}
Beispiel #13
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 );
    
}
Beispiel #14
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;
}