goofi v3.0.0

FmSignal

Generate a sinusoidal FM signal in real-time sample blocks.

Carrier and modulator are frequencies in Hz. Index sets phase deviation:

zero gives a sine wave; increasing it adds sidebands around the carrier.

Carrier and modulator phases persist between blocks and parameter changes.

Output is a float32 array in [-1, 1] with sampling-rate metadata. Connect

it to Buffer, Psd, or Peaks to inspect the spectrum. Choose output/sfreq

high enough for the sidebands; this source does not prevent aliasing.

FmSignal py
out
Type name
signal:FmSignal
Plane
signal
Tags
generator
Language
python
Tier
in-process
Bundle
signal
Source
node-bundles/signal/fm_signal.py
Availability
available

Slots

SlotDirectionType
outoutputARRAY

Parameters

fm

NameTypeDefaultRangeDoc
carrierfloat181 … 100Carrier frequency in Hz.
modulatorfloat50.1 … 100Modulator frequency in Hz; sets sideband spacing.
indexfloat1.20 … 5Phase deviation in radians. Zero disables modulation.

output

NameTypeDefaultRangeDoc
sfreqfloat256256 … 4096Output sample rate in Hz. Raise this to avoid aliasing at higher frequencies.

common

NameTypeDefaultRangeDoc
autotriggerbooltrueRun on the node's own schedule, instead of waiting for an input frame. Turn this on for sources; leave it off for transforms driven by their input.
max_frequencyfloat00 … 100Rate cap for this node, read through `frequency_mode`. 0 means uncapped — the node runs as often as the scheduler and its inputs allow.
frequency_modestringupdates_per_secondupdates_per_second | seconds_per_updateHow to read `max_frequency`: as a rate in Hz (updates per second), or as a period in seconds between updates — convenient for very slow nodes.

Source

The current source of this node, as it stands in the goofi repository at node-bundles/signal/fm_signal.py.

node-bundles/signal/fm_signal.py
"""Reusable real-time, block-output sinusoidal FM source for signal patches."""
import time
import numpy as np
import goofi


class FmSignal(goofi.Node):
    """Generate a sinusoidal FM signal in real-time sample blocks.

    Carrier and modulator are frequencies in Hz. Index sets phase deviation:
    zero gives a sine wave; increasing it adds sidebands around the carrier.
    Carrier and modulator phases persist between blocks and parameter changes.
    Output is a float32 array in [-1, 1] with sampling-rate metadata. Connect
    it to Buffer, Psd, or Peaks to inspect the spectrum. Choose output/sfreq
    high enough for the sidebands; this source does not prevent aliasing.
    """
    TAGS = ['generator']
    PRODUCER = True
    OUTPUTS = {'out': goofi.DataType.ARRAY}
    PARAMS = {'fm': {'carrier': goofi.FloatParam(18., 1., 100., doc='Carrier frequency in Hz.'),
                     'modulator': goofi.FloatParam(5., .1, 100., doc='Modulator frequency in Hz; sets sideband spacing.'),
                     'index': goofi.FloatParam(1.2, 0., 5., doc='Phase deviation in radians. Zero disables modulation.')},
              'output': {'sfreq': goofi.FloatParam(256., 256., 4096., doc='Output sample rate in Hz. Raise this to avoid aliasing at higher frequencies.')}}

    def setup(self):
        self.last = time.monotonic()
        self.remainder = 0.
        self.carrier_phase = self.modulator_phase = 0.

    def process(self):
        now = time.monotonic()
        rate = float(self.params.output.sfreq)
        count = (now - self.last) * rate + self.remainder
        self.last = now
        n = int(count)
        self.remainder = count - n
        if n == 0:
            return {}
        p = self.params.fm
        t = np.arange(n) / rate
        carrier = self.carrier_phase + 2 * np.pi * p.carrier * t
        modulator = self.modulator_phase + 2 * np.pi * p.modulator * t
        signal = np.sin(carrier + p.index * np.sin(modulator))
        self.carrier_phase = (self.carrier_phase + 2*np.pi*p.carrier*n/rate) % (2*np.pi)
        self.modulator_phase = (self.modulator_phase + 2*np.pi*p.modulator*n/rate) % (2*np.pi)
        return {'out': (signal.astype(np.float32), {'sfreq': rate})}

← All nodes