@thi.ng/dsp
    TypeScript icon, indicating that this package has built-in type declarations

    4.2.1 • Public • Published

    dsp

    npm version npm downloads Twitter Follow

    This project is part of the @thi.ng/umbrella monorepo.

    About

    Composable signal generators, oscillators, filters, FFT, spectrum, windowing & related DSP utils.

    Partially ported from other thi.ng projects (e.g. thi.ng/synstack, thi.ng/vexed-generation, toxiclibs).

    Status

    STABLE - used in production

    Search or submit any issues for this package

    Even though this library is now at v2.0.0 and still retains most of the features from earlier versions, all recently added features (IGen's, IProc's, composition ops etc.) should be considered "beta" and are likely to undergo further (hopefully not too drastic) changes in the near future. Also, pending outcomes of ongoing experiments, some aspects might be ported to WASM.

    Support packages

    Related packages

    Installation

    yarn add @thi.ng/dsp

    ES module import:

    <script type="module" src="https://cdn.skypack.dev/@thi.ng/dsp"></script>

    Skypack documentation

    For Node.js REPL:

    # with flag only for < v16
    node --experimental-repl-await
    
    > const dsp = await import("@thi.ng/dsp");
    

    Package sizes (gzipped, pre-treeshake): ESM: 7.56 KB

    Dependencies

    Usage examples

    Several demos in this repo's /examples directory are using this package.

    A selection:

    Screenshot Description Live demo Source
    Interactive inverse FFT toy synth Demo Source
    Polygon to cubic curve conversion & visualization Demo Source
    WebGL cube maps with async texture loading Demo Source
    WebGL screenspace ambient occlusion Demo Source

    API

    Generated API docs

    IGen

    The following unit generators are infinite data sources based on the IGen interface with most being resettable too. The interface is similar to ES6 iterators in that the next value can be obtained by calling .next(), however since IGens are always infinite, there's no need to wrap the result value as is done with ES6 iterables. Furthermore, all gens defined in this package do implement Symbol.iterator and so can actually be used as standard iterables as well.

    IGen also implements the IDeref interface to obtain the gen's current (last generated) value.

    // create exponential curve from 0 - 10 over 5 steps
    const c = curve(0, 10, 5);
    
    // get next value
    c.next()
    // 0
    c.next()
    // 6.087111442696312
    c.next()
    // 8.505616378877338
    c.next()
    // 9.46652635750935
    c.next()
    // 9.848310977098592
    c.next()
    // 9.999999999999998
    
    // get current value
    c.deref()
    // 9.999999999999998
    
    // reset gen
    c.reset()
    
    // produce an array (can also write into existing buffer)
    c.take(6)
    // [
    //   0,
    //   6.087111442696312,
    //   8.505616378877338,
    //   9.46652635750935,
    //   9.848310977098592,
    //   9.999999999999998
    // ]
    
    // use as ES6 iterable, here w/ transducers
    import { take } from "@thi.ng/transducers";
    
    [...take(6, c.reset())]
    // [
    //   0,
    //   6.087111442696312,
    //   8.505616378877338,
    //   9.46652635750935,
    //   9.848310977098592,
    //   9.999999999999998
    // ]
    • add - adder
    • adsr - timebased ADSR / AD envelope generator
    • alt - alternating values
    • constant - constant value
    • cosine - trig-free cosine osc
    • curve - timebased exponential gain/decay (factory for madd)
    • impulse - impulse gen
    • impulseTrain - timebased cyclic impulse
    • line - timebased line gen (factory for add)
    • madd - multiply-adder
    • mul - multiplier (exponential gain/decay)
    • pinkNoise - configurable pink noise (1/f power spectrum)
    • reciprocal - fractional sequence (1, 1/2, 1/3, 1/4 etc.)
    • sincos - trig-free sin/cos LFO
    • sweep - freq sweep gen w/ phase accumulation for oscillators
    • whiteNoise - white noise

    Higher order generators

    • mapG - IGen composition / transformation (1-4 inputs)
    • addG - higher-order adder
    • product - product of input gens
    • sum - sum of input gens

    Oscillators

    IGen wrappers
    • osc - arbitrary function oscillator w/ modulation support
    • modOsc - FM / FMAM oscillator builder
    const FS = 44100;
    
    // simple 100Hz sine oscillator
    const o = osc(sin, 100 / FS, 0.5);
    
    // get next sample
    o.next();
    ...
    
    // frequency & amplitude modulated saw osc
    const fmam = modOsc(
        // carrier waveform
        saw,
        // carrier freq
        1000 / FS,
        // fmod
        osc(saw, 5000 / FS, 0.3),
        // amod
        osc(saw, 500 / FS)
    );
    
    // compute 1sec of signal
    fmam.take(FS)

    Diagram of the FM/AM osc with some low pass filters applied:

    FM/AM waveform

    Stateless oscillator functions

    IProc

    The second fundamental interface in this package, similar to IGen and used to implement processors & transformers of input values (e.g those generated by the various IGens available). IProc implementations have a .next(x) method, where x is the next input to be processed.

    The package also provides several approaches to compose multi-step processing pipelines (see section further below). Furthermore, all implementations in this package implement the @thi.ng/transducers IXform interface and so can be directly used in transducer pipelines too.

    import { comp, push, take, transduce } from "@thi.ng/transducers";
    
    const FS = 48000;      // sample rate
    const F1 = 1 / FS;     // start freq
    const F2 = 10000 / FS; // end freq
    
    // generate oscillator sweep with some effects applied
    const sig = new Float32Array(
        transduce(
            comp(
                // consume 8 secs worth of samples
                take(8 * FS),
                // lowpass filter (state variable filter)
                svfLP(F2),
                // soft clip
                waveShaper(4),
                // 0.5sec delay w/ 60% feedback
                feedbackDelay(0.5 * FS, 0.6)
            ),
            // reducer: collect as array
            push(),
            // oscillator (consumed as ES6 iterable)
            osc(
                // osc function (use only 3 harmonics)
                sawAdditive(3),
                // freq sweep F1 -> F2 over 6 sec
                sweep(F1, F2, 6 * FS),
                // envelope (using attack & decay phase only)
                adsr({ a: 0.05 * FS, d: 5.95 * FS, s: 0 })
            )
        )
    );
    
    fs.writeFileSync("sig.raw", Buffer.from(sig.buffer));

    The raw audio file can then be converted to WAV via ffmpeg:

    ffmpeg -f f32le -ar 48k -ac 1 -i sig.raw sig.wav -y

    Filters

    The following diagrams show various combinations of oscillator signals and their filtered responses (with different cutoff/center frequencies).

    All diagrams were generated with this script.

    The following filter types / functions are available:

    1-pole
    • onepoleLP - low pass, 6dB/oct falloff
    • dcBlock - high pass, 6dB/oct falloff
    • allpass - allpass (-90° phase shift @ center freq)

    Low pass:

    LPF response

    DC blocker:

    DC block response

    Allpass:

    Allpass response

    Biquad

    Source

    • biquadLP - low pass, 12dB/oct falloff, resonance
    • biquadHP - high pass, 12dB/oct falloff, resonance
    • biquadBP - band pass, 12dB/oct falloff, resonance
    • biquadNotch - notch / band-stop, resonance/bandwidth
    • biquadPeak - peak EQ, customizable +/- gain, bandwidth
    • biquadLoShelf - low shelf, customizable +/- gain
    • biquadHiShelf - low shelf, customizable +/- gain

    (Q = 0.707 for all versions)

    Low pass:

    LPF response

    High pass:

    HPF response

    Band pass:

    BPF response

    Notch:

    Notch response

    Peak (gain = 6dB):

    Peak response

    Low shelf (gain = -6dB):

    Lo-shelf response

    High shelf (gain = -6dB):

    Hi-shelf response

    State variable filter

    Source

    • svfLP - low pass, resonance
    • svfHP - high pass, resonance
    • svfBP - band pass, resonance
    • svfNotch - notch / band-stop, resonance/bandwidth
    • svfPeak - peak EQ, customizable +/- gain, bandwidth
    • svfAllpass - allpass, bandwidth

    (Q = 0.5 for all versions)

    Low pass:

    LPF response

    High pass:

    HPF response

    Band pass:

    BPF response

    Notch:

    Notch response

    Peak (gain = 6dB):

    Peak response

    Allpass:

    Allpass response

    Filter responses

    Using the Filter response utils, the following filter types can be evaluated for analyzing their impact on specific frequencies (or frequency bands). Any type implementing IFilter can be used, currently:

    • 1-pole
    • DC-block
    • Biquad
    // peak biquad @ 5kHz w/ -60dB gain
    const coeffs = biquadPeak(5000 / FS, 10, -60).filterCoeffs();
    // {
    //   zeroes: [ 0.030659922512760035, -0.04493872132576855, 0.028719301737009807 ],
    //   poles: [ 1, -0.04493872132576855, -0.94062077575023 ]
    // }
    
    // compute 256 filter responses between 0 - nyquist
    // (magnitude in dBFS by default, phase shift in radians)
    const resp = freqRange(0, 0.5, 256).map((f) => filterResponse(coeffs, f));
    // [
    //   { freq: 0, phase: 0, mag: -9.836140158843584e-14 },
    //   {
    //     freq: 0.00196078431372549,
    //     phase: -1.025916720326544,
    //     mag: -5.731888923801755
    //   },
    //   {
    //     freq: 0.00392156862745098,
    //     phase: -1.27451127560192,
    //     mag: -10.788101434823263
    //   },
    //   ...
    // ]

    Basic filter response plot:

    Filter response

    Delay

    Source

    Ringbuffer / delay line for arbitrary values and support for single & multi-taps at any relative positions. Useful fundamental building block for various other effects, filters etc.

    Feedback delay

    Source

    Variation of delay() which adds a portion of the delayed value to each new input and stores result in delay line.

    Wave shaping

    Source

    This operator remaps inputs via a user provided function. The following shaping functions are provided:

    • waveshapeTan - arctan based (soft-clip/distortion)
    • waveshapeSigmoid - sigmoid based, similar to above
    • waveshapeSin - depending on coefficient, can produce entirely new waveforms

    Use the interactive calculator @ Desmos to experiment.

    Acrtan:

    Tan response

    Sigmoid:

    Sigmoid response

    Sine:

    Sine response

    Foldback distortion

    Source

    Recursively folds input into [-thresh .. +thresh] interval and amplifies it with amp (default: 1/thresh).

    Use the interactive calculator @ Desmos to experiment.

    Foldback response

    FFT

    Source

    • fft()
    • ifft()
    • normalizeFFT()
    • denormalizeFFT()
    • scaleFFT()
    • complexArray()
    • conjugate()
    • powerSumSquared()
    • powerMeanSquared()
    • powerTimeIntegral()
    • spectrumMag()
    • spectrumPow() (optionally as dBFS)
    • spectrumPhase()
    • binFreq()
    • freqBin()
    • fftFreq()
    • integralT() / integralTSquared()
    • integralF() / integralFSquared()

    Window functions

    Source

    • window()
    • applyWindow()
    • windowRect()
    • windowBartlett()
    • windowWelch()
    • windowSin()
    • windowSinPow()
    • windowLanczos()
    • windowHann()
    • windowHamming()
    • windowBlackman()
    • windowBlackmanHarris()
    • windowNuttal()
    • windowBlackmanNuttal()
    • windowGauss()

    Utilities

    Authors

    Karsten Schmidt

    If this project contributes to an academic publication, please cite it as:

    @misc{thing-dsp,
      title = "@thi.ng/dsp",
      author = "Karsten Schmidt",
      note = "https://thi.ng/dsp",
      year = 2015
    }

    License

    © 2015 - 2022 Karsten Schmidt // Apache Software License 2.0

    Install

    npm i @thi.ng/dsp

    DownloadsWeekly Downloads

    198

    Version

    4.2.1

    License

    Apache-2.0

    Unpacked Size

    201 kB

    Total Files

    123

    Last publish

    Collaborators

    • thi.ng