// ---------------------------------------------------------------------------
//	resetEnvelopes
// ---------------------------------------------------------------------------
//	Reset the instantaneous envelope parameters 
// 	(frequency, amplitude, bandwidth, and phase).
// 	The sample rate is needed to convert the 
// 	Breakpoint frequency (Hz) to radians per sample.
//
void 
Oscillator::resetEnvelopes( const Breakpoint & bp, double srate )
{
	//	Remember that the oscillator only knows about 
	//	radian frequency! Convert!
	i_frequency = bp.frequency() * TwoPi / srate;
	i_amplitude = bp.amplitude();
	i_bandwidth = bp.bandwidth();
	determ_phase = bp.phase();
	
 	//	clamp bandwidth:
	if ( i_bandwidth > 1. )
	{
		debugger << "clamping bandwidth at 1." << endl;
		i_bandwidth = 1.;
	}
	else if ( i_bandwidth < 0. )
	{ 
		debugger << "clamping bandwidth at 0." << endl;
		i_bandwidth = 0.;
	}

	//	don't alias:
	if ( i_frequency > Pi )
	{ 
		debugger << "fading out aliasing Partial" << endl;
		i_amplitude = 0.;
	}
}
// ---------------------------------------------------------------------------
//  resetEnvelopes
// ---------------------------------------------------------------------------
//  Reset the instantaneous envelope parameters
//  (frequency, amplitude, bandwidth, and phase).
//  The Breakpoint frequency (Hz) is in radians per sample.
void
RealtimeOscillator::restoreEnvelopes( const Breakpoint & bp) noexcept
{
    //  Remember that the oscillator only knows about
    //  radian frequency! Convert!
    m_instfrequency = bp.frequency();
    m_instamplitude = bp.amplitude();
    m_instbandwidth = bp.bandwidth();
    m_determphase = bp.phase();

    //  clamp bandwidth:
    if ( m_instbandwidth > 1. )
    {
        m_instbandwidth = 1.;
    }
    else if ( m_instbandwidth < 0. )
    {
        m_instbandwidth = 0.;
    }
    //  don't alias:
    if ( m_instfrequency > Pi )
    {
        m_instamplitude = 0.;
    }

    //  Reset the fitler state too.
    m_filter.clear();
}
// ---------------------------------------------------------------------------
//      accum_samples
// ---------------------------------------------------------------------------
//      helper
//
static void accum_samples( CSOUND * csound,
                           Oscillator & oscil, Breakpoint & bp,
                           double * bufbegin )
{
  if( bp.amplitude() > 0 || oscil.amplitude() > 0 )
    {
      double radfreq = radianFreq( csound, bp.frequency() );
      double amp = bp.amplitude();
      double bw = bp.bandwidth();

      //        initialize the oscillator if it is changing from zero
      //        to non-zero amplitude in this  control block:
      if ( oscil.amplitude() == 0. )
        {
          //    don't initialize with bogus values, Oscillator
          //    only guards against out-of-range target values
          //    in generateSamples(), parameter mutators are
          //    dangerous:

          if ( radfreq > PI )   //      don't alias
            amp = 0.;

          if ( bw > 1. )        //      clamp bandwidth
            bw = 1.;
          else if ( bw < 0. )
            bw = 0.;

#ifdef DEBUG_LORISGENS
        /*
          std::cerr << "initializing oscillator " << std::endl;
          std::cerr << "parameters: " << bp.frequency() << "  ";
          std::cerr << amp << "  " << bw << std::endl;
        */
#endif

          //    initialize frequency, amplitude, and bandwidth to
          //    their target values:
          /*
            oscil.setRadianFreq( radfreq );
            oscil.setAmplitude( amp );
            oscil.setBandwidth( bw );
          */
          oscil.resetEnvelopes( bp, (double) csound->esr );

          //    roll back the phase:
          oscil.resetPhase( bp.phase() - ( radfreq * (double) csound->ksmps ) );
        }

      //        accumulate samples into buffer:
      // oscil.generateSamples( bufbegin, bufbegin + nsamps, radfreq, amp, bw );
      oscil.oscillate( bufbegin, bufbegin + csound->ksmps,
                       bp, (double) csound->esr );
    }
}
// ---------------------------------------------------------------------------
//	matchPhaseFwd
//
//!	Compute the target frequency that will affect the
//!	predicted (by the Breakpoint phases) amount of
//!	sinusoidal phase travel between two breakpoints, 
//!	and assign that frequency to the target Breakpoint.
//!	After computing the new frequency, update the phase of
//!	the later Breakpoint.
//!
//! If the earlier Breakpoint is null and the later one
//! is non-null, then update the phase of the earlier
//! Breakpoint, and do not modify its frequency or the 
//! later Breakpoint.
//!
//! The most common kinds of errors are local (or burst) errors in 
//! frequency and phase. These errors are best corrected by correcting
//! less than half the detected error at any time. Correcting more
//! than that will produce frequency oscillations for the remainder of
//! the Partial, in the case of a single bad frequency (as is common
//! at the onset of a tone). Any damping factor less then one will 
//! converge eventually, .5 or less will converge without oscillating.
//! Use the damping argument to control the damping of the correction.
//!	Specify 1 for no damping.
//!
//! \pre		The two Breakpoints are assumed to be consecutive in
//!				a Partial.
//! \param		bp0	The earlier Breakpoint.
//! \param		bp1	The later Breakpoint.
//! \param		dt The time (in seconds) between bp0 and bp1.
//! \param		damping The fraction of the amount of phase error that will
//!				be corrected (.5 or less will prevent frequency oscilation 
//!				due to burst errors in phase). 
//! \param		maxFixPct The maximum amount of frequency adjustment
//!				that can be made to the frequency of bp1, expressed
//!				as a precentage of the unmodified frequency of bp1.
//!				If the necessary amount of frequency adjustment exceeds
//!				this amount, then the phase will not be matched, 
//!				but will be updated as well to be consistent with
//!				the frequencies. (default is 0.2%)
//
void matchPhaseFwd( Breakpoint & bp0, Breakpoint & bp1,
				    double dt, double damping, double maxFixPct )
{
	double travel = phaseTravel( bp0, bp1, dt );
    
    if ( ! BreakpointUtils::isNonNull( bp1 ) )
    {
        // if bp1 is null, just compute a new phase,
        // no need to match it.
        bp1.setPhase( wrapPi( bp0.phase() + travel ) );
    }
    else if ( ! BreakpointUtils::isNonNull( bp0 ) )
    {
        // if bp0 is null, and bp1 is not, then bp0
        // should be a phase reset Breakpoint during
        // rendering, so compute a new phase for
        // bp0 that achieves bp1's phase.
        bp0.setPhase( wrapPi( bp1.phase() - travel ) ) ;
    } 
    else
    {
        // invariant:
        // neither bp0 nor bp1 is null
        //
        // modify frequecies to match phases as nearly as possible
        double err = wrapPi( bp1.phase() - ( bp0.phase() + travel ) );
        
        //  The most common kinds of errors are local (or burst) errors in 
        //  frequency and phase. These errors are best corrected by correcting
        //  less than half the detected error at any time. Correcting more
        //  than that will produce frequency oscillations for the remainder of
        //  the Partial, in the case of a single bad frequency (as is common
        //  at the onset of a tone). Any damping factor less then one will 
        //  converge eventually, .5 or less will converge without oscillating.
        //  #define DAMPING .5
        travel += damping * err;
        
        double f0 = bp0.frequency();
        double ftgt = ( travel / ( Pi * dt ) ) - f0;
        
        #ifdef Loris_Debug
        debugger << "matchPhaseFwd: correcting " << bp1.frequency() << " to " << ftgt 
                 << " (phase " << wrapPi( bp1.phase() ) << "), ";
        #endif
        
        //	If the target is not a null breakpoint, may need to 
        //	clamp the amount of frequency modification.
        //
        //  Actually, should probably always clamp the amount
        //  of modulation, should never have arbitrarily large
        //  frequency adjustments. 
        //
        //  Really, should never call this function if bp1
        //  is a null Breakpoint, because we don't care about 
        //  those phases in Loris. 
        if ( true ) //  bp1.amplitude() != 0. )
        {	
            if ( ftgt > bp1.frequency() * ( 1 + (maxFixPct*.01) ) )
            {
                ftgt = bp1.frequency() * ( 1 + (maxFixPct*.01) );
            }
            else if ( ftgt < bp1.frequency() * ( 1 - (maxFixPct*.01) ) )
            {
                ftgt = bp1.frequency() * ( 1 - (maxFixPct*.01) );
            }
        }
        bp1.setFrequency( ftgt );
        
        //	Recompute the phase according to the new frequency.
        double phi = wrapPi( bp0.phase() + phaseTravel( bp0, bp1, dt ) );
        bp1.setPhase( phi );

        #ifdef Loris_Debug
        debugger << "achieved " << ftgt << " (phase " << phi << ")" << endl;
        #endif
    }
}
Exemple #5
0
// ---------------------------------------------------------------------------
//  synthesize
// ---------------------------------------------------------------------------
//! Synthesize a bandwidth-enhanced sinusoidal Partial.
//!
//! \param  buffer  The samples buffer.
//! \param  samples Number of samples to be synthesized.
//! \param  p       The Partial to synthesize.
//! \return Nothing.
//! \pre    The buffer has to have capacity to contain all samples.
//! \post   This RealTimeSynthesizer's sample buffer (vector) contain synthesised
//!         partials and storeed inner state of synthesiser.
//!
void RealTimeSynthesizer::synthesize( PartialStruct &p, float * buffer, const int samples) noexcept
{
    if (p.state.lastBreakpointIdx == PartialStruct::NoBreakpointProcessed)
        m_osc.resetEnvelopes( p.state.envelope, m_srateHz );
    else
        m_osc.restoreEnvelopes( p.state.envelope );
        
    int sampleCounter = 0;
	int sampleDiff = 0;
    int i;
    for (i = p.state.lastBreakpointIdx + 1;  i < p.numBreakpoints; ++i )
    {
        index_type tgtSamp = index_type( (p.breakpoints[i].first * m_srateHz) + 0.5 );   //  cheap rounding
        
        sampleCounter += sampleDiff = tgtSamp - p.state.currentSamp;
        
        if (sampleCounter > samples)// if this breakpoint is longer...
        {
            // cropp it...
            sampleDiff -= (sampleCounter - samples); // substract the overflowing number of samples to get max possible sample diff
            sampleCounter = samples; // we can process max "samples" count
        }
        
        Breakpoint *bp = &(p.breakpoints[i].second);
        //  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. && p.state.breakpointFinished )
        if ( i == PartialStruct::NoBreakpointProcessed + 1 && p.state.breakpointFinished )
        {
            //  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 * ( p.state.prevFrequency + m_osc.frequencyScaling() * bp->frequency() ) * ( tgtSamp - p.state.currentSamp ) * OneOverSrate;
            
            // If we transposed/pitch-shifted the sound using sample rate change, the transpose octave above would
            // mean create new signal with every second sample missing, so the partial would start earlier. If we
            // would like to have partial transposed but in the same time t0 as original,  what the new phase will be?
            // It will be the original phase with the phase change during the delta of time. What delta of time is it?
            // The start time in sample-removing pitch shifted signal would be half of time if we transpose octave up so the
            // delta time is t0 - t0/transposeFactor. So the new phase goes like this (here we do not have time t0 so we get
            // it from partial[iSamp]/float(fs)).
            double phaseFixed = (bp->phase() + 2*Pi*p.avgFrequency*p.state.currentSamp*OneOverSrate*(m_osc.frequencyScaling()-1));

            m_osc.setPhase( phaseFixed - dphase );
        }
        
        int samplesToBp = tgtSamp - p.state.currentSamp;
        m_osc.oscillate( buffer, buffer + sampleDiff, *bp, m_srateHz, samplesToBp );

		buffer += sampleDiff;// move buffer pointer
        
		p.state.currentSamp += sampleDiff;
        p.state.breakpointFinished = tgtSamp == p.state.currentSamp;

        if (p.state.breakpointFinished)
        {
            //  remember the frequency, may need it to reset the
            //  phase if a Null Breakpoint is encountered:
//            m_osc.resetEnvelopes(*bp, m_srateHz);
//            p.state.prevFrequency = bp->frequency();
            
            p.state.prevFrequency = m_osc.envelopes().frequency();
//                m_osc.setPhase(bp->phase());
        }
        
        if (sampleCounter == samples)
        {
            i--; // there is still something to be done in this break point
            break;
        }
	}
    
    p.state.envelope = m_osc.envelopes();
    p.state.lastBreakpointIdx = i;
}