.. _stdlib_strudel_synth: ========================================================= Audio synthesis: oscillators, drums, filters, and effects ========================================================= .. das:module:: strudel_synth Module strudel_synth +++++++++ Constants +++++++++ .. _global-strudel_synth-SAMPLE_RATE: .. das:attribute:: SAMPLE_RATE = 48000 SAMPLE_RATE:int const .. _global-strudel_synth-TWO_PI: .. das:attribute:: TWO_PI = 6.2831855f TWO_PI:float const .. _global-strudel_synth-OSC_TURN_DOWN: .. das:attribute:: OSC_TURN_DOWN = 0.3f OSC_TURN_DOWN:float const .. _global-strudel_synth-VOWEL_NUM_FORMANTS: .. das:attribute:: VOWEL_NUM_FORMANTS = 5 VOWEL_NUM_FORMANTS:int const .. _global-strudel_synth-VOWEL_MAKEUP_GAIN: .. das:attribute:: VOWEL_MAKEUP_GAIN = 8f VOWEL_MAKEUP_GAIN:float const ++++++++++++ Enumerations ++++++++++++ .. _enum-strudel_synth-OscType: .. das:attribute:: OscType :Values: * **osc_sine** = 0 - Pure sine wave. * **osc_sawtooth** = 1 - Band-limited sawtooth (poly-BLEP anti-aliased). * **osc_square** = 2 - Band-limited square wave (poly-BLEP anti-aliased). * **osc_triangle** = 3 - Triangle wave derived from the phase. * **osc_supersaw** = 4 - Unison stack of detuned band-limited sawtooths, stereo-spread. * **osc_pink_noise** = 5 - Pink noise via Paul Kellet's 7-stage cascaded IIR. * **osc_white_noise** = 6 - Uniform white noise. * **osc_unknown** = 7 - Unrecognised sound name — used as a sentinel by `to_osc_type`. ++++++++++ Structures ++++++++++ .. _struct-strudel_synth-PinkNoiseState: .. das:attribute:: PinkNoiseState struct PinkNoiseState .. _struct-strudel_synth-FormantBiquad: .. das:attribute:: FormantBiquad :Fields: * **a0** : double = 0lf - Feed-forward coefficient 0. * **a1** : double = 0lf - Feed-forward coefficient 1 (0 for BPF). * **a2** : double = 0lf - Feed-forward coefficient 2 (`-a0` for BPF). * **b1** : double = 0lf - Feedback coefficient 1. * **b2** : double = 0lf - Feedback coefficient 2. * **z1** : double = 0lf - Internal delay-line state 1. * **z2** : double = 0lf - Internal delay-line state 2. * **gain** : float = 1f - Per-formant amplitude scaling applied to the filter output by the vowel filter. * **active** : int = 0 - Non-zero when the filter has valid coefficients and should process samples. .. _struct-strudel_synth-VowelFilter: .. das:attribute:: VowelFilter :Fields: * **formants** : :ref:`FormantBiquad `\ [5] - Five formant bandpass biquads processed in parallel and summed. * **active** : bool = false - True once the filter has been set up for a known vowel; otherwise ``tick`` is bypassed by callers. .. _struct-strudel_synth-FormantData: .. das:attribute:: FormantData :Fields: * **freqs** : float\ [5] - Centre frequency of each formant in Hz. * **gains** : float\ [5] - Linear gain of each formant (formant 0 is usually the loudest). * **qs** : float\ [5] - Q (resonance) of each formant bandpass. .. _struct-strudel_synth-VoiceFX: .. das:attribute:: VoiceFX :Fields: * **bc** : :ref:`ma_bitcrush ` - Bitcrusher state (bit-depth + sample-rate reduction). * **ws** : :ref:`ma_waveshaper ` - Waveshaper state (drive-based soft distortion). * **dj** : :ref:`ma_djfilter ` - DJ filter state (single-knob low-pass/high-pass blend). * **bp** : :ref:`ma_bandpass ` - Bandpass filter state (centre + Q). * **ph** : :ref:`ma_phaser ` - Phaser state (rate/depth/centre/sweep). * **trem** : :ref:`ma_tremolo ` - Tremolo state (amplitude modulation rate/depth). * **comp** : :ref:`ma_compressor ` - Compressor state (threshold/ratio/knee/attack/release). * **has_crush** : bool = false - Bitcrush is enabled and will be applied to the voice. * **has_shape** : bool = false - Waveshaper is enabled. * **has_djf** : bool = false - DJ filter is enabled (position != 0.5). * **has_bpf** : bool = false - Bandpass filter is enabled. * **has_phaser** : bool = false - Phaser is enabled. * **has_tremolo** : bool = false - Tremolo is enabled. * **has_compressor** : bool = false - Compressor is enabled. * **active** : bool = false - True if any of the above effects is enabled; skip `fx_apply` entirely when false. .. _struct-strudel_synth-OscVoice: .. das:attribute:: OscVoice :Fields: * **osc_type** : :ref:`OscType ` = strudel_synth::OscType.osc_sine - Which waveform this voice produces. * **freq** : float = 440f - Base frequency in Hz (before FM and supersaw detune). * **phase** : float = 0f - Main oscillator phase in [0, 1). * **duration** : float = 1f - Total note duration in seconds (release stage extends beyond this). * **attack** : float = 0.001f - ADSR attack time in seconds. * **decay** : float = 0.05f - ADSR decay time in seconds. * **sustain** : float = 0f - ADSR sustain level in [0, 1]. * **release_sec** : float = 0.01f - ADSR release time in seconds. * **samples_elapsed** : int = 0 - Number of samples rendered so far; used to compute the envelope time. * **finished** : bool = false - Set to true when the ADSR has fully decayed; scheduler then retires the voice. * **gain** : float = 1f - Effective voice gain (event.gain * event.velocity). * **pan** : float = 0f - Stereo pan: -1 = full left, 0 = centre, +1 = full right. * **cut** : int = 0 - Cut group — non-zero values cut off any previous voice with the same cut value. * **offset_frames** : int = 0 - Sub-chunk onset delay in frames, consumed at the start of the first render chunk. * **lpf_biquad** : :ref:`ma_sf2_biquad ` - Low-pass biquad filter state (initialised by `osc_voice_init_filters`). * **hpf_biquad** : :ref:`ma_sf2_biquad ` - High-pass biquad filter state. * **fm_depth** : float = 0f - FM modulation index (0 disables FM). * **fm_harmonicity** : float = 1f - Ratio of modulator frequency to carrier frequency. * **fm_mod_phase** : float = 0f - FM modulator oscillator phase in [0, 1). * **supersaw_phases** : float\ [7] - Independent phases for each detuned sawtooth voice in the supersaw stack. * **pink** : :ref:`PinkNoiseState ` - Pink-noise generator state (Paul Kellet's 7-stage IIR). * **white_seed** : uint = 0x3039 - LCG seed used by the white-noise generator. * **reverb_send** : float = 0f - Send amount into the reverb bus (driven by Event.room). * **chorus_send** : float = 0f - Send amount into the chorus bus (driven by Event.chorus). * **delay_send** : float = 0f - Send amount into the delay bus (driven by Event.delay_amount). * **orbit** : int = 0 - Orbit index used by the scheduler to route this voice into a shared effect bus. * **vowel_filter** : :ref:`VowelFilter ` - Optional vowel formant filter applied after the biquad filters. * **fx** : :ref:`VoiceFX ` - Per-voice FX chain (bitcrush/waveshaper/DJ filter/bandpass/phaser/tremolo/compressor). ++++++++++++++++ Pitch conversion ++++++++++++++++ * :ref:`note_to_freq (note: float) : float ` .. _function-strudel_synth_note_to_freq_float: .. das:function:: note_to_freq(note: float) : float Convert a MIDI note number to frequency in Hz (A4 = 69 = 440 Hz, middle C = 60). :Arguments: * **note** : float ++++++++++++++++ Noise generators ++++++++++++++++ * :ref:`noise (var seed: uint&) : float ` .. _function-strudel_synth_noise_uint_ref_: .. das:function:: noise(seed: uint&) : float Linear congruential pseudo-random noise in [-1.0, 1.0]. Advances the seed in place. :Arguments: * **seed** : uint\ & ++++++++++++++ Drum renderers ++++++++++++++ * :ref:`render_bd (duration: float; gain: float) : array\ ` * :ref:`render_cowbell (duration: float; gain: float) : array\ ` * :ref:`render_cp (duration: float; gain: float) : array\ ` * :ref:`render_crash (duration: float; gain: float) : array\ ` * :ref:`render_hh (duration: float; gain: float) : array\ ` * :ref:`render_oh (duration: float; gain: float) : array\ ` * :ref:`render_ride (duration: float; gain: float) : array\ ` * :ref:`render_rimshot (duration: float; gain: float) : array\ ` * :ref:`render_sd (duration: float; gain: float) : array\ ` * :ref:`render_tambourine (duration: float; gain: float) : array\ ` * :ref:`render_tom (duration: float; gain: float; base_freq: float) : array\ ` .. _function-strudel_synth_render_bd_float_float: .. das:function:: render_bd(duration: float; gain: float) : array Render an 808-style kick drum as a mono buffer at SAMPLE_RATE. Combines a pitched sine body with a fast pitch sweep, a bandpass beater click and a rimshot-like snap, then applies a short room. :Arguments: * **duration** : float * **gain** : float .. _function-strudel_synth_render_cowbell_float_float: .. das:function:: render_cowbell(duration: float; gain: float) : array Render a cowbell: two detuned square-wave tones through a narrow bandpass, with a second quieter strike 8 ms later. :Arguments: * **duration** : float * **gain** : float .. _function-strudel_synth_render_cp_float_float: .. das:function:: render_cp(duration: float; gain: float) : array Render a hand-clap: a sharp bandpassed noise burst (~1.1 kHz) with a metallic bright edge and a long room tail. :Arguments: * **duration** : float * **gain** : float .. _function-strudel_synth_render_crash_float_float: .. das:function:: render_crash(duration: float; gain: float) : array Render a crash cymbal: lower-pitched bell partials plus a broadband metallic wash with a medium-fast decay. :Arguments: * **duration** : float * **gain** : float .. _function-strudel_synth_render_hh_float_float: .. das:function:: render_hh(duration: float; gain: float) : array Render a closed hi-hat: metallic oscillator bank layered with a short tonal bell (~180 Hz), plus room. :Arguments: * **duration** : float * **gain** : float .. _function-strudel_synth_render_oh_float_float: .. das:function:: render_oh(duration: float; gain: float) : array Render an open hi-hat: the same metallic oscillator bank as hh but with a much slower decay. :Arguments: * **duration** : float * **gain** : float .. _function-strudel_synth_render_ride_float_float: .. das:function:: render_ride(duration: float; gain: float) : array Render a ride cymbal: two bell partials (~340/387 Hz) plus a metallic shimmer, with a long sustain. :Arguments: * **duration** : float * **gain** : float .. _function-strudel_synth_render_rimshot_float_float: .. das:function:: render_rimshot(duration: float; gain: float) : array Render a rimshot/side-stick: a short woody body (~200 Hz) plus a bandpassed noise snap and a high transient click. :Arguments: * **duration** : float * **gain** : float .. _function-strudel_synth_render_sd_float_float: .. das:function:: render_sd(duration: float; gain: float) : array Render a snare drum as a mono buffer: tonal body (~220/330 Hz) plus high-passed noise for the wires, with a short room tail. :Arguments: * **duration** : float * **gain** : float .. _function-strudel_synth_render_tambourine_float_float: .. das:function:: render_tambourine(duration: float; gain: float) : array Render a tambourine: high-passed noise with two narrow bandpass jingle peaks (~3.8 kHz and ~8.8 kHz) and a delayed second hit. :Arguments: * **duration** : float * **gain** : float .. _function-strudel_synth_render_tom_float_float_float: .. das:function:: render_tom(duration: float; gain: float; base_freq: float) : array Render a tom drum at `base_freq` with a BD-style body + beater click + impulse + resonant-head overtones and a short room. Used for tom_low (~80 Hz) and tom_high (~175 Hz). :Arguments: * **duration** : float * **gain** : float * **base_freq** : float +++++++++++++++ Oscillator type +++++++++++++++ * :ref:`to_osc_type (sound: string) : OscType ` .. _function-strudel_synth_to_osc_type_string: .. das:function:: to_osc_type(sound: string) : OscType Map a strudel sound name ("sine", "sawtooth", ...) to an `OscType`; unknown names return `osc_unknown`. :Arguments: * **sound** : string ++++++++++++++ Voice FX chain ++++++++++++++ * :ref:`fx_apply (var fx: VoiceFX; var buf: array\; n_frames: int) ` * :ref:`fx_apply_to_samples (event: Event; var samples: array\; sample_rate: float) ` * :ref:`fx_init_from_event (var fx: VoiceFX; event: Event; sample_rate: float) ` .. _function-strudel_synth_fx_apply_VoiceFX_array_ls_float_gr__int: .. das:function:: fx_apply(fx: VoiceFX; buf: array; n_frames: int) Apply the enabled effects to an interleaved stereo buffer in place. Order: crush -> shape -> djfilter -> bandpass -> phaser -> tremolo -> compressor (phaser/tremolo modulate the finished tone, compressor is last so it sees the post-FX signal). :Arguments: * **fx** : :ref:`VoiceFX ` * **buf** : array * **n_frames** : int .. _function-strudel_synth_fx_apply_to_samples_Event_array_ls_float_gr__float: .. das:function:: fx_apply_to_samples(event: Event; samples: array; sample_rate: float) Apply Event-driven effects to a pre-rendered stereo buffer as a one-shot. Effect state is local to this call, so effects reset every render; used for drum/sample voices that don't stream. :Arguments: * **event** : :ref:`Event ` * **samples** : array * **sample_rate** : float .. _function-strudel_synth_fx_init_from_event_VoiceFX_Event_float: .. das:function:: fx_init_from_event(fx: VoiceFX; event: Event; sample_rate: float) Configure a VoiceFX chain from the relevant Event fields (crush/coarse/shape/djf/bpf/phaser/tremolo/compressor). Leaves everything disabled when no Event field is active, so the chain can stay at `fx.active == false`. :Arguments: * **fx** : :ref:`VoiceFX ` * **event** : :ref:`Event ` * **sample_rate** : float ++++++++++++++++ Oscillator voice ++++++++++++++++ * :ref:`osc_render_chunk (var voice: OscVoice; var output: array\; var reverb_send_buf: array\; var delay_send_buf: array\; var chorus_send_buf: array\; chunkFrames: int) ` * :ref:`osc_voice_init_filters (var voice: OscVoice; lpf: float; hpf: float; lpq: float; hpq: float) ` * :ref:`osc_voice_init_supersaw (var voice: OscVoice) ` .. _function-strudel_synth_osc_render_chunk_OscVoice_array_ls_float_gr__array_ls_float_gr__array_ls_float_gr__array_ls_float_gr__int: .. das:function:: osc_render_chunk(voice: OscVoice; output: array; reverb_send_buf: array; delay_send_buf: array; chorus_send_buf: array; chunkFrames: int) Render one chunk of the oscillator voice additively into the output buffer and the reverb/delay/chorus send buffers. Hoists invariants out of the inner loop and dispatches to the per-oscillator-type render function. :Arguments: * **voice** : :ref:`OscVoice ` * **output** : array * **reverb_send_buf** : array * **delay_send_buf** : array * **chorus_send_buf** : array * **chunkFrames** : int .. _function-strudel_synth_osc_voice_init_filters_OscVoice_float_float_float_float: .. das:function:: osc_voice_init_filters(voice: OscVoice; lpf: float; hpf: float; lpq: float; hpq: float) Configure the voice's low-pass and high-pass biquads from Event lpf/hpf cutoffs and lpq/hpq resonances. Cutoffs <= 0 or >= Nyquist leave the corresponding filter inactive. :Arguments: * **voice** : :ref:`OscVoice ` * **lpf** : float * **hpf** : float * **lpq** : float * **hpq** : float .. _function-strudel_synth_osc_voice_init_supersaw_OscVoice: .. das:function:: osc_voice_init_supersaw(voice: OscVoice) Seed the supersaw unison phases with randomised offsets so the detuned voices don't start in phase. :Arguments: * **voice** : :ref:`OscVoice ` ++++++++++++ Vowel filter ++++++++++++ * :ref:`formant_biquad_setup_bpf (var bq: FormantBiquad; freq: float; q: float; sample_rate: float) ` * :ref:`formant_biquad_tick (var bq: FormantBiquad; input: float) : float ` * :ref:`vowel_filter_init (var vf: VowelFilter; vowel: string) ` * :ref:`vowel_filter_tick (var vf: VowelFilter; input: float) : float ` * :ref:`vowel_get_formant_data (vowel: string; var data: FormantData) : bool ` .. _function-strudel_synth_formant_biquad_setup_bpf_FormantBiquad_float_float_float: .. das:function:: formant_biquad_setup_bpf(bq: FormantBiquad; freq: float; q: float; sample_rate: float) Compute bandpass coefficients for the given centre frequency and Q. Deactivates the filter if the cutoff is outside the usable range (near 0 or Nyquist). :Arguments: * **bq** : :ref:`FormantBiquad ` * **freq** : float * **q** : float * **sample_rate** : float .. _function-strudel_synth_formant_biquad_tick_FormantBiquad_float: .. das:function:: formant_biquad_tick(bq: FormantBiquad; input: float) : float Process one sample through the biquad and return the output. :Arguments: * **bq** : :ref:`FormantBiquad ` * **input** : float .. _function-strudel_synth_vowel_filter_init_VowelFilter_string: .. das:function:: vowel_filter_init(vf: VowelFilter; vowel: string) Configure a VowelFilter for the named vowel, deactivating it if the vowel is unknown. :Arguments: * **vf** : :ref:`VowelFilter ` * **vowel** : string .. _function-strudel_synth_vowel_filter_tick_VowelFilter_float: .. das:function:: vowel_filter_tick(vf: VowelFilter; input: float) : float Process one sample through the vowel filter: sum the 5 parallel formant bandpass outputs and apply the makeup gain. :Arguments: * **vf** : :ref:`VowelFilter ` * **input** : float .. _function-strudel_synth_vowel_get_formant_data_string_FormantData: .. das:function:: vowel_get_formant_data(vowel: string; data: FormantData) : bool Look up formant parameters for a vowel name ("a", "e", "i", "o", "u", "ae", "aa", "oe", "ue", "y", "uh", "un", "en", "an", "on"). Returns false if the vowel is unknown. :Arguments: * **vowel** : string * **data** : :ref:`FormantData ` +++++++++++++++ Voice rendering +++++++++++++++ * :ref:`mono_to_stereo (var mono: array\) : array\ ` * :ref:`render_event_stereo (event: Event; duration_sec: float; bank: SampleBank) : array\ ` * :ref:`render_event_stereo (event: Event; duration_sec: float) : array\ ` .. _function-strudel_synth_mono_to_stereo_array_ls_float_gr_: .. das:function:: mono_to_stereo(mono: array) : array Upmix a mono buffer to interleaved stereo by duplicating each sample to both channels. Consumes `mono` (moves and frees it). :Arguments: * **mono** : array render_event_stereo ^^^^^^^^^^^^^^^^^^^ .. _function-strudel_synth_render_event_stereo_Event_float_SampleBank: .. das:function:: render_event_stereo(event: Event; duration_sec: float; bank: SampleBank) : array Render an Event into stereo PCM, preferring a sample from the bank when present and falling back to synthesis. Returns raw stereo — gain and pan are applied later by the scheduler. :Arguments: * **event** : :ref:`Event ` * **duration_sec** : float * **bank** : :ref:`SampleBank ` .. _function-strudel_synth_render_event_stereo_Event_float: .. das:function:: render_event_stereo(event: Event; duration_sec: float) : array