Skip to content

Commit

Permalink
Merge pull request #2 from mtmccrea/fix-init-more-docs
Browse files Browse the repository at this point in the history
Update for accurate initialization and state management.
  • Loading branch information
madskjeldgaard authored Jul 28, 2023
2 parents d75f589 + 972bd51 commit 6c316fe
Show file tree
Hide file tree
Showing 5 changed files with 275 additions and 197 deletions.
77 changes: 52 additions & 25 deletions plugin-code/plugins/RampUpGen/RampUpGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,71 +10,98 @@ namespace RampUpGen {

RampUpGen::RampUpGen() {

// Initialize the state of member variables that depend on input aruguments
m_frequency = in0(Frequency);

// Set the UGen's calculation function depending on the rate of the first
// argument (frequency)
if (inRate(Frequency) == calc_FullRate) {
// argument (frequency).
// Call that function for one calculation cycle, which generates an
// initialization sample for downstream UGens
if (isAudioRateIn(Frequency)) {
mCalcFunc = make_calc_function<RampUpGen, &RampUpGen::next_a>();

// Calculate first value
next_a(1);
} else {
mCalcFunc = make_calc_function<RampUpGen, &RampUpGen::next_k>();

// Calculate first value
next_k(1);
};

// Reset the initial state of member variables.
m_phase = 0.0;
// This is so the initialization sample calulated above by 'next' matches
// the first output sample when the synth is run and 'next' is called again.
// m_frequency is not reset because it's initial value is unaffected by
// the next_k(1)
}

// The guts of our ramp generator
inline float RampUpGen::progressPhasor(float frequency) {
// The ramp generator
inline float RampUpGen::progressPhasor(double phase, float frequency) {
// Calculate increment value.
// Double precision is important in phase values
// because division errors are accumulated as well
double increment = static_cast<double>(frequency) / sampleRate();

m_phase += increment;

phase += increment;
const double minvalue = 0.0;
const double maxvalue = 1.0;

// Wrap the phasor if it goes beyond the boundaries
if (m_phase > maxvalue) {
m_phase = minvalue + (m_phase - maxvalue);
} else if (m_phase < minvalue) {
// in case phase is below minimum value
m_phase = maxvalue - std::fabs(m_phase);
// Wrap the phasor if it goes above maxvalue or below minvalue
if (phase > maxvalue) {
phase = minvalue + (phase - maxvalue);
} else if (phase < minvalue) {
phase = maxvalue - std::fabs(phase);
}

return m_phase;
return phase;
}

// Calculation function for audio rate frequency input
void RampUpGen::next_a(int nSamples) {
const float *frequency = in(Frequency);
float *outbuf = out(0);

double current_phase = m_phase;

for (int i = 0; i < nSamples; ++i) {
outbuf[i] = progressPhasor(frequency[i]);
// Be sure to read from UGen's inputs BEFORE writing to outputs,
// they share a buffer by default!
const float freq = frequency[i];

// Write out the phase
outbuf[i] = current_phase;

// Advance the phase
current_phase = progressPhasor(current_phase, freq);
}

// Store final value of phase to be used next time the
// calculation function runs
m_phase = current_phase;
}

// Calculation function for control rate frequency input
void RampUpGen::next_k(int nSamples) {
const float frequencyParam = in(Frequency)[0];
SlopeSignal<float> slopedFrequency =
makeSlope(frequencyParam, m_frequency_past);
makeSlope(frequencyParam, m_frequency);
float *outbuf = out(0);
double current_phase = m_phase;

for (int i = 0; i < nSamples; ++i) {
const float freq = slopedFrequency.consume();

outbuf[i] = progressPhasor(freq);
// Write out the phase
outbuf[i] = current_phase;

// Advance the phase
current_phase = progressPhasor(current_phase, freq);
}

// Store final value of frequency slope to be used next time the calculation
// function runs
m_frequency_past = slopedFrequency.value;
// Store final value of frequency and phase to be used next time the
// calculation function is called
m_frequency = slopedFrequency.value;
m_phase = current_phase;
}

} // namespace RampUpGen

PluginLoad(RampUpGenUGens) {
Expand Down
14 changes: 8 additions & 6 deletions plugin-code/plugins/RampUpGen/RampUpGen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ class RampUpGen : public SCUnit {
RampUpGen();

private:
// Calc function
// Calc functions
void next_a(int nSamples);
void next_k(int nSamples);
inline float progressPhasor(float frequency);

// Helper functions
inline float progressPhasor(double phase, float frequency);

// Parameter names corresponding to our argument indices
enum Inputs { Frequency };
// Member variables

// State variables
double m_phase{0.0};

// State variables
float m_frequency_past{0.f};
float m_frequency; // this will be initialized in the constructor
};

} // namespace RampUpGen
6 changes: 3 additions & 3 deletions plugin-code/plugins/RampUpGen/RampUpGen.sc
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ RampUpGen : UGen {

checkInputs {

// Input 0 is frequency
if(inputs.at(0) == \audio, {
"You're not supposed to use audio rate here".error
// If you want to do custom rate checking...
if(this.rate == \control and: { inputs.at(0).rate == \audio }, {
^"An audio-rate frequency argument isn't allowed when RampUpGen runs at control rate."
});

// Checks if inputs are valid UGen inputs
Expand Down
58 changes: 50 additions & 8 deletions plugin-code/plugins/RampUpGen/RampUpGen.schelp
Original file line number Diff line number Diff line change
@@ -1,26 +1,68 @@
class:: RampUpGen
summary:: Ramping up values
related:: TODO
categories:: UGens>TODO
summary:: Cyclically ramping up values
related:: Classes/Saw, Classes/LFSaw
categories:: UGens>Generators


description::

Ramping up values
Generate cyclically ramping values from teletype::0.0:: to teletype::1.0::.
teletype::RampUpGen:: behaves similarly to link::Classes/LFSaw::.


classmethods::

method::ar, kr
method:: ar

argument:: frequency
Frequency of the ramping signal, in Hertz.

argument::TODO
method:: kr

argument::TODO
argument:: frequency
Frequency of the ramping signal, in Hertz. Audio rate is not supported.


examples::

code::
( // Plot the output
s.waitForBoot{
{ RampUpGen.ar(300) }.plot(3 * 1/300) // 3 cycles
}
)

( // Inspect output values by storing in an array
{
RampUpGen.ar(300)
}.loadToFloatArray(
1/300, // duration: one cycle
action:{ |arr| arr.do(_.postln) } // post the output
)
)

( // Test all input rates, plot the output
var freq = 300;
{
RampUpGen.ar([
freq, // initialization-rate (scalar) Frequency
DC.kr(freq), // control rate Frequency
DC.ar(freq) // audio rate Frequency
])
}.plot(3 / freq) // 3 cycles
)

{ RampUpGen.ar(/* TODO */) }.play
( // Test parameter modulation, at kr and ar rates
var freqFrom = 300, freqTo = 2000;
{
RampUpGen.ar([
Line.kr(freqFrom, freqTo, 3/freqFrom),
Line.ar(freqFrom, freqTo, 3/freqFrom)
])
}.plot(3 / freqFrom)
)

( // Try an invalid argument rate
{ RampUpGen.kr(DC.ar(30)) }.plot(3 * 1/30) // 3 cycles
)
::
Loading

0 comments on commit 6c316fe

Please sign in to comment.