Skip to content

Signal builders

Tim edited this page Feb 18, 2019 · 5 revisions

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
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"

How to write your own signal builder

  1. Create a class inherited from SignalBuilder
  2. Override float NextSample() method.
  3. 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 _max;
            if (val <= _min) return _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();
Clone this wiki locally