ilusm.dev

audio

Audio processing and synthesis - sample generation, effects (reverb, echo, filter), oscillator-based synthesis, spectrum analysis, and streaming.

Load with: use audio

What this module does

audio works with audio data as plain ilusm lists of floating-point samples. You create a sample object describing the format (sample rate, bit depth, channels), generate or load audio data into it, apply effects, feed it through a synthesiser, analyse its frequency content, and read/write it via a stream.

All processing is pure ilusm using mth for trigonometry - there are no native bindings for audio I/O. The module handles the maths; you connect the output to your platform's audio pipeline.

Quick example

use audio

# Generate a 440 Hz sine wave, 1 second, full amplitude
sample = adsne(440.0, 1.0, 1.0, 44100)

# Apply reverb
sample = adffc(sample, 0.3, 0.5)

# Check the peak amplitude
prn(adnpk(sample))

# Build a two-oscillator synth
synth = adsba()
synth = adsfa(synth, "osc1", "sine", 440.0, 0.7)
synth = adsfa(synth, "osc2", "square", 880.0, 0.3)
out = adsyn(synth, 2.0, 44100)

Functions

Sample creation

adsqa()

Creates a default empty sample: 44100 Hz, 16-bit, stereo, no data, duration 0.0.

adsma(sample_rate, bit_depth, channels) / adsna(sample_rate, bit_depth, channels)

Creates a sample with explicit format settings. Any nil argument falls back to the default (44100 / 16 / 2). Returns an empty sample object ready to receive data.

adsmp(sample, frequency, duration, amplitude)

Fills a sample's data with a pure sine wave at frequency Hz, lasting duration seconds, scaled by amplitude (0.0–1.0). Uses mth.sin. Returns the updated sample.

adsne(frequency, duration, amplitude, sample_rate)

Convenience shorthand: creates a new sample and fills it with a sine wave in one call.

adska(sample1, sample2, weight)

Mixes two samples together. Both must have the same data length. Each output sample is sample1[i] * weight + sample2[i] * (1 - weight). Returns a new sample with the mixed data.

Effects

adfxr(sample, room_size, damping) / adffc(sample, room_size, damping)

Simple reverb. Adds a delayed copy of each sample (delayed by room_size * sample_rate samples) scaled by damping to the original. Returns a new sample.

adfxc(sample, delay_time, decay) / adffa(sample, delay_time, decay)

Echo effect. Adds a delayed copy (delayed by delay_time * sample_rate samples) scaled by decay. Returns a new sample.

adfxf(sample, filter_type, cutoff) / adfha(sample, filter_type, cutoff)

First-order IIR filter. filter_type is "lowpass" or "highpass". cutoff is the cutoff frequency in Hz. Alpha is computed as 2π × cutoff / sample_rate. For any other filter type, returns the original data unchanged. Returns a new sample.

adfga(type, parameters) / audfx(type, parameters)

Creates a generic effect descriptor object with type and parameters. The effect is not applied - this is a data object for passing effect configs around.

Synthesis

adsla() / adsba()

Creates a new synthesiser with empty oscillator, envelope, and filter maps.

adsja(synth, id, waveform, frequency, amplitude) / adsfa(synth, id, waveform, frequency, amplitude)

Adds an oscillator to the synth. waveform can be "sine", "square", "sawtooth", or "triangle". Returns the updated synth.

adsia(synth, duration, sample_rate) / adsyn(synth, duration, sample_rate)

Renders audio from all oscillators in the synth by summing their outputs sample-by-sample. Returns a mono sample object (channels: 1) with the mixed result.

adsga(oscillator, duration, sample_rate)

Internal: generates the raw sample list for a single oscillator over duration seconds. Waveform generation per sample:

  • "sine" - mth.sin(phase)
  • "square" - 1.0 if sine ≥ 0, else −1.0
  • "sawtooth" - 2 × (phase/2π − floor(phase/2π + 0.5))
  • "triangle" - absolute value of sawtooth, scaled to −1/+1
  • Unknown waveform - 0.0

Analysis

adncr(fft_size, window_function) / adnlz(fft_size, window_function)

Creates an analyser with a given FFT size (default 1024) and window function (default "hanning").

adnaa(analyzer, sample) / adnfr(sample, fft_size)

Simplified frequency spectrum. Takes the first fft_size samples and computes a magnitude for each frequency bin up to Nyquist. Note: this is a simplified magnitude readout (abs of sample value), not a proper FFT. Returns {frequencies, magnitudes}.

adnrm(analyzer, sample) / adnrm(sample)

RMS (Root Mean Square) amplitude of the sample - square root of the mean of squared sample values. Returns 0.0 for empty data.

adnpk(analyzer, sample) / adnpk(sample)

Peak amplitude - the maximum absolute sample value. Returns 0.0 for empty data.

Format descriptors

adfca()

Returns a WAV format descriptor: {name: "WAV", extensions: [".wav"]}.

adfaa()

Returns an MP3 format descriptor: {name: "MP3", extensions: [".mp3"]}.

adfrm()

Returns a FLAC format descriptor: {name: "FLAC", extensions: [".flac"]}.

adfba()

Returns an OGG format descriptor: {name: "OGG", extensions: [".ogg"]}.

Streaming

adsha(buffer_size, sample_rate) / adsoa(buffer_size, sample_rate)

Creates a stream with a given buffer size (default 1024) and sample rate (default 44100). The stream starts paused (playing: fls).

adsaa(stream, data)

Appends sample data to the stream's buffer.

adsda(stream, num_samples)

Reads up to num_samples from the stream, advancing the read position. Returns however many are available up to the request.

adsca(stream)

Sets the stream to playing (playing: tru).

adstr(stream)

Sets the stream to stopped (playing: fls).

adsea(stream, position)

Seeks the stream's read position to the given sample index.

Notes

  • All audio data is stored as lists of floats. There is no native I/O - connect the output to your platform's audio API yourself.
  • adnaa / adnfr is a simplified approximation, not a real FFT. Magnitudes are absolute sample values for the first fft_size/2 samples.
  • adska (mix) requires both samples to have the same data length - it errors otherwise.
  • Some function names are overloaded (adnrm, adnpk, adsoa) and have convenience single-argument shorthands that create a default analyser internally.
  • Requires mth, trl, txt, jsn, cry, and tim.