Skip to content

jm object NAME --preset generator — generator (() → output)

A generator produces output without taking input — step() takes no argument and returns the next value, steps(n) produces n values. State carries whatever the algorithm needs to advance from one call to the next.

Concrete examples: a numerically-controlled oscillator (NCO), a pseudo-random sequence (LFSR), a counter, a UUID generator, a queue drainer that yields the next item, or a parser tokenizer that emits tokens one at a time from a pre-loaded buffer.

--preset generator expands to --arg-type void, which strips the input side of step(). The scaffold builds and tests green straight away; with no input, void arg-type defaults to a complex return.

A generator's whole job is to advance its own state on every call, so it almost always needs --mutable (the default step() takes a const state pointer; see --mutable).

Command

jm object NAME --preset generator \
    --return-type "float _Complex" \
    --state phase:float:0.0f \
    --state freq:float:0.0f \
    --mutable

What you get

native/inc/<pkg>/NAME/NAME_core.h

Every C symbol carries the component's C stem, <pkg>_NAME ([project] c_prefix, which jm new defaults to the package name).

typedef struct {
    float phase;
    float freq;
} <pkg>_NAME_state_t;

<pkg>_NAME_state_t *<pkg>_NAME_create(float phase, float freq);
void <pkg>_NAME_destroy(<pkg>_NAME_state_t *state);
void <pkg>_NAME_reset(<pkg>_NAME_state_t *state);
/* plus <pkg>_NAME_get_/set_ for each state field */

/* Per-call generator: emit one sample. */
static inline float _Complex
<pkg>_NAME_step(<pkg>_NAME_state_t *state)
{
    (void)state; /* TODO: implement */
    return (float _Complex)0;
}

/* Block generator: fill n samples into output[]. */
void <pkg>_NAME_steps(<pkg>_NAME_state_t *state, float _Complex *output,
                      size_t n);

native/src/NAME/NAME_core.c

void
<pkg>_NAME_steps(<pkg>_NAME_state_t *state, float _Complex *output,
                 size_t n)
{
    for (size_t i = 0; i < n; i++)
        output[i] = <pkg>_NAME_step(state);
}

What you fill in

The step() body in NAME_core.h — advance state, emit one sample. A cosine oscillator is typical (add #include <math.h> to NAME_core.h, above step(), for cosf/sinf):

static inline float _Complex
<pkg>_NAME_step(<pkg>_NAME_state_t *state)
{
    float _Complex y = cosf(state->phase) + sinf(state->phase) * I;
    state->phase += state->freq;
    return y;
}

Other shapes: an LFSR, a Costas loop, a file-decoded sample stream — whatever produces one sample per call.

Python usage

import numpy as np
from <pkg> import NAME

src = NAME(phase=0.0, freq=0.01)
y = src.step()                           # → one complex sample
ys = src.steps(1024)                     # → (1024,) complex64

Concrete types

Slot Accepts Rejects Default
--arg-type Implicit void; sources take no input. Nothing is refused: an explicit --arg-type after --preset overrides the preset, and the object is then no longer a generator. void
--return-type Any scalar. const char *, void (use consumer); array return unsupported. float _Complex
--state field:T:D Any scalar. const char *. phase:float:0.0f, freq:float:0.0f

The generator preset always emits a steps(n) that fills an n-sized ndarray; the element type matches --return-type.