17.13. SoundFont 2 per-voice runtime: envelope, LFO, modulators, biquad

Module strudel_sf2_voice

17.13.1. Enumerations

SF2EnvStage
Values:
  • env_delay = 0 - Silent delay before the envelope starts.

  • env_attack = 1 - Rising ramp from 0.0 to 1.0.

  • env_hold = 2 - Hold at peak level before decay begins.

  • env_decay = 3 - Falling ramp from peak toward sustain level.

  • env_sustain = 4 - Held at sustain level until note-off.

  • env_release = 5 - Falling ramp from current level toward zero after note-off.

  • env_finished = 6 - Envelope has fully decayed and voice can be freed.

17.13.2. Structures

SF2Envelope
Fields:
  • stage : SF2EnvStage = strudel_sf2_voice::SF2EnvStage.env_delay - Current envelope stage.

  • time_in_stage : float = 0f - Countdown samples remaining in the current stage (delay/hold).

  • level : float = 0f - Current envelope output level [0..1].

  • release_level : float = 1f - Level captured at note-off; release ramps down from here.

  • slope : float = 0f - Per-sample slope (linear delta or exponential multiplier depending on stage).

  • is_exponential : bool = false - True when the current segment uses an exponential curve instead of linear.

  • is_amp_env : bool = false - True for the amplitude envelope (uses exponential decay/release and velocity-scaled attack).

  • midi_velocity : int = 127 - MIDI velocity used to scale the attack segment length on amplitude envelopes.

  • delay_sec : float = 0f - Delay stage duration in seconds.

  • attack_sec : float = 0.001f - Attack stage duration in seconds.

  • hold_sec : float = 0f - Hold stage duration in seconds.

  • decay_sec : float = 0.001f - Decay stage duration in seconds.

  • sustain_level : float = 1f - Sustain level in [0..1] as a linear fraction (1.0 = peak, 0.0 = silence).

  • release_sec : float = 0.001f - Release stage duration in seconds.

SF2LFO
Fields:
  • delay_sec : float = 0f - Silent delay before the LFO begins oscillating, in seconds.

  • freq : float = 8.176f - LFO frequency in Hz (SF2 default ~8.176 Hz = 0 cents from middle C).

  • phase : float = 0f - Current phase in [0..1].

  • elapsed : float = 0f - Elapsed time since note-on, used to test against delay_sec.

SF2VoiceAttenState
Fields:
  • base_atten_cb : int = 0 - Generator initialAttenuation value in centibels, before modulators.

  • velocity : int = 127 - Note-on velocity captured at voice creation.

  • key : int = 60 - MIDI key number captured at voice creation.

  • atten_mods : array< SF2Modulator> - All modulators (zone + unoverridden defaults) targeting initialAttenuation, collected once.

17.13.3. Envelope

sf2_env_start(env: SF2Envelope; sample_rate: float)

def sf2_env_start (var env: SF2Envelope; sample_rate: float)

Arguments:
sf2_env_tick(env: SF2Envelope; released: bool; sample_rate: float): float

def sf2_env_tick (var env: SF2Envelope; released: bool; sample_rate: float) : float

Arguments:
  • env : SF2Envelope

  • released : bool

  • sample_rate : float

17.13.4. LFO

sf2_lfo_tick(lfo: SF2LFO; dt: float): float

def sf2_lfo_tick (var lfo: SF2LFO; dt: float) : float

Arguments:

17.13.5. Modulators

sf2_apply_default_modulators(izone: SF2Zone; global_izone: SF2Zone const?; pzone: SF2Zone const?; global_pzone: SF2Zone const?; velocity: int; key: int; cc_values: int const[128]; gen_offsets: float[64])

Apply the 10 SF2 spec default modulators (8.4.1-8.4.10) that are not overridden by any zone modulator. Must be called AFTER zone modulators have already been accumulated into gen_offsets.

Arguments:
  • izone : SF2Zone

  • global_izone : SF2Zone?

  • pzone : SF2Zone?

  • global_pzone : SF2Zone?

  • velocity : int

  • key : int

  • cc_values : int[128]

  • gen_offsets : float[64]

sf2_apply_modulators(zone: SF2Zone; velocity: int; key: int; cc_values: int const[128]; gen_offsets: float[64])

Accumulate all modulator outputs from a zone into gen_offsets indexed by destination generator.

Arguments:
  • zone : SF2Zone

  • velocity : int

  • key : int

  • cc_values : int[128]

  • gen_offsets : float[64]

sf2_collect_atten_modulators(izone: SF2Zone; global_izone: SF2Zone const?; pzone: SF2Zone const?; global_pzone: SF2Zone const?): array<SF2Modulator>

Collect every modulator targeting initialAttenuation across all zone layers plus unoverridden defaults. Called once at voice creation; results are cached in SF2VoiceAttenState for fast CC-driven re-evaluation.

Arguments:
sf2_default_cc_values(cc: int[128])

Initialise a CC array to General MIDI defaults (CC7=100, CC10=64, CC11=127, pitchwheel slot=8192). Used by MidiChannelState and standalone voice creation; slot 127 holds the pitch wheel value for SF2 modulator input.

Arguments:
  • cc : int[128]

sf2_mod_get_value(m: SF2Modulator; velocity: int; key: int; cc_values: int const[128]): float

Evaluate a single SF2 modulator: amount * transform(src1) * transform(src2). Applies the optional absolute-value transform when m.transform == 2.

Arguments:
  • m : SF2Modulator

  • velocity : int

  • key : int

  • cc_values : int[128]

sf2_mod_identity_match(a: SF2Modulator; b: SF2Modulator): bool

Two SF2 modulators share identity when src1, src2, and destination all match. Used to detect when a zone modulator overrides a default modulator.

Arguments:
sf2_recalc_atten(atten_state: SF2VoiceAttenState; cc_values: int const[128]): float

Recalculate voice attenuation (linear gain) from cached modulators and current CC values. Called at voice creation and whenever CC7/CC11/etc change on the channel.

Arguments:
sf2_velocity_attenuation(velocity: int): float

SF2 default velocity-to-attenuation curve (concave, 960 cB range). Implements default modulator #1 from SF2 spec section 8.4.2; returns a linear gain.

Arguments:
  • velocity : int

17.13.6. Voice creation

sf2_create_c_voice(sf2: SF2File; inst_idx: int; izone_idx: int; pzone: SF2Zone const?; global_pzone: SF2Zone const?; note: int; velocity: int; channel: int; cc_values: int const[128]; out_voice: ma_sf2_voice; out_atten_state: SF2VoiceAttenState): bool

Resolve all SF2 generators/modulators for a note and populate the C ma_sf2_voice runtime state. Sets up sample addressing, pitch, envelopes, LFOs, filter, pan and attenuation; returns false if the sample is invalid.

Arguments:
sf2_get_chorus_send(sf2: SF2File; inst_idx: int; izone_idx: int; pzone: SF2Zone const?; global_pzone: SF2Zone const?): float

Resolve the chorus effects-send amount across zone layers, normalised to [0..1].

Arguments:
sf2_get_exclusive_class(sf2: SF2File; inst_idx: int; izone_idx: int; pzone: SF2Zone const?; global_pzone: SF2Zone const?): int

Resolve the exclusiveClass generator across all zone layers. Non-zero classes cut off earlier notes in the same class (GM drum open/closed hi-hat behaviour).

Arguments:
sf2_get_reverb_send(sf2: SF2File; inst_idx: int; izone_idx: int; pzone: SF2Zone const?; global_pzone: SF2Zone const?): float

Resolve the reverb effects-send amount across zone layers, normalised to [0..1].

Arguments: