// --------------------------------------------------------------------------- // 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 ); } }
// --------------------------------------------------------------------------- // 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(); }
// --------------------------------------------------------------------------- // generateSamples // --------------------------------------------------------------------------- // Accumulate bandwidth-enhanced sinusoidal samples modulating the // oscillator state from its current values of radian frequency, // amplitude, and bandwidth to the specified target values, into // the specified half-open range of doubles. // // The caller must ensure that the range is valid. Target parameters // are bounds-checked. // void Oscillator::oscillate( double * begin, double * end, const Breakpoint & bp, double srate ) { double targetFreq = bp.frequency() * TwoPi / srate, targetAmp = bp.amplitude(), targetBw = bp.bandwidth(); // clamp bandwidth: if ( targetBw > 1. ) { debugger << "clamping bandwidth at 1." << endl; targetBw = 1.; } else if ( targetBw < 0. ) { debugger << "clamping bandwidth at 0." << endl; targetBw = 0.; } // don't alias: if ( targetFreq > Pi ) // radian Nyquist rate { debugger << "fading out Partial above Nyquist rate" << endl; targetAmp = 0.; } // compute trajectories: const double dTime = 1. / (end - begin); const double dFreq = (targetFreq - i_frequency) * dTime; const double dAmp = (targetAmp - i_amplitude) * dTime; const double dBw = (targetBw - i_bandwidth) * dTime; // could use temporary local variables for speed... nah! // Cannot possibly be worth it when I am computing square roots // and cosines! double am; for ( double * putItHere = begin; putItHere != end; ++putItHere ) { // use math functions in namespace std: using namespace std; // compute amplitude modulation due to bandwidth: // // This will give the right amplitude modulation when scaled // by the Partial amplitude: // // carrier amp: sqrt( 1. - bandwidth ) * amp // modulation index: sqrt( 2. * bandwidth ) * amp // am = sqrt( 1. - i_bandwidth ) + ( bwModulator() * sqrt( 2. * i_bandwidth ) ); // compute a sample and add it into the buffer: *putItHere += am * i_amplitude * cos( determ_phase ); // update the instantaneous oscillator state: determ_phase += i_frequency; // frequency is radians per sample i_frequency += dFreq; i_amplitude += dAmp; i_bandwidth += dBw; if (i_bandwidth < 0.) i_bandwidth = 0.; } // end of sample computation loop // wrap phase to prevent eventual loss of precision at // high oscillation frequencies: // (Doesn't really matter much exactly how we wrap it, // as long as it brings the phase nearer to zero.) determ_phase = m2pi( determ_phase ); // set the state variables to their target values, // just in case they didn't arrive exactly (overshooting // amplitude or, especially, bandwidth, could be bad, and // it does happen): i_frequency = targetFreq; i_amplitude = targetAmp; i_bandwidth = targetBw; }
// --------------------------------------------------------------------------- // oscillate // --------------------------------------------------------------------------- // Accumulate bandwidth-enhanced sinusoidal samples modulating the // oscillator state from its current values of radian frequency, // amplitude, and bandwidth to the specified target values, into // the specified half-open range of floats. SSE2 instructions are used. // // The caller must ensure that the range is valid. Target parameters // are bounds-checked. // void RealtimeOscillator::oscillate( float * begin, float * end, const Breakpoint & bp, double srate, int dSample) noexcept { double targetFreq = m_frequencyScaling * bp.frequency() * TwoPi / srate; // radians per sample double targetAmp = bp.amplitude(); double targetBw = bp.bandwidth(); // clamp bandwidth: if ( targetBw > 1. ) { targetBw = 1.; } else if ( targetBw < 0. ) { targetBw = 0.; } // don't alias: if ( targetFreq > Pi ) // radian Nyquist rate { targetAmp = 0.; } // compute trajectories: const double dTime = 1. / dSample; //(end - begin); const double dFreqOver2 = 0.5 * (targetFreq - m_instfrequency) * dTime; // split frequency update in two steps, update phase using average // frequency, after adding only half the frequency step const double dAmp = (targetAmp - m_instamplitude) * dTime; // const double dBw = (targetBw - m_instbandwidth) * dTime; // Use temporary local variables for speed. // Probably not worth it when I am computing square roots // and cosines... double ph = m_determphase; // double f = m_instfrequency; // double a = m_instamplitude; // double bw = m_instbandwidth; // Ignore bandwidth // Also use a more efficient sample loop when the bandwidth is zero. // if (false 0 < bw || 0 < dBw ) // { // double am, nz; // for ( double * putItHere = begin; putItHere != end; ++putItHere ) // { // // use math functions in namespace std: // using namespace std; // // // compute amplitude modulation due to bandwidth: // // // // This will give the right amplitude modulation when scaled // // by the Partial amplitude: // // // // carrier amp: sqrt( 1. - bandwidth ) * amp // // modulation index: sqrt( 2. * bandwidth ) * amp // // // nz = m_filter.apply( m_modulator.sample() ); // am = sqrt( 1. - bw ) + ( nz * sqrt( 2. * bw ) ); // // // compute a sample and add it into the buffer: // *putItHere += am * a * cos( ph ); // // // update the instantaneous oscillator state: // f += dFreqOver2; // ph += f; // frequency is radians per sample // f += dFreqOver2; // a += dAmp; // bw += dBw; // if (bw < 0.) // { // bw = 0.; // } // } // end of sample computation loop // } // else { // Vectorized loop double a4[4] = { m_instamplitude }; double f4[4] = { m_instfrequency }; V4SF ph4 = { (float) m_determphase }; for (int i = 1; i < 4; i++) { f4[i] = f4[i - 1] + dFreqOver2; ph4.f[i] = ph4.f[i - 1] + f4[i]; f4[i] = f4[i] + dFreqOver2; a4[i] = a4[i - 1] + dAmp; } V4SF cosVal; float * putItHere = begin; for ( ; putItHere + 4 < end; putItHere += 4 ) { cosVal.v = cos_ps(ph4.v); for (int i = 0; i < 4; i++) { putItHere[i] += (a4[i] == 0 ? 0 : a4[i] * cosVal.f[i]); } f4[0] = f4[3] + dFreqOver2; ph4.f[0] = ph4.f[3] + f4[0]; f4[0] = f4[0] + dFreqOver2; a4[0] = a4[3] + dAmp; for (int i = 1; i < 4; i++) { f4[i] = f4[i - 1] + dFreqOver2; ph4.f[i] = ph4.f[i - 1] + f4[i]; f4[i] = f4[i] + dFreqOver2; a4[i] = a4[i - 1] + dAmp; } } // end of sample computation loop for ( ; putItHere != end; ++putItHere ) { // use math functions in namespace std: using namespace std; // no modulation when there is no bandwidth // compute a sample and add it into the buffer: *putItHere += (a4[0] == 0 ? 0 : a4[0] * cos(ph4.f[0])); // update the instantaneous oscillator state: f4[0] += dFreqOver2; ph4.f[0] += f4[0]; // frequency is radians per sample f4[0] += dFreqOver2; a4[0] += dAmp; } // end of ph = ph4.f[0]; targetAmp = a4[0]; targetFreq = f4[0]; } // wrap phase to prevent eventual loss of precision at // high oscillation frequencies: // (Doesn't really matter much exactly how we wrap it, // as long as it brings the phase nearer to zero.) m_determphase = m2pi( ph ); // set the state variables to their target values, // just in case they didn't arrive exactly (overshooting // amplitude or, especially, bandwidth, could be bad, and // it does happen): m_instfrequency = targetFreq; m_instamplitude = targetAmp; m_instbandwidth = targetBw; }