Jump to content

Lua modulation API: Difference between revisions

From SimTX Wiki
Clanker (talk | contribs)
Document native-rate audio blocks and optional Lua resampling
Clanker (talk | contribs)
Rework documentation for new users: illustrated steps, clear navigation, preserved technical references, and current 0.3.6 behavior
 
Line 1: Line 1:
{{Documentation navigation}}
A modulation registration receives a context object named <code>ctx</code>. The same object is passed to the optional <code>start</code> and <code>stop</code> callbacks and to every <code>process</code> callback. Indices are zero-based.
A modulation registration receives a context object named <code>ctx</code>. The same object is passed to the optional <code>start</code> and <code>stop</code> callbacks and to every <code>process</code> callback. Indices are zero-based.
== How to use this reference ==
Use this page after declaring a mode with [[Lua extension API#Modulation declaration|simtx.modulation]]. A callback receives one block at a time. Read its required output size from ctx:n(), fill every sample, and keep values finite. The lifecycle snippets below illustrate callbacks; they are not complete working signal generators. Keep default input resampling unless the extension deliberately implements its own conversion.


== Lifecycle ==
== Lifecycle ==
Line 20: Line 26:


== Stream information ==
== Stream information ==
{| class="wikitable"
{| class="wikitable mw-collapsible mw-collapsed"
! Method !! Result
! Method !! Result
|-
|-
Line 45: Line 51:


== Reading audio and writing output ==
== Reading audio and writing output ==
{| class="wikitable"
{| class="wikitable mw-collapsible mw-collapsed"
! Method !! Effect
! Method !! Effect
|-
|-
Line 117: Line 123:


== Controls and inputs ==
== Controls and inputs ==
{| class="wikitable"
{| class="wikitable mw-collapsible mw-collapsed"
! Method !! Result
! Method !! Result
|-
|-
Line 138: Line 144:
Image coordinates are zero-based. Channel and luminance values are normalized from 0 to 1.
Image coordinates are zero-based. Channel and luminance values are normalized from 0 to 1.


{| class="wikitable"
{| class="wikitable mw-collapsible mw-collapsed"
! Method !! Result
! Method !! Result
|-
|-
Line 161: Line 167:
Video frame indices are zero-based.
Video frame indices are zero-based.


{| class="wikitable"
{| class="wikitable mw-collapsible mw-collapsed"
! Method !! Result
! Method !! Result
|-
|-
Line 182: Line 188:


== File objects ==
== File objects ==
{| class="wikitable"
{| class="wikitable mw-collapsible mw-collapsed"
! Method !! Result
! Method !! Result
|-
|-

Latest revision as of 21:22, 13 September 2026

A modulation registration receives a context object named ctx. The same object is passed to the optional start and stop callbacks and to every process callback. Indices are zero-based.


How to use this reference[edit | edit source]

Use this page after declaring a mode with simtx.modulation. A callback receives one block at a time. Read its required output size from ctx:n(), fill every sample, and keep values finite. The lifecycle snippets below illustrate callbacks; they are not complete working signal generators. Keep default input resampling unless the extension deliberately implements its own conversion.

Lifecycle[edit | edit source]

simtx.modulation {
  name = "Example",
  start = function(ctx)
    -- Prepare state for this transmission.
  end,
  process = function(ctx)
    -- Produce one complete output block.
  end,
  stop = function(ctx)
    -- Release script state if necessary.
  end,
}

Each transmission receives an isolated copy of the script state. Control values remain live and may change between blocks. Every output sample must be finite.

Stream information[edit | edit source]

Method Result
ctx:n() Number of output samples required in the current block.
ctx:blockSize() Alias of n().
ctx:rate() Output and Lua processing sample rate in hertz.
ctx:sampleRate() Alias of rate().
ctx:audioN() Number of input audio samples available in the current block.
ctx:audioBlockSize() Alias of audioN().
ctx:audioSampleRate() Input audio sample rate in hertz.
ctx:time() Time in seconds at the start of the block.
ctx:sessionTime() Alias of time().
ctx:samplePosition() Absolute sample position at the start of the block.

Reading audio and writing output[edit | edit source]

Method Effect
ctx:audio(index) Returns an input mono audio sample at a zero-based input index, normally in the range -1 to 1.
ctx:iq(index, i, q) Writes one complex output sample.
ctx:map(function(audio, index, time) ... end) Maps every input audio sample. The callback returns I and optional Q.
ctx:generate(function(index, time) ... end) Generates every sample without using audio. The callback returns I and optional Q.
ctx:passthrough() Copies incoming mono audio to the output.
ctx:gain(amount) Copies incoming audio to the output with a constant linear gain.
ctx:limit(peak) Limits both output components to the symmetric positive peak.
ctx:mixTone(frequencyHz, level) Mixes a sine tone with incoming audio and reserves headroom for it.
ctx:mixTone(frequencyHz, level, reserveHeadroom) Mixes a sine tone and selects whether the input is reduced by the tone level.
ctx:fill(value) Fills an audio block, or the I component of an IQ block, with a constant value.
ctx:fill(i, q) Fills the block with a constant complex sample.
ctx:silence() Fills the block with zeroes.

map and generate callbacks receive absolute sample time, not merely time within the current block. This permits phase-continuous generators. Omitting the Q return value sets Q to zero.

process = function(ctx)
  local gain = ctx:control("gain")
  ctx:map(function(audio)
    return audio * gain
  end)
  ctx:limit(1)
end

Block operations such as gain, mixTone, and fill are preferable when they express the required operation because they avoid a Lua callback for every sample.

Native-rate audio[edit | edit source]

Input audio is resampled to the Lua processing rate by default. In this mode, ctx:audioN() matches ctx:n(), ctx:audioSampleRate() matches ctx:sampleRate(), and the one-to-one helpers can be used normally.

A modulation may set resample_audio = false to receive audio at the source's native rate:

simtx.modulation {
    name = "Custom resampler",
    sample_rate = 240000,
    resample_audio = false,
    process = function(ctx)
        local outputCount = ctx:n()
        local outputRate = ctx:sampleRate()
        local inputCount = ctx:audioN()
        local inputRate = ctx:audioSampleRate()

        -- Read native input with ctx:audio(index).
        -- Produce outputCount samples with ctx:iq() or ctx:generate().
        -- Keep filter and FIFO state in Lua across calls.
    end,
}

The input and output blocks cover the same elapsed duration, subject to sample-count rounding. Their sample counts can differ. During extreme downsampling, an output block can occasionally contain zero samples.

When the input and output rates or block sizes differ, ctx:map(), ctx:passthrough(), ctx:gain(), and ctx:mixTone() raise an error because they require one input sample per output sample. Use ctx:audio() to read the native block and write the resampled result with ctx:iq() or ctx:generate(). The extension is responsible for its resampling filter, FIFO, and state.

The registration fields are documented in Lua extension API.

Controls and inputs[edit | edit source]

Method Result
ctx:control(id) Current value of one control, or nil for an unknown ID.
ctx:controls() Snapshot table containing all current control values.
ctx:input(id) Decoded image, video, or file input, or nil.
ctx:inputs() Snapshot table containing all supplied non-audio inputs.
ctx:hasInput(id) Whether a non-audio input was supplied.
ctx:finish() Requests a clean end after the current block.

Audio is accessed through audio, map, and the block operations rather than through input.

Image objects[edit | edit source]

Image coordinates are zero-based. Channel and luminance values are normalized from 0 to 1.

Method Result
image:name() Original filename.
image:width(), image:height() Dimensions in pixels.
image:red(x,y), green, blue, alpha One normalized channel.
image:luma(x,y) Rec. 709 luminance.
image:pixel(x,y) Table with r, g, b, a, and luma fields.
image:argb(x,y) Packed ARGB integer.
image:resize(width,height) High-quality resized image object.

An image side may contain at most 8,192 pixels, and an image may contain at most 16,777,216 pixels.

Video objects[edit | edit source]

Video frame indices are zero-based.

Method Result
video:name() Original filename.
video:width(), video:height() Frame dimensions.
video:frames() Number of frames.
video:duration() Duration in seconds.
video:frameRate() Average frames per second.
video:frame(index) Decoded frame as an image object.
video:frameAt(seconds) Frame at the requested time as an image object. Times outside the duration are clamped.

Video decoding supports MP4 and MOV-family files accepted by the picker. A script should normally obtain one frame per logical video frame and reuse it while producing the corresponding samples.

File objects[edit | edit source]

Method Result
file:name() Original filename.
file:size() Size in bytes.
file:byteAt(index) Unsigned byte from 0 to 255 at a zero-based index.
file:text() Entire payload decoded as UTF-8 text.

A generic file input may contain at most 64 MiB.