-
Notifications
You must be signed in to change notification settings - Fork 72
Signal builders
Signal builder is a special object whose purpose is generating signals of a certain waveform. This object has state and can generate sample after sample (online generation) as well as entire signals at once (offline generation), set constructing parameters and reset its state.
All signal builders inherit from abstract class SignalBuilder
providing two main methods for signal generation:
builder.NextSample()
builder.Build()
The former method generates new sample at each call, so the signal builder acts as a real-time generator of samples. The latter method builds the entire signal of some length. In both cases sampling rate must be specified (otherwise it'll be set to 1 by default). Other parameters depend on a particular builder (waveform).
Builders offer fluent syntax for their construction and usage. Let's take a look at the example code:
DiscreteSignal sinusoid =
new SineBuilder()
.SetParameter("frequency", 500.0/*Hz*/)
.SetParameter("phase", Math.PI / 6)
.OfLength(1000)
.SampledAt(44100/*Hz*/)
.Build();
DiscreteSignal noise =
new PinkNoiseBuilder()
.SetParameter("min", -2.5)
.SetParameter("max", 2.5)
.OfLength(800)
.SampledAt(44100/*Hz*/)
.DelayedBy(200/*samples*/)
.Build();
DiscreteSignal noisy =
new SineBuilder()
.SetParameter("min", -10.0)
.SetParameter("max", 10.0)
.SetParameter("freq", 1200.0/*Hz*/)
.OfLength(1000)
.SampledAt(44100/*Hz*/)
.SuperimposedWith(noise)
.Build();
The following methods were designed only for offline generation and will have no effect in real-time processing:
builder.DelayedBy()
builder.RepeatedTimes()
builder.SuperimposedWith()
Builder methods can be called in any order.
One prominent use-case of online generation is working with LFOs (low frequency oscillators):
SignalBuilder lfo =
new TriangleWaveBuilder()
.SetParameter("min", 100)
.SetParameter("max", 1500)
.SetParameter("frequency", 2.0/*Hz*/)
.SampledAt(16000/*Hz*/);
while (...)
{
var sample = lfo.NextSample();
//...
}
lfo.Reset();
If the signal builder is going to be reused later and it will have to start generating samples "from scratch", then Reset()
method needs to be called. Otherwise it'll continue its work from the position where it was stopped earlier.
Builders can have various parameters. Parameters are addressed by some string keyword like "freq" or "min".
GetParametersInfo()
method returns the array of keywords that will be recognized by the builder.
At the moment, there are 14 ready-to-use signal builders in NWaves.
Periodic signals:
Builder | Parameter | Parameter | Parameter | Parameter |
---|---|---|---|---|
SineBuilder | "min", "low", "lo" | "max", "high", "hi" | "frequency", "freq" | "phase", "phi" |
CosineBuilder | "min", "low", "lo" | "max", "high", "hi" | "frequency", "freq" | "phase", "phi" |
SawtoothBuilder | "min", "low", "lo" | "max", "high", "hi" | "frequency", "freq" | |
TriangleWaveBuilder | "min", "low", "lo" | "max", "high", "hi" | "frequency", "freq" | |
SquareWaveBuilder | "min", "low", "lo" | "max", "high", "hi" | "frequency", "freq" | |
PulseWaveBuilder | "min", "low", "lo" | "max", "high", "hi" | "period", "t" | "pulse", "width" |
Noise signals:
Builder | Parameter | Parameter | Parameter |
---|---|---|---|
WhiteNoiseBuilder | "min", "low", "lo" | "max", "high", "hi" | |
PinkNoiseBuilder | "min", "low", "lo" | "max", "high", "hi" | |
RedNoiseBuilder | "min", "low", "lo" | "max", "high", "hi" | |
PerlinNoiseBuilder | "min", "low", "lo" | "max", "high", "hi" | "scale", "octave" |
AwgnBuilder | "mean", "mu" | "sigma", "stdev" |
Misc:
Builder | Parameter | Parameter | Parameter | Parameter |
---|---|---|---|---|
WaveTableBuilder | ||||
AdsrBuilder | "attackAmp", "amp" | "attack", "a" | "decay", "d" | "sustain", "s" |
RampBuilder | "slope", "k" | "intercept", "b" | ||
SincBuilder | "min", "low", "lo" | "max", "high", "hi" | "frequency", "freq" | |
ChirpBuilder | "frequency", "freq" | "min", "low", "lo" | "f0", "freq0", "start" | "f1", "freq1", "end" |
PadSynthBuilder | "frequency", "freq" | "fftsize", "size" | "bandwidth", "bw" | "scale", "bwscale" |
KarplusStrongBuilder | "frequency", "freq" | "stretch", "s" | "feedback", "a" | |
KarplusStrongDrumBuilder | "frequency", "freq" | "stretch", "s" | "feedback", "a" | "probability", "prob" |
For example, the cell "min", "low", "lo" means that the corresponding signal builder will recognize all these three keywords as the minimum amplitude value for a signal to be generated.
Note. WaveTableBuilder
doesn't have parameters, and the array of samples (i.e. wavetable) must be passed to constructor. The samples will be looped.
Note. AdsrBuilder
generates exponential ADSR envelope. Optional parameters represent the exponential slope at each corresponding stage. Defaults are 0.2 (and they're OK). There is also parameter attackAmp
(or amp
) that represents the gain of attack part of ADSR curve (by default, it's 1.5).
Note. PadSynthBuilder
also provides method SetAmplitudes(float[] amplitudes)
for specifying harmonics amplitudes.
FadeInOutBuilder
is the special decorator for other signal builders that adds fade-in and -out behaviour.
sound = new FadeInOutBuilder(
new PadSynthBuilder()
.SetParameter("fftsize", _config.FftSize)
.SetParameter("freq", freq)
.SetAmplitudes(_config.Amplitudes)
.OfLength(_sampleRate * _config.Seconds)
.SampledAt(_sampleRate))
.In(0.1) // 100 msec
.Out(0.5); // 500 msec
During offline signal generation fading out will start automatically in the end of the signal. In online scenarios the method FadeOut()
can be called to start fading-out. Properties FadeStarted
and FadeFinished
indicate the state of fading-out.
- Create a class inherited from
SignalBuilder
- Override
float NextSample()
method. - If you need parameters, in constructor fill the dictionary of setters
ParameterSetters
, like this:
ParameterSetters = new Dictionary<string, Action<double>>
{
{ "low, lo, min", param => _low = param },
{ "high, hi, max", param => _high = param }
};
Most likely, your class will maintain some state. Do it using private members of the class.
Example:
/// <summary>
/// Class for generator of random samples clamped in range [min, max]
/// </summary>
public class ClampBuilder : SignalBuilder
{
/// <summary>
/// Minimum amplitude level
/// </summary>
private double _min;
/// <summary>
/// Maximum amplitude level
/// </summary>
private double _max;
/// <summary>
/// Constructor
/// </summary>
public ClampBuilder()
{
ParameterSetters = new Dictionary<string, Action<double>>
{
{ "low, lo, min", param => _min = param },
{ "high, hi, max", param => _max = param }
};
_min = -0.5;
_max = 0.5;
}
/// <summary>
/// Method generates random samples clamped in range [min, max]
/// </summary>
/// <returns>Sample</returns>
public override float NextSample()
{
var val = _rand.NextDouble();
if (val >= _max) return (float)_max;
if (val <= _min) return (float)_min;
return (float)val;
}
private Random _rand = new Random();
}
Usage:
DiscreteSignal clamped =
new ClampBuilder()
.SetParameter("min", -0.8)
.SetParameter("max", 0.8)
.OfLength(100)
.SampledAt(8000/*Hz*/)
.Build();