Document:Cycle Sample Generator / Soft Synth
Author:bsp
Date:23-Dec-2024

1 Table of Contents

2 Preface

Cycle is a modular soft-synth which emits TKS or C sample generation code, or STFX voice plugins.
It operates either in hi-fi (32bit float), or lo-fi (8bit signed integer) mode:
While the processing blocks (modules) in Cycle are rather simple, their combination can yield quite complex sounds, even with (by today's standards, slow) CPUs like the Motorola M68000.
In STFX plugin-mode, Cycle generates native-code C voice plugins via the GCC / Clang compiler.
STFX plugins and float sample generation code can also be used in the standalone Synergy replay.

3 Global Keywords

Global keywords may appear at the beginning of a Cycle script.
KeywordDescription
argDeclare argument (will be editable in UI) (shortcut form: a)
arg_valuesArgument preset values (shortcut form: av)
varDeclare variable (shortcut form: v)
oversample_factor
oversample
Oversample factor (0.03125..32, def=1) (also supports fractional values, e.g. "1/8")
caution_s
Oversampling is ignored when exporting to lo-fi 8bit "C" sample generator source

note_s
8 is usually a good factor when targeting ~16kHz samples (=128kHz internal rate)
oversample_qualityOversample quality (0..10, def=4)
caution_s
Oversampling is ignored when exporting to lo-fi 8bit "C" sample generator source
dithery, n, or mode (0=off, 2=dithering, 3=distortion shaping, ..8=adaptive noise shaping)
rateOverride zone sample-rate
freqOutput frequency. Default is 261.63 (middle C, MIDI note 60 / C-5).
alternatively: MIDI note C-1..G-A (followed by finetune, e.g. G-4 -0.2)
alternatively: float ratio, e.g. 16574/64
octOctave shift (-4..4)
durOverride sample duration (frames or, when followed by "ms", milliseconds)
wt_wOverride wavetable width (1..16 in 2D mode, 1..128 in 1D mode)
wt_hOverride wavetable height (1..16 in 2D mode, 1 in 1D mode)
wt_cyclelenOverride wavetable cycle length (number of frames. 1..1048576)
wt_freqsPer-cycle note frequencies/rates (multisampled keymaps)
e.g. wt_freqs c-3 g-3 c-4 "g-4+0.11" c-5 g-5 c-6 g-6 c-7 g-7
note_s
the current cycle freq is available via $wt_freq variable during synthesis
skipSkip output of first "n" sample frames or, when followed by "ms", milliseconds
xfadeCrossfade end of loop with samples before loop start. Sample frames or loop-len percentage (when followed by %)
mirrorCalculate first half of waveform and mirror to second half (single cycle waveforms and wavetables)
curveDeclare curve lookup-table. Must be followed by curve index (0..15) and curve id (e.g. "amp") (shortcut form: c)
lutDeclare procedural lookup-table. Must be followed by LUT id, optional mode (voice/shared), optional table size (default=256), and optional "inv" (inverse) flag
zoneDeclare external zone reference. Must be followed by ref idx (0..) and zone id (e.g. "myzone", or "myothersmp.myzone") (shortcut form: z)
macroDeclare macro parameter set(s). See macros.
incinclude global .cy file (the suffix is auto-appended) OR include patch-local buffer when include name starts with .
Global Keywords

3.1 Plugin keywords

Keywords used in plugin-generation mode:
KeywordDescription
idPlugin id (mandatory)
name
Plugin name (optional. def=<id>)
note_s
the plugin name (and dll / so / dylib) will be prefixed with cycle_
authorPlugin author (optional)
cat
category

Plugin category (optional. def="osc")
unknownlimiterpantransient
ringmodenhancepitchconvolver
waveshaperdacchorusmixer
filterlofiflangercomparator
eqgatedelayutility
compressorampreverbosc
paramDeclare plugin parameter (shortcut form: p)
param_valuesDeclare plugin preset values (shortcut form: pv)
modDeclare plugin modulation target (shortcut form: m)
modparamDeclare plugin parameter and modulation target (param will be prefixed with p_, modulation with m_) (shortcut form: mp)
xincimport native code (C / C++) extension module (e.g. xinc xamp). The file suffix .h is auto-appended.
Plugin Keywords

4 Values and sample output

The same value type is used for module parameters, args, variables, and intermediate sample data.
In hi-fi mode, values are represented by 32 bit floating point numbers. The final sample output is also a float.
In lo-fi mode, values are represented by signed twos-complement, 16 bit fixed point numbers (4:11), i.e. the value range is -15..15.
The final sample output is a signed 8 bit sample.

5 Output lanes

Lanes divide the script into output sections:
<out:
  sin
hello, world.
<layer_1:
  sin

<layer_2:
  sin f=1.5
hello, layered world.
Each lane consists of a list or tree of modules:
<out:
  sin
  abs
  fma
lane module list
<out:
  sin
    phase:
      sin
      * 0.1
  abs
  fma
lane module tree
note_s
fma converts from unipolar (0..1) to bipolar (-1..1) values by default (*2 - 1)
note_s
a patch should contain at least one output section ("lane") (else the output will be 0 / silence)

5.1 Layers

When a script contains multiple lanes, they will be summed to create the final sample output.
This can be used for layered synthesis (lane == layer).

5.2 Init lane

A script may contain an optional init section that is evaluated before the main section.
It can be used to implement procedural macro parameters that read args and write one or many variables.
arg angle 0 0 1

var v_sin
var v_cos

<init:
  sin freq=0 phase=$angle
  sto v_sin
  sin freq=0
    phase:
      $angle
      + 0.25
  sto v_cos
sin / cos init example
note_s
the init section must occur before the first audio lane
note_s
in plugin-mode, the init section is evaluated each time a new note is played (note-on)

5.3 Prepare lane

The prepare section is evaluated before the main section(s), and after the (optional) init section.
Heavy calculations, which do not need to be evaluated per sample frame, should be placed in the prepare section.
id     simple_2op_fm_osc
name   "simple_2op_fm_osc"
author bsp
cat    osc

param p_op2_ratio 0.5 0 1
param p_op2_level 0 0 16

mod m_op2_ratio
mod m_op2_level

var v_op2_freq

<prepare:
  # ratio 0..1 => (1/4096)^3 .. 1 .. 4^3
  $m_op2_ratio
  # 0..1 => -1..1
  fma 2 -1
  # -1=>div 4096, +1=>mul 4
  bts 4096 4
  pow 3
  # quantize to 100 fractional divisions per integer
  qua 100
  sto v_op2_freq

<out:
  sin
    phase:
      sin freq=$v_op2_freq
      * $m_op2_level
simple 2 operator FM / phase modulation oscillator plugin
note_s
in plugin-mode, the prepare section is called at a rate of 1kHz while a voice is playing.

5.4 Wavetable Cycle Init lane

The optional wt_init section is evaluated at the beginning of each wavetable cycle.
It can be used to implement procedural macro parameters that read the built-in $wt_x / wt_y vars and write one or many variables.
arg angle_scl 1 0 15
arg angle_off 0 -1 1

var v_sin
var v_cos

<wt_init:
  sin freq=0
    phase:
      $wt_x
      * $angle_scl
      + $angle_off
  sto v_sin
  sin freq=0
    phase:
      $wt_x
      * $angle_scl
      + $angle_off
      + 0.25
  sto v_cos
sin / cos wavetable cycle init example
note_s
the wt_init section must occur before the first audio lane, and after the (optional) init lane

6 Constants

Constants are typically used for static module input values, e.g.
<out:
  sin freq=1.5
constant input value
A constant is either a floating point number (in the range -15..15), or a note ratio in the form of d#3/g-3:
<out:
  sin freq=d#4/c-3
constant note ratio
tip_s
Many modules have specialized / optimized code generation paths for constant inputs.
Some inputs only accept constant values (marked by const in the module's Inputs documentation).

7 Args

Args are named constants that are declared by the global keyword arg (before any output section).
Arg values are editable in the (auto-generated) script UI (slider + value widgets).
Example:
arg lpf1_ms 30 min=0 max=500
arg lpf1_ms 30 0 500
arg lpf_rng 0.3
declare args
The default (implicit) min / max range is 0..1
Script code can reference args by prefixing the arg id with a $ sign, e.g.
arg amp_lvl 1.5

<out:
  sin
  * $amp_lvl
  clp
declare and read arg constant
Instead of arg, its shortcut form a can be used instead:
a lvl 0.7

<out:
  sin
  * $lvl
  clp
arg decl shortcut form
caution_s
args and variables share the same namespace

7.1 Arg Preset Values

Each arg can be assigned a list of preset values (shown when hold-clicking the value widget).
Example:
arg        angle 0 0 1
arg_values angle 0="0 deg" 0.25="90 deg" 0.5="180 deg" 0.75="270" 1="360 deg"
arg preset values
Instead of arg_values, its shortcut form av can be used instead:
a  mode 0 0 3
av mode 0=red 1=green 2=blue
arg preset values shortcut syntax
note_s
arg preset values always refer to scaled values, i.e. within min..max range

8 Variables

Variables are very similar to args.
While they do not generate UI widgets, their content may be updated at any time via the sto module.
Script code can read variables by prefixing the var id with a $ sign, e.g.
var myvar

<out:
  2.5
  sto myvar
  sin
  * $myvar
  clp
declare and read var
Instead of var, its shortcut form v can be used instead:
v amp

<out:
  2.5
  sto amp
  sin
  * $amp
  clp
variable shortcut syntax
Variables declared within a lane (or a lane within a template instantiation) will be prefixed with the lane id (and a unique template instantiation id).
<out:
  var myvar
  2.5
  sto myvar
  sin
  * $myvar
  clp
lane-local var

8.1 Declare and store var shortcut

<out:
  tri phase=0.25
  vst tri
  + $tri
    hld 0.7
  * 0.5
vst example
see vst

8.2 Smooth interpolation

In plugin mode, variables can be interpolated smoothly over a processing block.
When smooth mode is enabled, the new variable value should be written within the <prepare: lane.
Reading from the variable within an audio lane will returns its interpolated value.
Example:
id smooth_var_test_1

param p_freq 0.5

mod m_freq

var v_freq smooth=1

<prepare:
  $m_freq
  # 0..1 => -1..1
  fma 2 -1
  # -1..1 => 1/4..*4
  bts 4 4
  sto v_freq

<out:
  sin freq=$v_freq
smoothly interpolated variable
caution_s
args and variables share the same namespace

8.3 Built-in Variables

$x stores the current, normalized position in procedural look-up tables (lut)
$wt_x stores the current, normalized horizontal (or 1D) wavetable position (0..1)
$wt_y stores the current, normalized vertical (2D) wavetable position (0..1)

9 Operators

Each module may be prefixed by an operator that determines how its output is combined with the previous module's output.
OperatorIdDescription
=OP_REPReplace
+OP_ADDAdd
+srOP_ADD_SRAdd (over)samplerate-independent value
-OP_SUBSubtract (a-b)
r-OP_RSUBReverse-Subtract (b-a)
*OP_MULMultiply
*srOP_MUL_SRMultiply by (over)samplerate-independent value
&OP_ANDBitwise and
|OP_ORBitwise or
^OP_EORBitwise exclusive-or
&&OP_LANDLogical and (a > 0) & (b > 0)
||OP_LORLogical or (a > 0) | (b > 0)
^^OP_LEORLogical eor (a > 0) ^ (b > 0)
!&OP_LNANDLogical nand !((a > 0) & (b > 0))
!|OP_LNORLogical nor !((a > 0) | (b > 0))
!^OP_LNEORLogical neor !((a > 0) ^ (b > 0))
mOP_MINMinimum
xOP_MAXMaximum
MOP_ABSMINAbsolute minimum
XOP_ABSMAXAbsolute maximum
.OP_NONEEmit but discard result of sub-tree
_OP_SKIPSkip sub-tree (do not emit)
amOP_AMArithmetic mean
qmOP_QMQuadratic mean sqrt(aa+bb)
QMOP_QMUUnipolar Quadratic mean rescaled to -1..1
hmOP_HMHarmonic mean (2*a*b)/(a+b)
HMOP_HMUUnipolar Harmonic mean rescaled to -1..1
gmOP_GMGeometric mean sign(a*b)*sqrt(abs(a*b))
GMOP_GMUUnipolar Geometric mean rescaled to -1..1
Operators
tip_s
The initial output value of a sub-tree is copied from the parent output value.
While this will immediately be overwritten by a new value in most cases, it allows a sub-tree to mix in a processed version of the parent output, e.g. using a dly:
<out:
  sin
  rmp 50 1 0
  pow 3
  + dly n=0.2
    * 0.25
process parent output
tip_s
The _ operator can be used for (temporarily) skipping a sub-tree (e.g. for testing purposes):
<out:
  sin
  + sin f=1.5
    * 0.25
sub-tree enabled
<out:
  sin
  _ sin f=1.5
    * 0.25
sub-tree disabled
The default operator is = (replace):
<out:
  = sin
default op
<out:
  sin
default op short form

9.1 Truth table

Logical operator truth table:
a b  &&   ||   ^^   !&   !|   !^
0 0  0    0    0    1    1    1
0 1  0    1    1    1    0    0
1 0  0    1    1    1    0    0
1 1  1    1    0    0    0    1
truth table
tip_s
can be used for pulse / gate signal math (sequencers)

10 Modules

Modules generate new sample frames or process the output of the previous module.
Static / constant module input values are set by key=value pairs, or simply by value (in internal parameter order)
<out:
  saw
  lpf freq=0.4 res=0.5
  lpf 0.4 0.5
const module input example
Dynamic module input value sub-trees must be placed on separate, indented lines.
Modules thus become nested (stacked), i.e. module inputs can be controlled by the output of other modules:
<out:
  sin
    freq:
      sin 5.0
      * 0.25   # could also be written as  fma 0.25 1
      + 1
  * 3.0
  clp
stacked / dynamic module input example
Please note that some module parameters cannot be dynamic (e.g. hpf filter configuration).
Also keep in mind that dynamic parameters require more complex code that will lengthen the synthesis duration.
tip_s
some module inputs have implicit previous output values, like the pd (phase-distortion) insert of the pha module.
tip_s
in the Cycle machine source editor, press lctrl-i to expand a basic key=value input to (an arbitrarily complex) input sub-tree

10.1 abs

Return absolute value of previous output.

Example

<out:
  -42
  abs
absolute
This is equivalent to full-wave rectification (fwr).
See hwf for half-wave rectification.

10.2 adsr

Analog-style Attack / Decay / Sustain / Release envelope generator.
The previous output should be a gate signal (rising edge restarts envelope, falling edge starts release phase).

Inputs

gate / ggate input (when not connected, use previous output)
a(attack rate) range is 0..1
as(attack shape) range is -16..16
d(decay rate) range is 0..1
ds(decay shape) range is -16..16
s(sustain level) range is 0..1
r(release rate) range is 0..1
rs(decay shape) range is -16..16`
constreset0=reset level only during voice allocation ("analog") 1=reset level when envelope restarts ("digital")
sclsets rate scaling (0..f) (e.g. 0.01=suitable for "prepare" lane (1000Hz rate), 0.001=suitable for audio rate) (default is 0.001)
constmode / mselects envelope flavour. 0=rate-shaping (v2/default, smoother). 1=level-shaping (v1, more percussive)
constvel / venables velocity (gate level) scaling (1=on, 0=off)
onend / oeevaluated when end of envelope is reached
tip_s
it's often sufficient to calculate envelopes in a <prepare: lane (i.e. per 1kHz sample block) instead of per sample frame

10.3 apf

All-pass filter (1st order) previous output.

Examples

arg p_amp 0.7 0 8
arg p_c   0.5 -1 1

<out:
  saw
  apf c=$p_c
  * rmp 0 1
  * $p_amp
  clp
test apf 1 example patch
arg p_amp 0.7 0 8
arg p_f   1.0 0 1

<out:
  saw
  apf f=$p_f
  * rmp 0 1
  * $p_amp
  clp
test apf 2 example patch
arg p_amp 0.7 0 8
arg p_f   1.0 0 1

<out:
  saw
  apf ## dynamic frequency input (=> force float mode)
    f:
      $p_f
      * rmp 0 1
  * rmp 0 1
  * $p_amp
  clp
test apf 3 example patch
id simple_phaser

mp c 0.5
mp ap_mix 0.5

<out:
  $c
  fma #0..1=>-1..1
  vst v_c

  saw
  ipl t=$ap_mix
    b:
      rep 4
        apf c=$v_c
  clp
test apf 4 simple_phaser plugin

Inputs

csets filter coefficient (-1..1)
freq / fsets normalized filter frequency (0..1) (overrides coefficient input)
caution_s
when the freq input is connected and it is not a constant value, the module falls back to float mode (for tan())

10.4 bit

Bit-crush (shift) previous output.
Converts the input to a 16 bit integer and right-shifts it by shift*15 bits.

Examples

<out:
  sin
  bit shift=0.5
  bit 0.5
8 bit reduction
arg p_amp 0.7 0 8
arg p_bit 8 0 15

def bit_linear bit=8
  vst p
  $p
  bit
    shift:
      %bit%
      itg
      * 1/15
  ipl
    b:
      $p
      bit
        shift:
         %bit%
         itg
         fam 1 1/15
    t:
      %bit%
      frc

<out:
  sin
  ~bit_linear bit=$p_bit
  * rmp 0 1
  * $p_amp
  clp
linear bit reduction template
arg p_amp 0.7 0 8
arg p_bit 8 0 15

inc bit_linear

<out:
  sin
  ~bit_linear bit=$p_bit
  * rmp 0 1
  * $p_amp
  clp
linear bit reduction template include

Inputs

shift / srange is 0..1 (0..16 bits)

10.5 boo

Boost sample deltas of previous output.

Example

<out:
  sin
  boo amount=0.6
  boo 0.6
boost sample deltas

Inputs

amount / amtrange is 0..1
constsr1=(over)samplerate-independent amount.def=1

10.6 box

Box filter previous output.
Simple but computationally inexpensive.

Example

<out:
  saw
  box freq=0.5
  box 0.5
box lowpass filter

Inputs

freq / frange is 0..1
constsr1=(over)samplerate-independent filter freq.def=1

10.7 bpf

reserved
tip_s
use svf in mode=bpf instead

10.8 bts

Bipolar to scale.

Example

arg $freq
<out:
  sin
    freq:
      $freq
      # 0..1 => -1..1
      fma 2 -1
      # -1..1 => 1/16 .. *16
      bts 16
bipolar to exponential scale

Inputs

div / drange is 1..32def=8
mul / mrange is 1..32def=8
note_s
uses div input when mul is not connected
caution_s
only supported in float-mode (fall back to float in int mode)

10.9 buf

not implemented, yet

10.10 bus

Read sample from (cross-zone) voicebus.

Examples

id vb_read_prev
param lvl 1 0 1
<out:
   sin
     phase:
       bus 0
       * $lvl
phase-modulate sine wave by previous zone output
id vb_read_1
param lvl 1 0 1
<out:
   sin
     phase:
       bus 1
       * $lvl
phase-modulate sine wave by voice bus 1
id vb_read_nri
# (note) useful range is 0..32%
param bus 0 0  100
param lvl 1 0    1
<out:
   sin
     phase:
       bus nri=$bus
       * $lvl
phase-modulate sine wave by previous zone output (integer bus nr)
id vb_read_nrf
# (note) useful range is 0..32%
param bus 0 0 1
param lvl 1 0 1
<out:
   sin
     phase:
       bus nrf=$bus
       * $lvl
phase-modulate sine wave by previous zone output (float bus nr)

Inputs

nrirange is 0..32 (bus 1..32, 0=previous zone nr)def=1
nrfrange is 0..0.32 (bus 1..32, 0=previous zone nr)def=0.1
caution_s
only supported in STFX plugin mode (returns 0 otherwise)

10.11 ceq

var t
<out:
  $t
  ceq 0
    1
  sto t
ceq example

Inputs

rhsright-hand side value
see cmp

10.12 cge

var t
<init:
  set t 0

<out:
  $t
  +sr 0.0001
  cge 1
    1
  sto t
cge example

Inputs

rhsright-hand side value
see cmp

10.13 cgt

var t
<init:
  set t 0

<out:
  $t
  +sr 0.0001
  cgt 1
    1
  sto t
cgt example

Inputs

rhsright-hand side value
see cmp

10.14 cle

var t
<init:
  set t 0

<out:
  $t
  cle 0.5
    +sr 0.001
  sto t
cle example

Inputs

rhsright-hand side value
see cmp

10.15 clk

Generate clock pulse width configurable duty cycle duration.

Example

id clk_test
<out:
  sin
  * clk div=1/4
    adsr
modulate sine level by adsr env which is retriggered every 16th note

Inputs

hzrange is 0..f (clock ticks per second)
bpmrange is 0..f (0=host bpm, beats per minute (quarter notes) otherwise. used when bpm is not connected)def=0.0
divrange is 0..f (clock divider. <1 speeds up clock, >1 slows it down)def=1.0
durduty cycle duration in millisecondsdef=5
constforce / f1=emit 0 at end of duty cycle when cycle duration is longer than intervaldef=1
restart / rsrestart clock counter on rising edge
swing / sw<0 pull every 2nd pulse >0 push/delay every 2nd pulsedef=0.0
onclk / ocevaluated on each clock pulse. must return new pulse level (0..1, 0 discards pulse)

10.16 clp

Clip previous output

Examples

<out:
  sin
  * 7
  clp
clip to -1..+1
<out:
  sin
  * 7
  clp 0.5
clip to -0.5..+0.5
<out:
  sin
  * 7
  clp ceil=0.6 floor=-0.75
separate boundaries
<out:
  sin
  * 7
  # same as clp ceil=0.6 floor=-0.75
  clp 0.6 -0.75
separate boundaries (short form)

Inputs

ceil / crange is 0..1 (default=1)
floor / frange is -1..1 (default=-1) (when not connected, floor = -ceil)

10.17 clt

var t
<init:
  set t 1

<out:
  $t
  +sr -0.001
  clt 0
    0
  sto t
clt example

Inputs

rhs / rright-hand side value
see cmp

10.18 cmp

Compare previous output value to right-hand side value and run statement sequence when result is true.
Supported operators: <, <=, ==, !=, >=, >.
var t
<init:
  set t 1

<out:
  $t
  +sr -0.001
  cmp < 0
    0
  sto t
cmp example
id cmp_test_2
var t
<out:
  tri
  fma 0.6 0.4
  sto t
  sin
  clt
    rhs:
      $t
      neg
    -1
  cgt $t
    1
  box 0.8
cmp example plugin
id test_cmp_else_1

<prepare:
  tri
  clt 0
      -1
    else:
      1
  trc

<out:
  sin
if / else example

Inputs

rhs / rright-hand side value
elseevaluated when comparison failed
tip_s
see clt, cle, ceq, cne, cge, cgt short cut modules

10.19 cne

var t
<out:
  $t
  cne 1
    1
  sto t
cne example

Inputs

rhsright-hand side value
see cmp

10.20 con

reserved

10.21 dec

Decrement variable
If the delta input is not connected, decrement variable by previous output value.
var f
<out:
  set f 0
  rep 16
    dec f 0.1
    dec f delta=0.1
    dec f
      delta:
        0.05
        * 2
decrement variable
var f
<out:
  set f 0
  0.1
  dec f
decrement by 0.1

Inputs

deltawhen connected, decrement variable by given value. when not connected, decrement by previous output value.

10.22 div

Divide values.

Example

arg p_num 16 1 32
<out:
  div 1.0 $p_num
output 1 / $p_num

Inputs

anominator input
bdenominator input
tip_s
useful in conjunction with rep / lop, e.g. when indexing LUTs
caution_s
this is a rather expensive operation in integer / lo-fi mode (uses iterative approximation when inputs are not constant)

10.23 dly

Store previous output value in delay ring buffer read delayed value.
If the freq input is connected, the module is set to tuned-delay-line mode (key follow) and the input value determines the delay length scaling (e.g. 2.0 doubles the length and thus transposes the delay one octave down).

Examples

arg p_amp 0.7 0 2

<out:
  sin
  * rmp 50 1 0
    pow 3
  + dly len=0.08 fb=0.5
    * 0.25
  * $p_amp
  clp
dly test 1
# test feedback insert
arg p_amp 0.7 0 2

<out:
  saw
  * rmp 100 1 0
    pow 3
  svf r=1
    f:
      rmp 70 1 0
      pow 3
      p2s
      fma 0.8 0.2
  + dly size=32768 len=0.2 fb=0.5
      read:
        svf f=0.1 r=0.6
    * 0.25
  * $p_amp
  clp
dly test 2
# tuned delay line test
skip 180
oct  0

arg p_amp     0.7  0   2
arg p_scl     1    0   4
arg p_fb      0.67 0   1
arg p_ins_flt 0.1  0   1
arg p_ins_res 0.64 0.1 1
arg p_mix     0.9  0   1

<out:
  fsr
  * rmp 10 1 0
    pow 3
  * 1.0
  svf r=0.5
    f:
      rmp 50 1 0
      pow 3
      p2s
      fma 0.5 0.5
  ipl t=$p_mix
    b:
      dly s=1024 fb=$p_fb
        f:
          $p_scl
        r:
          svf r=$p_ins_res
            f:
              $p_ins_flt
              p2s
  * $p_amp
  #hbx f=0.7
  clp
dly test 4
# tuned delay line voice plugin test
id test_dly_stfx_4

mp dly_scl  0.224
mp dly_fb   0.889
mp dly_mix  0.987
mp osc_lpf  0.919
mp osc_res  1.0
mp dly_lpf  0.228
mp dly_res  1.0
mp osc_lvl  1.0

<out:
  fsr reset=0
  * $m_osc_lvl

  svf r=$m_osc_res
    f:
      $m_osc_lpf
      kbd mode=1 amt=1

  ipl
    t:
      $m_dly_mix
      clp 1 0
    b:
      dly s=8192 fb=$m_dly_fb lin=1
        f:
          $m_dly_scl
          fma 2 -1
          bts 16 16
        read:
          svf
            r:
              $m_dly_res
            f:
              $m_dly_lpf
              kbd mode=1 amt=1
  clp
dly test stfx 4

Inputs

constsize / smaximum delay buffer size in framesdef=32768
len / ncurrent delay buffer length (0..1 ⇒ 0..size)def=0.5
freq / ftuned delay line rate divider (1=note speed, 0.5=1 oct up, ..) (when connected)
feedback / fbfeedback amount (0..1)def=0
constlin / l1=linear interpolation 0=nearest neighbour)def=0 (nearest)
read / rdelay-read insert (pre feedback)
write / wdelay-write insert (post feedback)
note_s
in lo-fi / integer mode, the delay buffer size is limited to 32767 frames
note_s
the delay size is multiplied by the oversample factor
note_s
linear interpolation is only supported in hi-fi / float mode
see xdly

10.24 drv

reserved

10.25 end

Close current lane.
note_s
optional. can be used for placing lut declarations in between lanes.
note_s
when lane is a lut init lane, skip the output write (lane must write to lut via sta instead)

10.26 env

Generates a three-segment (Attack/Decay, Sustain, Release) envelope.
Previous output is gate signal (rising edge (re-)starts envelope, falling edge starts release phase).
The subtree takes the current normalized envelope position (0..1) as input and must return the envelope level for that position.

Example

id env_1
# (note) please map "Gate" to plugin mod "gate" in the mod matrix or the envelope won't trigger
mod m_gate
# Bezier curve 0 stores the envelope shape
curve 0 env
<out:
  sin
  * $m_gate
    env ad=0.07 s=0.4 r=0.01 scl=0.001
      # subtree returns env level for normalized position input 0..1
      lut env lin=1 clamp=1
env example

10.26.1 loop

When loop mode is enabled, the gate-off signal is ignored and the envelope will repeat continously, switching ad / r rates at the sustain position.
This can (also) be used for audio-rate waveform synthesis.
tip_s
scale the rates by kbd mode=2 amt=1 for chromatic key tracking

Example

id env_3
oversample 4

modparam ad 0.5 0 1
modparam s 0.5
modparam r 0.5

# Bezier curve 'env' stores the envelope shape
curve env

var v_ad  smooth=1
var v_s   smooth=1
var v_r   smooth=1

<prepare:
  $m_ad
  clp 1 0
  pow 3
  kbd mode=2 amt=1
  sto v_ad

  $m_s
  sto v_s

  $m_r
  clp 1 0
  pow 3
  kbd mode=2 amt=1
  sto v_r

<out:
  sin
  * 1 # gate
    env ad=$v_ad s=$v_s r=$v_r scl=0.01 loop=1
      lut env lin=1 clamp=1
    # slw 0.05 0.1
looping audio-rate envelope

Inputs

adattack/decay rate (0..1)def=0.01
ssustain position (0..1)def=0.5
rrelease rate (0..1)def=0.01
constloop / lp1=loop 0=oneshot / halt at sustain posdef=0 (off)
restart / rsrestart signal
srenable sample-rate independent envelope speed (must be const) (voice plugins only)def=1 (on)
onend / oeevaluated when end of envelope is reached / envelope loops
sclenvelope rate scaling (0..1)def=0.001
consttuned / tunnormalize envelope rates and follow pitch (auto-enables loop mode)def=0 (off)

10.27 eq3

A simple 3-band equalizer
arg p_amp 0.7 0 8

arg fl 0.1 0 1
arg fh 0.2 0 1
arg gl 0.5 -2 2
arg gm 0.5 -2 2
arg gh 0.5 -2 2
arg eq3_amt 1 0 1
arg p_amp_eq 1.5 0.5 4

<out:
  saw
  * $p_amp
  ipl t=$eq3_amt
    b:
     eq3 freqlo=$fl freqhi=$fh gainlo=$gl gainmid=$gm gainhi=$gh
     * $p_amp_eq
  clp
eq3 test 1 example

Inputs

freqlo / flLow frequency (0..1)def=0.5
freqhi / fhHigh frequency (0..1)def=0.5
gainlo / glLow Gain (negative values invert phase)def=0.5
gainmid / gmMid Gaindef=0.5
gainhi / ghHigh Gaindef=0.5
constsr1=(over)samplerate-independent freqlo/freqhi.def=1

10.28 fed

Detect logic signal falling edge and evaluate sub-tree.
id edge_detect

var v_out
var v_dcy

<out:
  pul
  red
    set v_dcy 0.999
    set v_out 1
  fed
    set v_dcy 0.95
  $v_out
  *sr $v_dcy
  sto v_out
  fma 2 -1
rising / falling edge detect osc
note_s
this module has no inputs
see also: red

10.29 fix

Replace NaN (Not a Number) and Infinity values by 0.
This should not be necessary in regular patches but may be useful for debugging purposes.

10.30 fld

Fold previous output value at ceiling.

Examples

<out:
  sin
  * 2.0
  fld ceil=1.0
fold sine oscillator
note_s
ceil sets min=-ceil, max=+ceil
<out:
  sin
  * 2.0
  fld min=-1.0 max=1.0
fold sine oscillator (separate min / max)
note_s
min / max override default ceil

Inputs

ceil / csets min=-val, max=+valdef=1.0
min / mminimum value
max / xmaximum value
consthq1=enable high-quality (float) mode (arbitrary number of folds).def=1 (on)
see also: wrp

10.31 fam

Fused add multiply: out = (out + add) * mul

Examples

<out:
  sin
  # -1..+1 => 0..1
  fam
bipolar to unipolar example
arg exp 1 0 16

<out:
  sin
  fam # -1..+1 => 0..1
  pow $exp
  fma # 0..1 => -1..+1
test_fam_1 example patch

Inputs

add / ainput is in range -15..15def=1.0
mul / minput is in range -15..15def=0.5

10.32 fma

Fused multiply add: out = out * mul + add

Example

<out:
  sin
  # -1..+1 => 0..1
  fma 0.5 0.5
  # 0..1 => -1..+1
  fma
fused multiply add
tip_s
by default, fma converts unipolar values to bipolar (0..1 ⇒ -1..1) while fam converts bipolar to unipolar (-1..1 ⇒ 0..1)
<out:
  sin
  fam ## to unipolar
  pow
  fma ## back to bipolar
bipolar ↔ unipolar

Inputs

mul / minput is in range -15..15def=2.0
add / ainput is in range -15..15def=-1.0

10.33 frc

Calculate fractional part of previous output (discard integer bits)

Examples

<out:
  1.67
  frc      # => 0.67
  trc

  -1.67
  frc      # => 0.33
  trc

  -1.67
  frc s=1  # => -0.67
  trc
unsigned and signed fractional parts
<out:
  sin
  * 3.3
  frc s=1
test frc 1 example patch

Inputs

constsigned / s0=unsigned mode 1=signed modedef=0 (unsigned)
tip_s
the (default) unsigned mode is especially useful for oscillator phases (i.e. to wrap any value into the normalized 0..1 range)
note_s
itg does the opposite and discards all fractional bits

10.34 fsr

Generate linear feedback shift register ("C64") noise (normalized to -1..1).

Examples

<out:
  fsr
noise
id noise
<out:
  fsr seed=$4489 bits=1.0 reset=0
noise voice plugin with custom seed and no note-on reset

Inputs

constseed / sis the initial shift register state (16 bits). must not be 0.def=$4489
constbits / bdetermines the number of output bits (1.0=all bits, 0=1 bit (actually two, sign+msb).def=1.0
constcycle / cydetermines whether the shift register state resets at each (wavetable) cycle. Ignored in plugin mode.def=1 (true)
constshift1 / sh1is the first feedback shift (negative=shift left). -15..15.def=7
constshift2 / sh2is the second feedback shift. -15..15.def=-9
constshift3 / sh3is the third feedback shift. -15..15.def=13
constreset / rsrange is 0..1 (0=disable plugin voice note on reset and eval only when voice is allocated)def=1
constsigned / sgn1=generate signed values (-1..1) 0=generate unsigned values (0..1)def=1
note_s
changing the number of bits does not make an audible difference, except when the output is used as a controller value
note_s
fsr noise contains less low frequencies than nos noise

10.35 fwr

Discard sign of previous output and optionally rescale to -1..1 range.

Examples

<out:
  -0.8
  fwr   # ==> 0.8
  0.9 b=1
  fwr   # ==> 0.8  (== 0.9*2-1)
uni- and bipolar full wave rectification
id fwr_test_1

mp amp 0.5

<out:
  sin
  fwr b=1  ## bipolar mode scales 0..1 output to -1..1
  * $m_amp
    * 2
  clp
fwr_test_1 voice plugin example
See hwr for half-wave rectification.

Inputs

constbipolar / brange is 0/1 (1=rescale to -1..1 range)def=0 (unipolar)
constneg / nrange is 0/1 (1=flip output sign)def=0 (positive)

10.36 hbx

High-pass box filter previous output.
Simple but computationally inexpensive.

Example

<out:
  saw
  hbx freq=0.5
  hbx 0.5
high-pass box filter

Inputs

freq / frange is 0..1def=0.5
constsr1=(over)samplerate-independent freq.def=1 (on)

10.37 hwr

Clip previous output to 0..f and optionally rescale to -1..1 range.

Examples

<out:
  -0.8
  hwr   # ==> 0
  0.9 b=1
  hwr   # ==> 0.8  (== 0.9*2-1)
half-wave rectification
id hwr_test_2_max
<out:
  sin
  vst a
  sin phase=0.1 f=2.01
  vst b
  $a   # max(a,b) = (b+hwr(a-b))
  - $b
  hwr
  + $b
max
id hwr_test_3_min
<out:
  sin
  vst a
  sin phase=0.1 f=2.01
  vst b
  # min(a,b) = (a-hwr(a-b))
  $a
  - $b
  hwr n=1   # -hwr
  + $a
min
See fwr / abs for full-wave rectification.

Inputs

constbipolar / brange is 0/1 (1=rescale to -1..1 range)def=0 (unipolar)
constneg / nrange is 0/1 (1=flip output sign)def=0 (positive)

10.38 hld

Sample and hold previous output.

Example

<out:
  hld rate=0.5
sample and hold

Inputs

rate / rrange is 0..1 (⇒ 0..samplerate)def=1.0
constsr1=(over)samplerate-independent freq. 0=fixed freq.def=1 (on)
onsample / osevaluated when a new sample is taken. input is previous output, output is new sample&hold value. does not modify previous output.
note_s
when the previous output uses complex calculations, it is more efficient to render these into a separate zone at a lower rate, then read the precalced values via zon

10.39 hpf

High-pass filter previous output. Uses preconfigured, static cutoff/resonance configurations (biquad coefficients).

Example

<out:
  saw
  hpf cfg=3
high-pass filter

10.39.1 Filter configurations

cfgcutoffresonance
01270
1127108
21220
3122108
41150
5115100
61100
7110100
81000
910097
10900
119070
12770
137779

Inputs

constcfg / cselects filter config (0..13)def=0

10.40 if

Conditional code generation (compile time).
Syntax:
  if <arg> [<,<=,==,!=,>=,>] <constant>
    then:
       ..
    else:
       ..

Example

arg mod_waveform;

<out:
  if mod_waveform == 0
    then:
      nos
    else:
      if mod_waveform < 0.33
        then:
          sin
        else:
          if mod_waveform < 0.66
            then:
              tri
            else:
              saw
compile time if / else cascade

Inputs

thenevaluated when expression is true
elseevaluated when expression is false

10.41 inc

Increment variable
If the delta input is not connected, increment variable by previous output value.

Examples

var f
<out:
  set f 0
  rep 16
    inc f 0.1
    inc f delta=0.1
    inc f
      delta:
        0.05
        * 2
increment using delta input
var f
<out:
  set f 0
  0.1
  inc f
increment by 0.1

Inputs

deltawhen connected, increment variable by given value. when not connected, increment by previous output value.

10.42 int

Evaluate sub-tree in integer mode.
The previous output value is converted to 16bit fixpoint (1:4:11) before the sub-tree is emitted. Afterwards, the integer output value is converted back to float.

Example

<out:
  int
    sin
integer sub-tree
note_s
in integer mode, no conversions take place and the sub-tree is emitted as usual

10.43 ipl

Interpolate from a to b at time t.
The module behaves differently depending on which inputs are connected:

Examples

arg a_blend 0 0 1
<out:
  ipl win=smoothstep
    a:
      sin
    b:
      tri
    t:
      $a_blend
blend between sine and triangle using a smoothstep window
id osc_vsync

param p_vsync       0 0 1
param p_window_amt  1 0 1
param p_window_freq 0 0 1

mod m_vsync
mod m_window_amt
mod m_window_freq

<out:
  sin
    vsync:
      # 0..1 => 1..16
      $m_vsync
      fma 15 1
  # interpolate between sine wave and 0 using a triangle window
  #  (note) the 'a' input of the 'ipl' module is the previous output (sin)
  #          (since 'a' is not explicitily connected)
  ipl b=0
    t:
      1.0
      - tri
         freq:
           # 0..1 => 1..16
           $m_window_freq
           fma 15 1
           # to integer
           itg
        # -1..1 => 0..1
        fma 0.5 0.5
      * $m_window_amt
virtual oscillator sync voice plugin that uses a triangle window with customizable frequency

Inputs

asource valuedef=0.0
bdestination valuedef=1.0
tinterpolation amountdef=1.0
constwininterpolation window. 0=linear, 1=smoothstep, 2=in_quad, 3=out_quad, 4=in_cubic, 5=out_cubic, 6=in_quintic, 7=out_quinticdef=0 (linear)
tip_s
The initial input to a, b, t (mode-dependent) is the previous output. This can be used to crossfade between processed versions of the input signal.

10.44 itg

Calculate integral part of previous output (discard fractional bits)

Example

<out:
  sin
  * 16
  itg
  * 1/16
cast to integer
note_s
frc does the opposite and discards all integer bits

10.45 kbd

mode=0:
mode=1:
mode=2:

Examples

id     cutoff_kbd_tracking_test
name   "cutoff kbd tracking test"
author bsp
cat    filter

param p_cutoff 0.5 0 1
param p_keyfollow 1 0 1
param p_octave_off 0 -4 4

<out:
  svf
    freq:
      $p_cutoff
      kbd $p_keyfollow $p_octave_off
filter cutoff keyboard tracking example plugin
id     pitch_kbd_tracking_test
name   "pitch kbd tracking test"
author bsp
cat    osc

param p_keyfollow 1 0 1
param p_octave_off 0 -4 4

<out:
  sin fixed=1
    freq:
      1.0
      kbd mode=1 amt=$p_keyfollow off=$p_octave_off ctr=60
oscillator pitch keyboard tracking example plugin

Inputs

amt / ais in the range -16..16 (keyboard follow amount)def=1.0
off / ois in the range -4..4 (octave offset)def=0.0
scl / sis in the range 0..16 (post scaling)def=4.0
constclamp / ctoggles clamp-to-1.0 (should be set to 0 when using this for pitch tracking)def=1 (on)
constmode / mselects mode (0=filter 1=eqtemp 2=lin_exp)def=0 (filter)
constctrsets keyboard center note (only used in pitch=1 mode)def=60 (C-5)
caution_s
only supported in plugin / float mode (fall back to float within int-subtree)

10.46 lle

Scale previous output by logarithmic(c<0)..linear(c=0)..exponential(c>0) factor

Example

<out:
  8
  lle c=1.4
  trc
log / lin / exp

Inputs

cis in the range -f..f.def=0.0
caution_s
only supported in float-mode (fall back to float in int mode)

10.47 log

Return logarithm of previous output.

Example

<out:
  8
  log base=2
  trc
calc log2(8)

Inputs

base / brange is 1..f. When not connected, return the natural logarithm.
caution_s
only supported in float-mode (fall back to float in int mode)

10.48 lop

Loop statement sequence. Supports dynamic loop iterators.
<out:
  2.0
  * 4.0
  vst num
  0.0
  vst i
  lop $num
    + 0.01
dynamic loop

Inputs

num / nrange is 0..1023 (0..15 in integer / lo-fi mode)def=1
caution_l
some modules (e.g. sin) use variables to store their state. evaluating such modules in a loop will cause all iterations to use the same state.
tip_l
use rep to generate unrolled loops where each iteration will have its own state

10.49 lpf

1RC resonant low pass filter.
Simple but computationally inexpensive.

Example

<out:
  saw
  lpf freq=0.4 res=0.5
  lpf 0.4 0.5
low-pass filter

Inputs

freq / frange is 0..1def=0.5
res / rrange is 0..1def=0.5

10.50 lut

Use previous output as index for curve look-up table (LUT).
The first argument is either the name of a curve, or the name of a plugin array param that is associated with a curve.

Example

curve amp

<out:
  sin
  * rmp 0 0 1
    lut amp clamp=0 lin=0
cubic bezier curve lookup-table

Inputs

clamp / cmust be either 0 (repeat lut) or 1 (clamp to last index)
lin / lmust be either 0 (nearest neighbour) or 1 (linear interpolation)
scl / sscales the index by a float number (hifi mode) or clamps to the index before last (lofi mode, when scl != 1.0) (at compile time). This can be used to avoid (costly) index clamping in linear filtered mode by using wrap mode instead (clamp=0) and scaling the index by e.g. scl=31/32 (when LUT / curve has 32 elements) to avoid filtering the last with the first element when the index approaches 1.0.
constbipolar/benable bipolar index mode (flip sign of index+output as required)
note_s
the lut module is often combined with a rmp or pha module that generates the look-up index

10.51 mac

The mac module updates the current, interpolated values of a macro's fields.
See macros for further information and examples.

Inputs

pos / prange is 0..1 (last setting is lerped back to first setting)def=0.0

10.52 map

Remap previous output value using a non-contiguous table.

Inputs

constmode / m0=nearest 1=match 2=up 3=down 4=ipl/lindef=0 (nearest)
defwhen connected, emit default value when value could not be mapped
ipldwhen connected, distort interpolation t (when mode=ipl) (in=0..1, out=0..1)
See Maps for further info and example code.

10.53 mkv

A dual state, probability driven generator that is not just useful for weather forecasts but also for sound synthesis.
Two probabilities ab and ba determine the chances of transitioning from state a to b (ab) or from state b to a (ba).
See markov_bd_1, markov_sd_1, markov_hh_1 example patches.

Inputs

ainput Adef=0.0
binput Bdef=1.0
rrandom input. use previous output value when not connected.
abA => B probability (0..1)def=0.5
baB => A probability (0..1)def=0.5
onbevaluated when entering "b" state
onaevaluated when entering "a" state
act1=always evaluate a and b input. 2=evaluate only during state transitions (initial state is floating)def=1 (always eval a + b)

10.54 mul

Multiply variable
If the factor/f input is not connected, multiply variable by previous output value.

Examples

var f
<out:
  set f 1.0
  rep 16
    mul f 0.5
    mul f factor=0.5
    mul f
      factor:
        0.5
multiply variable by input
var f
<out:
  set f 2.0
  0.5
  mul f
multiply variable by previous output

Inputs

factorwhen connected, multiply by input instead of previous output value

10.55 neg

Negate previous output

Example

<out:
  saw
  neg
flip phase

10.56 nos

Render white noise via xor-shift pseudo random number generator

Example

<out:
  nos seed=0x44894489 cycle=0
xor-shift noise

Inputs

constseed / sis the random seed (a 32bit integer).the default seed is 0x3d9fb971
constcycle / cyrange is 0/1 (0=disable wavetable cycle reset)def=1 (on)
constreset / rsrange is 0/1 (0=disable plugin voice note on reset)def=1 (on)

10.57 not

Invert previous logic signal output

Examples

<out:
  1
  # 1 => 0
  not
  # 0 => 1
  not
invert previous output
var v_state

<out:
  # invert the boolean state of the given variable
  not v_state
invert boolean variable

10.58 note

Returns the current voice note number (0..127).

Example

id note_test_1
<out:
  sin
    freq:
      0.1
      + note
        * 1/32
note example
note can also be used in short form value assignments to (dynamic) inputs.
note_s
only available in voice plugin mode. in sample synth mode, this simply returns 60.0 (middle-C).

10.59 nth

Discards all clock signals except for every nth one.

Examples

<out:
  clk div=1/4
  nth 4
pass every 4th 16th note clock signal (e.g. a base drum)
<out:
  clk div=1/4
  nth 8 shift=-4
pass every 8th 16th note clock signal, starting with the 4th one (e.g. a snare drum)

Inputs

constevery / epass every nth pulse (const integer)def=1
constshift / sshift pulse counter (<0 delay first passed pulse)def=0
restart / rsreset pulse counter on rising edge
onclk / ocrun statement block when duty cycle begins. must return pulse level scaling (0..1, 0 discards pulse)

10.60 out

Write sample to (cross-zone) voicebus.

Examples

id vb_out_test
param lvl 1 0 1
<out:
  sin
  out 1
write sine output to voice bus 1

Inputs

nrirange is 1..32 (bus 1..32)def=1
nrfrange is 0.1..0.32 (bus 1..32)def=0.1
caution_s
only supported in STFX plugin mode

10.61 pha

Generate phase output (0..1 normalized saw-wave).

Example

curve mywaveshape

<out:
  pha freq=1.5 phase=0.25
  lut mywaveshape
phase table lookup (single-cycle bezier wave)

Inputs

freq / frange is 0.25..4 (1.0=middle C in fixed frequency mode)def=1.0
phase / phrange is 0..1 (start phase)def=0.0
vsync / vsrange is 0.01..4 (virtual oscillator sync)def=1.0 (off)
constcycle / cyrange is 0/1 (0=disable wavetable cycle reset)def=1 (on)
constreset / rsrange is 0/1 (0=disable plugin voice note on resetdef=1 (on)
constfixed / fxrange is 0..1 (1=enable fixed frequency mode)def=0 (follow kbd)
phasemod / pmrange is 0..1 (post vsync phase modulation)def=0.0 (off)
pdphase distortion insert (subtree). input is phase (0..1), output is new phase (0..1). only supported in float/hi-fi mode.

10.62 p2s

Return (pow(2, exp * prev_out) - 1.0) / (pow(2, exp) - 1.0).
Can be used for e.g. scaling filter cutoff frequencies (linear to exponential).

Examples

arg cutoff

<out:
  svf
    freq:
      $cutoff
      p2s 7
(non-kbd-tracked) exponential cutoff scaling
When the kbd (and optionally the off) input is connected and Cycle is in STFX plugin-generation mode, the module input follows the keyboard.
id     cutoff_kbd_tracking_test_p2s
name   "cutoff kbd tracking test p2s"
author bsp
cat    filter

param p_cutoff     0.7   0 1
param p_keyfollow  1.0   0 1
param p_octave_off 0.0  -4 4

<out:
  saw
  svf sr=0
    freq:
      $p_cutoff
      p2s kbd=$p_keyfollow off=$p_octave_off
filter cutoff keyboard tracking example plugin

Inputs

exp / erange is 0..15def=7.0
constscl / srange is 0..1 (0=>scl=(pow(2,exp)-1))def=0.0
kbd / krange is -16..16 (keyboard follow amount)def=0.0
off / orange is -4..4 (octave offset)def=0.0
caution_s
when kbd is connected (in plugin mode), this factors in the current samplerate and oversample factor. caution when passing the output to another module which also does this !
caution_s
only supported in float-mode (fall back to float in int mode)
tip_s
for keyboard tracking purposes, the kbd module is usually the preferred solution

10.63 pow

Return previous output raised to the power of 2 or 3 (fast-path), or an arbitrary exponent (float-mode only).

Examples

<out:
  sin
  pow 3
pow fastpath
<out:
  pow
    base:
      2.71828182845904523 # e
    exp:
      sin
generic pow with base input connected

Inputs

exp / erange is [2, 3] (int) or any float valuedef=2.0
base / brange is any float val. when not connected, use previous output as base.def=2.0
tip_s
useful for bending rmp envelopes into non-linear shapes
caution_s
constant exponents 2 and 3 behave differently than arbitrary (or dynamic) exponents, i.e. -2²=+4 while e.g. -2^4=-16
caution_s
arbitrary or dynamic exponents are only supported in float-mode (fall back to float in int mode)

10.64 pre

This is a dummy (no-op) module that simply returns the previous output (no extra code generated).
caution_s
pre must be the first module in a template or module input that reads the previous output via an operator and a template call, e.g.
def myrev
  + ~mydly
not working
..must be written as:
def myrev
  pre
  + ~mydly
working
This is currently required due to a parser-issue which may be proper-fixed later.
see also: Templates / pre

10.65 pul

Generate pulse wave

Example

<out:
  pul freq=1 phase=0 width=0.5 vsync=2.5
vsynced pulse (rectangle) oscillator

Inputs

freq / frange is 0.25..4 (1.0=middle C in fixed frequency mode)def=1.0
phase / phrange is 0..1 (start phase)def=0.0
width / wrange is 0..1 (length of duty cycle)def=0.5
vsync / vsrange is 0.01..4 (virtual oscillator sync)def=1.0 (off)
constcycle / cyrange is 0/1 (0=disable wavetable cycle reset)def=1 (on)
constreset / rsrange is 0/1 (0=disable plugin voice note on reset)def=1 (on)
constfixed / fxrange is 0/1 (1=enable fixed frequency mode)def=0 (follow kbd)
window / winrange is 0..8 (0=off/none(windowed vsync off), 1=sin(sine window), 2=hs(half-sine window), 3=tri(triangle window), 4=hse1(hse1_36), 5=hse2, 6=hse3, 7=hse4, 8=hse5) (must be const OR return amp factor (see below))
phasemod / pmrange is 0..1 (post vsync phase modulation)def=0.0 (off)
pdphase distortion insert (subtree). input is phase (0..1), output is new phase (0..1). only supported in float/hi-fi mode.
caution_s
windowed vsync is only supported in float-mode (ignored in integer mode)
note_s
when window input is not constant, it is treated as an insert which receives the window phase and returns the window amplification factor (0..1). I.e. this can be used to implement custom window functions.

10.66 qua

Quantize float value to n fractional steps per integer.

Example

<out:
  sin
  # reduce 32bit -1..1 float to ~4.32 bits
  #   (.., -0.2, -0.1, 0.0, 0.1, 0.2, ..)
  qua num=10
quantize

Inputs

num / nrange is 1..ndef=32
tip_s
useful for controllers, e.g. FM ratios
tip_s
when num=1, simply discard fractional bits (cast to integer)
caution_s
only supported in float-mode (fall back to float in int mode) (except when num=1)

10.67 rcp

Calculate reciprocal (1/x) of previous output.

Example

arg p_sin_scl 1.5 1 4

<out:
  sin
  * $p_sin_scl
  rcp
  clp
  * tri ph=0.25 ## fade-out infinity
    abs
  * $p_sin_scl
test rcp 1 example patch

10.68 rdl

Read left channel input sample.
caution_s
available only in plugin mode.

10.69 rdr

Read right channel input sample.
caution_s
available only in plugin mode.

10.70 red

Detect logic signal rising edge and evaluate sub-tree.

Example

id edge_detect

var v_out
var v_dcy

<out:
  pul
  red
    set v_dcy 0.999
    set v_out 1
  fed
    set v_dcy 0.95
  $v_out
  *sr $v_dcy
  sto v_out
  fma 2 -1
rising / falling edge detect osc
note_s
this module has no inputs
see also: fed

10.71 rep

Loop / unroll statement sequence.
important_s
The number of iterations must be constant.

Example

arg p_num 8 1 32
var i
<out:
  1.0
  sto i
  0.0
  rep $p_num
    + sin
        freq:
          $i
          * 0.5
      * 0.1
  clp
unrolled loop

Inputs

constnum / nrange is 0..1023def=1

10.72 rev

reserved

10.73 rmp

Generate linear ramp

Example

<out:
  rmp millisec=8 start=0 end=1 cycle=1
linear 8ms ramp
<out:
  rmp 0 0 1
linear ramp from 0..1 that spans entire sample

Inputs

constmillisec / msrange is 0..4000. 0 auto-adjusts the duration to match the target zone sample lengthdef=200
conststart / srange is 0..15 (start value)def=1.0
constend / erange is 0..15 (end value)def=0.0
constcycle / cydetermines whether the ramp position is reset at each new wavetable cycle (0 or 1)def=1 (on)

10.74 saw

Generate sawtooth wave

Example

<out:
  saw
sawtooth oscillator
oversample 2
<out:
  saw freq=2 phase=0 vsync=2.5
vsynced sawtooth oscillator (second harmonic)

Inputs

freq / frange is 0.25..4 (1.0=middle C in fixed frequency mode)def=1.0
phase / phrange is 0..1 (start phase)def=0.0
vsync / vsrange is 0.01..4 (virtual oscillator sync)def=0.0 (off)
cycle / cyrange is 0/1 (0=disable wavetable cycle reset)def=1 (on)
constreset / rsrange is 0/1 (0=disable plugin voice note on reset)def=1 (on)
constfixed / fxrange is 0/1 (1=enable fixed frequency mode)def=0 (follow kbd)
window / winrange is 0..8 (0=off/none(windowed vsync off), 1=sin(sine window), 2=hs(half-sine window), 3=tri(triangle window), 4=hse1(hse1_36), 5=hse2, 6=hse3, 7=hse4, 8=hse5) (must be const OR return amp factor (see below))
phasemod / pmrange is 0..1 (post vsync phase modulation)def=0.0 (off)
pdphase distortion insert (subtree). input is phase (0..1), output is new phase (0..1). only supported in float/hi-fi mode.
caution_s
windowed vsync is only supported in float-mode (ignored in integer mode)
note_s
when window input is not constant, it is treated as an insert which receives the window phase and returns the window amplification factor (0..1). I.e. this can be used to implement custom window functions.

10.75 sat

Saturate previous output.
Processes previous output value through tangens hyperbolicus (tanh) function.

Example

arg drive 0 -1 1

var v_amp

<prepare:
  # amp=/10..*10
  10
  pow $drive
  sto v_amp

<out:
  sin
  * $v_amp
  sat  # same as tanh
tanh saturation
arg drive 0 0 2

lut tanh shared 4096

<lut_tanh:
  # 0..1 => -100..+100
  fma 200 -100
  sat  # same as tanh

var v_amp

<prepare:
  # amp=*1..*100
  10
  pow $drive
  sto v_amp

<out:
  sin
  * $v_amp
  # -100..+100 => 0..1
  fma 1/200 0.5
  lut tanh lin=1
tanh saturation with lookup-table
See tanh (deprecated name in earlier releases)
note_s
useful for saturation-like non-linear distortion (converges to -1..1)
caution_s
only supported in float-mode (fall back to float in int mode)

10.76 set

Set variable
If the value/v input is not connected, set variable to previous output value.

Examples

var f
<out:
  1.23
  set f
set f to 1.23
var f
<out:
  set f 1.23
  set f value=1.23
  rep 16
    inc f 0.1
increment f 16 times

Inputs

value / vwhen connected, set variable to input value instead of previous output value

10.77 sin

Generate sine wave

Examples

<out:
  sin
sine oscillator
<out:
  sin freq=1 phase=0 vsync=2.5 window=sin
windowed / vsynced sine oscillator
<out:
  sin
    pd:
      tri freq=1
sine osc phase distortion

Inputs

freq / frange is 0.25..4 (1.0=middle C in fixed frequency mode)def=1.0
phase / phrange is 0..1 (start phase)def=0.0
vsync / vsrange is 0.01..4 (virtual oscillator sync)def=1.0 (off)
constcycle / cyrange is 0/1 (0=disable wavetable cycle reset)def=1 (on)
constreset / rsrange is 0/1 (0=disable plugin voice note on reset)def=1 (on)
constfixed / fxrange is 0/1 (1=enable fixed frequency mode)def=0 (follow kbd)
window / winrange is 0..8 (0=off/none(windowed vsync off), 1=sin(sine window), 2=hs(half-sine window), 3=tri(triangle window), 4=hse1(hse1_36), 5=hse2, 6=hse3, 7=hse4, 8=hse5) (must be const OR return amp factor (see below))
phasemod/pmrange is 0..1 (post vsync phase modulation)def=0.0 (off)
pdphase distortion insert (subtree). input is phase (0..1), output is new phase (0..1). only supported in float/hi-fi mode.
caution_s
windowed vsync is only supported in float-mode (ignored in integer mode)
note_s
when window input is not constant, it is treated as an insert which receives the window phase and returns the window amplification factor (0..1). I.e. this can be used to implement custom window functions.
tip_s
all oscillators can be used as wavefolders by setting their speed to 0, then modulating their phase input

10.78 slf

Read past sample output ("self" feedback)

Example

<out:
  pul
  + slf rate=0.98
  clp
mix past output

Inputs

rate / rrange is 0..1def=0.985

10.79 slw

Slew from a to b with configurable up / down amounts.
The module behaves differently depending on which inputs are connected:

Example

arg amp    0.7
arg slw_up 0.15
arg slw_dn 0.11

<out:
  pul
  slw up=$slw_up down=$slw_dn
  * rmp 0 1
  * $amp
  clp
cycle int slw example patch

Inputs

up / u(b > a) slew amount (1=instant .. 0=never)def=0.3
down / d(b < a) slew amount (1=instant .. 0=never)def=0.1
asource value (optional)
bdestination value (optional)
constsr1=(over)samplerate-independent slew amounts. 0=fixed slew amounts. (float/hi-fi mode only)def=1

10.80 spd

Returns current note speed (including oversampling factor).
Can be used to accumulate normalized (0..1) phases.

Example

id spd_test

var v_phase

<out:
  $v_phase
  + spd
  sto v_phase
  # wrap-around (keep only the fractional bits)
  frc
  # 0..1 => -1..1
  fma 2 -1
a simple saw oscillator (pitch follows keyboard)
note_s
in sample calculator (instead of voice plugin) mode, this simply returns (1.0 / oversampling_factor)

10.81 spr

Apply dampened spring oscillation to previous output value.

Example

id spring_time_1
<out:
  pul
  * 0.5
  spr spd=0.1 dcy=0.8
  clp
dampened spring example plugin

Inputs

spd / sspeed (0..1)def=0.1
dcy / ddecay (0..1)def=0.8
sr1=(over-)samplerate independency (float/hi-fi mode only)def=1 (on)

10.82 ssl

Sine slice oscillator.
This is a rather experimental oscillator that mainly generates "chiptune"-ish sounds.
It works similar to how the additional 7 waveforms on the Yamaha TX81Z were created back in the 1980ies.
The (built-in) sine table as well as the destination waveform(-cycle) is divided into 8 regions / slices.
Each target slice is assigned one of the 8 sine regions (0..7) via the slice sequence seq (first parameter).
The zeromask input determines which slices are silenced while modmask selects which ones are affected by the modulation mode and modulation amount mod.
seqmod is multiplied by 16384, converted to a 16bit integer, replicated to 32 bit and added to the base sequence (where each nibble (4 bits) represents a slice, e.g. $01234567). In other words: it (dynamically) shifts the sequence's slice selection with wildy varying results.

Example

<out:
  ssl 01234567 freq=1.0 phase=0.0 mode=4 mod=0 seq=0 seqmod=0 modmask=1.0 zeromask=0.0
The first argument is the base slice sequence (0=region 0, 7=region 7).
The sequence is repeated if it has less than 8 entries.

Inputs

freq / frange is 0.25..4 (1.0=middle C in fixed frequency mode)def=1.0
phase / phrange is 0..1def=0.0
modrange is -1..1 (slice modulation)def=0.0
constseq / srange is 0..1 (0=base slice sequence, 00000001..7777777)
modmask / mmrange is 0..1 (0=no slices, 1=all slices)def=1.0 (all)
zeromask / zmrange is 0..1 (0=no slices cleared, 1=all slices cleared)def=0.0 (none)
constmode / mrange is 0..5
ModeDescription
0 pointignore fractional (mod) bits, simply recombine waveforms (fastest)
1 phasefractional (mod) bits determine source region start offset shift ("phase" mod 1/8 sin)
2 lerpfractional (mod) bits are used to lerp sine source region to cosine wave
3 shiftfractional (mod) bits are used to shift source region (dc offset + reflect/fold)
4 angfractional (mod) bits determine source waveform start offset shift ("phase" +- 2PI)
5 ampfractional (mod) bits are used to amplify source region
SSL mode settings
def=0 (point)
seqmod / smrange is 0..3.9999 (per-sample sequence modulation)def=0 (no modulation)

10.83 sta

Store variable in LUT at normalized index determined by previous output value.
Syntax: sta <lut_id> <index_mul> <index_add> <src_var> <var_mul> <var_add>

Example

lut ws shared 4096
lut ws_inv shared 4096 inv

<lut_ws:
  # transform implicit input x={0..1} => {-1..1}
  fma 2 -1
  pow 3
  # store in inverse LUT 'ws_inv'
  #  - transform 'pow' result (-1..1) into normalized index (0..1)
  #  - transform x from 0..1 to -1..1 and store in LUT
  sta ws_inv 0.5 0.5 x 2 -1
  # store 'pow' result in LUT 'ws' (done by auto-generated code)
  #  (note) holes in the ws_inv LUT will be auto-filled afterwards

<out:
   sin
   # -1..1 => 0..1
   fma 0.5 0.5
   lut ws
   lpf freq=0.3 res=0.5
   clp
   # -1..1 => 0..1
   fma 0.5 0.5
   lut ws_inv
inverse exponential lookup table
curve ws
lut ws_inv shared 2048 inv

<lut_ws_inv:
  # use implicit input x (0..1) as index into bezier curve 'ws'
  lut ws
  clp
  # store in inverse LUT 'ws_inv'
  #  - transform 'clp' result (-1..1) into normalized index (0..1)
  #  - transform x from 0..1 to -1..1 and store in LUT
  sta ws_inv 0.5 0.5 x 2 -1
  # don't write output value to lut (already written)
  end

<out:
   sin
   # -1..1 => 0..1
   fma 0.5 0.5
   lut ws
   lpf freq=0.3 res=0.5
   clp
   # -1..1 => 0..1
   fma 0.5 0.5
   lut ws_inv
inverse bezier curve lookup table
note_s
referencing an inverse LUT in another LUT's init lane via sta causes the inverse LUT to be filled before the init lane is executed, and potential holes (unwritten elements) in the inverse LUT to be closed afterwards

10.84 sto

Store previous output in variable

Examples

var t

<out:
  sin
  sto t
  * $t
  * $t
sin*sin oscillator
The variable is initialized with 0 but retains its content over multiple sample frame iterations.
var sin_val

<out:
  $sin_val
  + sin
  sto sin_val
  clp
accumulate sine

10.85 svf

Process previous output with two-pole state-variable filter.

Example

<out:
  saw
  svf freq=0.4 res=0.9 mode=lpf
  svf 0.4 0.5 0
resonant four-pole (-24dB/oct) low-pass filter

Inputs

freq / frange is 0..1def=0.5
res / rrange is 0(full res)..1(no res)def=1.0 (no resonance)
constmode / m0=lpf, 1=hpf, 2=bpf, 3=notchdef=0=lpf
postlpf / plpost-LPF / pre-HPF insert
posthpf / phpost-HPF / pre-BPF insert
postbpf / pbpost-BPF / pre-LPF insert
constsr1=enable (over-)samplerate independent frequency (voice-plugins)def=1 (on)

10.86 tan

Calculate the tangens of the previous output value.
tip_s
tan(0.125*2PI) = tan(0.785398163398) = 1.0, i.e. scale normalized -1..1 input by 0.78539 to create a tan waveshaper (see test tan 1 example patch)
caution_s
forces fallback to hifi / float mode

10.87 tanh

note_s
this is an alias for sat

10.88 tmp

This is the (deprecated) long-form of the ~ template module instantiation command.
see templates

10.89 trc

Debug-trace last output value (print to console).

Example

lut mylut shared 16

<lut_mylut:
  # print current $x
  trc

<out:
  sin
debug-print normalized LUT index

Inputs

constc1=trace value changes only, 0=trace all valuesdef=1 (on)
constz1=trace 0 values, 0=skipdef=1 (on)

10.90 tri

Generate triangle wave

Examples

<out:
  tri
triangle oscillator
<out:
  tri freq=1 phase=0 vsync=2.5
vsynced triangle oscillator

Inputs

freq / frange is 0.25..4 (1.0=middle C in fixed frequency mode)def=1.0
phase / phrange is 0..1 (start phase)def=0.0
vsync / vsrange is 0.01..4 (virtual oscillator sync)def=1.0 (off)
constcycle / cyrange is 0/1 (0=disable wavetable cycle reset)def=1 (on)
constreset / rsrange is 0/1 (0=disable plugin voice note on reset)def=1 (on)
constfixed / fxrange is 0/1 (1=enable fixed frequency mode)def=0 (follow kbd)
window / winrange is 0..8 (0=off/none(windowed vsync off), 1=sin(sine window), 2=hs(half-sine window), 3=tri(triangle window), 4=hse1(hse1_36), 5=hse2, 6=hse3, 7=hse4, 8=hse5) (must be const OR return amp factor (see below))
phasemod / pmrange is 0..1 (post vsync phase modulation)def=0.0 (off)
pdphase distortion insert (subtree). input is phase (0..1), output is new phase (0..1). only supported in float/hi-fi mode.
caution_s
windowed vsync is only supported in float-mode (ignored in integer mode)
note_s
when window input is not constant, it is treated as an insert which receives the window phase and returns the window amplification factor (0..1). I.e. this can be used to implement custom window functions.

10.91 tsq

Generate trigger sequence with configurable duty cycle duration.

Example

var v_clk
<out:
  clk div=1/4
  sto v_clk
  sin
  * tsq x.x..x...x....x.....x......x.......x cg=$v_clk
    adsr
envelope restarted by trigger pattern
var v_clk
<out:
  clk div=1/4
  sto v_clk
  sin
  * tsq x.x..x...x....x.....x......x.......x cg=$v_clk
    adsr
envelope restarted by trigger pattern
id arrayparam_seq_test_1

param p_seq

curve 0 steps param=p_seq size=16 var=4

var v_env_lvl

<out:
  clk div=1/4
  # (note) 'x' is a dummy pattern
  tsq x len=16 stepth=0.5
    step:
      # input is step index (0..15)
      * 1/16
      lut steps
    ontrig:
      set v_env_lvl 1

  tri
  * $v_env_lvl
    *sr 0.9993
    sto v_env_lvl
    slw 0.05 0.05
read trigger pattern from array param

Inputs

dur / drange is 0..f (trigger pulse duty cycle duration in milliseconds)def=5
restart / rsrange is 0/1 (restart sequence on rising edge)
shift / shrange is 0..1 (shift play position by normalized position (0..1 ⇒ 0..pat_len))def=0
gate / grange is 0/1 (play current step on rising edge)
clk / crange is 0/1 (advance to next step on rising edge)
cgrange is 0/1 (clock+gate, play current step and advance to next step on rising edge)
constloop / lprange is 0/1 (1=repeat pattern, 0=oneshot/stop at end of pattern)def=1 (on)
constlen / lsequence length (1..32(float), 1..15(integer/lo-fi)). default=0=use xx.xx pattern length.def=0 (use seq len)
step / swhen connected, eval subtree when value is needed for step 'x'. input is step index (0..n), output is step value.
conststepth / sththreshold for step output (value=1 when output is > stepth, 0 otherwise).def=0.0
ontrig / otevaluated each time a step is triggered/gated. must return new pulse level (0..1, 0 discards pulse)
onend / oeevaluated when end of pattern (or first step in loop mode) is reached during clock tick
important_s
when neither clk, gate or cg are connected, the clock+gate input is read from the previous output value

10.92 vel

Returns the current voice velocity (0..1).

Examples

id vel_test_1
<out:
  sin
  * vel  # linear velocity curve
velocity example 1
vel can also be used in short form value assignments to (dynamic) inputs:
id vel_test_2
<out:
  sin
  * ipl a=0.1 b=0.9 t=vel win=smoothstep  # non-linear, biased velocity curve
velocity example 2
note_s
only available in voice plugin mode. in sample synth mode, this simply returns 1.0.

10.93 vpl

Process previous output via stfx voice plugin.

Examples

<out:
  sin 5.0
  vpl "bsp ws quintic" "Dry / Wet"=0.5 Drive=0.39
single line voice plugin invocation
arg drive 0.39 min=0 max=1

<out:
  sin 5.0
  vpl "bsp ws quintic" "Dry / Wet"=0.5 "Drive"=$drive
single line voice plugin invocation with arg slider
arg drive 0.39 min=0 max=1

<out:
  sin 5.0
  vpl "bsp ws quintic"
    "Dry / Wet" = 1
    "X^0"       = 0
    "X^1"       = 0.5
    "X^2"       = 0.74
    "X^3"       = 0.8
    "X^4"       = 0.62
    "X^5"       = 0.5
    "Drive"     = $drive
multi line voice plugin invocation with arg slider
In Lo-Fi (integer) mode, the input/output values are temporarily converted to / from float.
note_s
See Eureka sampler "FX" tab for a list of available plugins.
tip_s
Plugin setups can be exported to single- or multi-line Cycle scripts (clipboard) via the Eureka sampler "FX" tab context menu
caution_s
vpl modules cannot be exported to "C" source

10.94 vsq

Generate value sequence.
note_s
please use step/s input to return sequence value for given sequence step index (initial value when evaluating step input)
See tsq module

10.95 vst

Declare variable and store previous output.
important_s
the variable will be declared within the scope of the current lane

Example

<out:
  tri phase=0.25
  vst tri
  + $tri
    hld 0.7
  * 0.5
sample&hold triangle osc mix

10.96 wrl

Write left channel output sample.

Inputs

value / vwhen connected, read sample from input instead of previous output value
caution_s
available only in plugin mode.
note_s
implicitely enables STEREO plugin mode

10.97 wrp

Wrap previous output value at ceiling.

Examples

<out:
  sin
  * 2.0
  wrp
wrap into -1..1 range
<out:
  sin
  * 2.0
  wrp ceil=0.8
wrap into -0.8..0.8 range
note_s
ceil sets min=-ceil, max=+ceil
<out:
  sin
  * 2.0
  wrp min=-0.5 max=0.9
wrap into -0.5..0.9 range
note_s
min / max override default ceil
oversample 4

arg amp_pre 0.99 0 8
arg wrp_min -1 -1 1
arg wrp_max  1 -1 1
arg p_amp 0.7 0 8

<out:
  sin
  * $amp_pre
  wrp min=$wrp_min max=$wrp_max
  * rmp 0 1
  * $p_amp
  clp
test wrp 1 example

Inputs

ceil / csets min=-val, max=+valdef=1.0
min / mminimum value (override -ceil)
max / xmaximum value (override ceil)
consthq1=enable high-quality (float) mode (arbitary number of wrap-arounds).def=1
see also: fld

10.98 wrr

Write right channel output sample.

Inputs

value / vwhen connected, read sample from input instead of previous output value
caution_s
available only in plugin mode.
note_s
implicitely enables STEREO plugin mode

10.99 xfd

Crossfade between previous output and input b. At the center position, the two inputs are summed.

Example

arg mod_waveform 0 -1 1
<out:
  tri
  xfd amount=$mod_waveform
    b:
      saw
crossfade between triangle and saw

Inputs

amount / amtcrossfade-to-b amount (-1=previous input .. 0=previous input and b .. 1=b)
See also: ipl

10.100 zlp

Read looped sample frame from other zone. Can be used to mix samples or create chords.

Example

zone 0 otherzone
zone 1 othersmp.otherzone

<out:
  zlp 0 rate=0.3 phase=0.5
  + zlp 1 0.9
  clp
mix looped sample zones

Inputs

rate / rrange is 0..15def=1.0
phase / phrange is 0..1def=0.0
constdiv / drange is 0..256 (set repeatlen to zonelen / div)
sub / srange is 0..256 (shifts start offset by sub*repeatplen, e.g. for selecting sub-wave from wavetable. requires div to be connected)
constlin / lenables bilinear filteringdef=0 (off)
note_s
sample len is determined by last loop length of the specified zone
note_s
sample len should be a power of two (read offset is ANDed with (sampleLen-1))

10.101 zon

Read sample frame from other zone. Can be used to mix samples or create chords.

Examples

zone 0 otherzone
zone 1 othersmp.otherzone

<out:
  zon 0 rate=0.3
  + zon 1 0.9
  clp
Mix two zones at different rates
zone 0 loopzone

<out:
  # 'and=255' causes zone frames 0..255 to be repeated
  zon 0 rate=0.5 lin=1 and=255
Upsampled loop
zone 0 singlenote_g3

<out:
  zon 0
  + zon 0 d#3/g-3
  + zon 0 c-3/g-3
  * 0.5
  clp
Minor chord

Inputs

constrate / rrange is 0..15def=1.0
constlin / lenables bilinear filteringdef=0 (nearest)
constand / aenables power-of-two wrap-around (0=off, 1..65535 otherwise)def=0 (off)
andf / aflogic-and read position with float converted to fixpointdef=0.0 (off)
restart / rsrising edge restarts sample
pos / pnormalized restart position (0..1)def=0.0
freq / frange is 0..15 (effectiverate = `rate * currentnote_speed * freq`)
constclamp / cclamp source offset (always in float mode, def=1 in int mode)def=1 (on)
tip_s
in order to create chords, synthesize the highest note to a zone, then in a following zone read and mix the previously rendered zone at different speeds / rates
See also: zlp (supports arbitrary loops)

10.102 zsq

Sequence (other) zone sample.
Can be used to create (multi-channel) drum loops.

Example

zone 0 bd
zone 1 sd

<out:
  #       0   1   2   3   4   5   6   7
  = zsq 0 xx......x....x.. note=16 bpm=125 rate=1
  + zsq 1 ....x.......x... bpm=125 rate=1.1 lin=1
  clp
Kick / snare drum-sample sequence

Inputs

constnoterange is 1..256 (step note duration)def=16
constbpmrange is 0..1000 (<10 use project bpm)def=0.0
constswingrange is -0.5..0.5def=0.0
constraterange is 0.125..8def=1.0
constlinrange is 0..1 (>=0.5 enable bilinear filtering)def=0/nearest
constdivrange is 0..256 (divide source zone into n regions)
offrange is 0..1 (select sub-divided source region, e.g. $wt_x)
tip_s
When the input zones (one-shot samples) are placed in the same samplebank as the sequenced loop, ticking the Recalc All checkbox (next to the F11: Synth button at the bottom of the screen) will cause all procedural zones to be recalculated when any synth patch changes. This makes it possible to tweak the input sounds and immediately hear the edits in the context of the loop.
tip_s
rate can (like any constant value) also be expressed as a note ratio, e.g. c-3/d#3

11 Curve Lookup Tables (LUTs)

Cycle supports up to 16 cubic-bezier curve tables which can be used as amp or filter envelopes, single-cycle waveforms, or sample waveshapers.
Curve tables are internally converted (sampled) to short, power-of-two sized arrays. The array sizes are configured in the UI.
Before a curve table can be used with a lut module, it must first be declared with a curve statement:
curve amp

<out:
  sin
  * rmp 0 0 0.95   # millisec=0 (auto-fit sample size) start=0 end=0.95
    lut
Using a curve LUT as an amp envelope (no interpolation)
curve amp

<out:
  sin
  * rmp 0 0 1   # millisec=0 (auto-fit sample size) start=0 end=1
    lut amp lin=1 clamp=1
Amp envelope with linear interpolation (clamp to end of curve)

11.1 Auto enumeration

The curve index (first arg after curve keyword) may be skipped or set to -1. This will cause the curve to be auto-assigned to slot 0..15.
note_s
The curve index allocator attempts to retain the previous curve assignment based on the curve's alias / id.
arg p_amp     0.7 0 8
arg p_feg_amt 0.2 0 4

curve freq
curve amp

<out:
  sin
    freq:
      rmp 0 0 1
      lut freq
      pow 2
      fma $p_feg_amt 1
  * rmp 0 0 1
    lut amp
  * $p_amp
  clp
curve auto enum test 1

12 Procedural Lookup Tables (LUTs)

Cycle supports an arbitrary number of procedural look up tables which share the same namespace with curve lookup tables.
The table size must be a power of two.
Tables are calculated either per-voice (at note-on, using the then-current parameters and modulation), or are statically shared among all voices.
lut pow shared 256

<lut_pow:
  # (note) implicit var "x" stores normalized table position (0..1=last table element)
  # (note) the default out value is $x
  fma 2 -1
  pow 1.44

<out:
  sin
  fma 0.5 0.5
  lut pow lin=1
shared LUT example
param p_exp 1 1 16

mod m_exp

lut pow voice 32

<lut_pow:
  # (note) implicit var "x" stores normalized table position (0..1=last table element)
  # (note) the default out value is $x
  fma 2 -1
  pow exp=$m_exp

<out:
  sin
  fma 0.5 0.5
  lut pow lin=1
per-voice modulated LUT example
tip_s
lookup tables can greatly improve the rendering performance by precalculating recurring values
note_s
the default table size is 64
note_s
the default table mode is voice
note_s
LUT init sections must be named lut_<id> (e.g. <lut_pow:)
warning_s
if no init section has been declared for a LUT used by a "lut" module, compilation will fail
note_s
the shared LUTs are calculated after the <init: section
note_s
the per-voice (or per-wavetable cycle) LUTs are updated after the <prepare: / <wt_init: section

12.1 Inverse Lookup Tables

See sta module documentation for examples.

13 Macros

Macros are used to define smoothly interpolated value sets, controlled by a single parameter (or other value).
They are mostly useful to hide the internal complexity of a Cycle patch, and reduce the number of (plugin-)parameters.
The mac module updates the current, interpolated values of the macro fields.
The current value of a macro field is accessed (read) using the <macro_id>.<field_name> syntax.

Examples

id     osc_mix_macro_manual
name   "osc mix macro manual"
author bsp
cat    osc

# Plugin parameter
param p_osc_mix 0 0 1

# 4 manually configured settings
macro osc_mix
  sin   tri  saw   pul
  1     0     0    0
  0     1     0    0
  0     0     1    0
  0     0     0    1

<prepare:
  # calculate interpolated macro values
  #  (updated 1000 times per second)
  mac osc_mix pos=$p_osc_mix

<out:
  # calculate current output sample
  sin
  * osc_mix.sin
  + tri
    * osc_mix.tri
  + saw
    * osc_mix.saw
  + pul
    * osc_mix.pul
macro example (manual settings)
id     osc_mix_macro_permutate
name   "osc mix macro permutate"
author bsp
cat    osc

# Plugin parameter
param p_osc_mix 0 0 1

# auto-generated settings
#  (permutations of values 0/1/-1 => 3*3*3*3=81 settings)
macro osc_mix
  sin   tri  saw   pul
  permutate 0 1 -1

<prepare:
  # calculate interpolated macro values
  #  (updated 1000 times per second)
  mac osc_mix $p_osc_mix

<out:
  # calculate current output sample
  sin
  * osc_mix.sin
  + tri
    * osc_mix.tri
  + saw
    * osc_mix.saw
  + pul
    * osc_mix.pul
macro example (permutated settings)
pos is in the range 0..1 (last setting is lerped back to first setting)
note_s
the maximum number of (auto-generated) permutations is limited to 16384
tip_s
alternatively to macros, the <init: and <prepare: sections may also contain arbitrarily complex, procedural pre-calculations

14 Maps

Maps are used to remap values using non-contiguous tables.
Applications include microtuning scales, drum maps or envelopes.
A map is divided into n regions:
id map_test_4

map mymap
       freq vol cutoff   # map field names
  0.0  1    0.2   0.8    ## map region 1 starts at 0.0
  4.0  1.5  0.5   0.2    ## map region 2 starts at 4.0
  5.0  2.5  1.0   0.1    ## map region 3 starts at 5.0

<out:
  note
  & 7
  vst mapkey  # store (note modulo 8) in var 'mapkey'
  saw
    freq:
      $mapkey
      map mymap.freq def=1 mode=lin  # remap previous output value using linear interpolation
  svf
    freq:
      $mapkey
      map mymap.cutoff def=0.5 mode=up  # remap previous output value (round up to nearest map key)
  * $mapkey
    map mymap.vol def=0 mode=down  # remap previous output value (round down to nearest map key)
map example
See map module.

15 Templates

Templates are parametrizable code snippets. Each template supports an arbitrary number of variables which will be replaced by either their default values or the values assigned in the template instantiation.
The special var %% is replaced by a unique template instantiation id (used when generating global variables, lanes, or parameter / modulation names).
tip_s
variables declared within a template are automatically prefixed with a unique id during instatiation
Template applications:

15.1 Example

A simple, dual oscillator additive synthesis patch plugin.
Each macro oscillator renders the fundamental plus three harmonics with adjustable level decay.
The oscillators levels and harmonic decays can be changed in the sample voiceplugin parameter editor and can also be modulated in the sample's modmatrix.
id test_template_2

modparam lvl1  0.75
modparam hdcy1 0.25
modparam lvl2  0.25
modparam hdcy2 0.125

def osc_sin_hdcy_1 freq=1 hdcy=0.25
  vst myosc_harm 1
  sin
    freq:
      %freq%
  * $myosc_harm
  mul myosc_harm %hdcy%
  + sin
      freq:
        %freq%
        + 1
    * $myosc_harm
    mul myosc_harm %hdcy%
  + sin
      freq:
        %freq%
        + 2
    * $myosc_harm
    mul myosc_harm %hdcy%
  + sin
      freq:
        %freq%
        + 3
    * $myosc_harm
    mul myosc_harm %hdcy%

<out:
  ~osc_sin_hdcy_1 1 $m_hdcy1
  * $m_lvl1
  + ~osc_sin_hdcy_1 freq=2.5 hdcy=$m_hdcy2
    * $m_lvl2
additive synthesis template example

15.2 Instance id

Template instances are usually auto-enumerated (1..n) but the instance name can be overridden using the id attribute:
def myclass freq=1
  var freq = %freq%
  var out
  arg a_%%_amp  0.125  # a_mc_amp for id=mc
  arg a_%id%_fb 0      # a_mc_fb for id=mc

~myclass id=mc

<out:
  mc.out
    * $a_mc_fb
  + tri freq=mc.freq
    * $a_mc_amp
      * 8
  sto mc.out
  * rmp 0 1
  clp
explicit instance id ("template instance id" example patch)

15.3 Audio lanes

Template declarations may also include audio lanes.
tip_s
all audio lanes are summed to generate the final output, see layers
id test_template_4

def myosc f1=1 f2=1.5 f3=2 dcy=0.5
  modparam myosc%%_lvl 1

  <out_myosc%%:
    sin freq=%f1%
    vst lvl %dcy%
    + sin freq=%f2%
      * $lvl
    mul lvl %dcy%
    + sin freq=%f3%
      * $lvl
    * $m_myosc%%_lvl
    end # (note) end lane so the second ~myosc does not end up in the first myosc lane

~myosc 1 1.5 2 dcy=0.5
~myosc 1.015 1.5025 2.015 dcy=0.25
lanes and instance id example

15.3.1 init, prepare, calc lanes

The init, prepare and calc lanes are not treated as audio lanes but can rather be inserted at any point in the patch.
arg p_amp 0.5

def myclass
  arg a_%%_freq 0 -1 1  # a_mc_freq for id=mc1
  arg a_%%_amp 1        # a_mc_amp for id=mc1
  var v_rate
  init:
    var myinitvar   # test vardecl (internally prefixed by templ class+instance id+lane id)
    42
    trc             # debug: print 42 to see if init was called
  prepare:
    $a_%%_freq
    bts 4 4         # -1..1 => 1/4..*4
    trc             # debug: print rate
    sto v_rate
  calc:
    tri freq=$v_rate
    * $a_%%_amp

~myclass id=mc1
~myclass id=mc2

<init:      # note-on
  init mc1  # insert init lanes
  init mc2

<prepare:      # prepare next block (update internal vars)
  prepare mc1  # insert prepare lanes
  prepare mc2

<out:          # calculate output
  calc mc1     # insert calc lanes
  + calc mc2
  * rmp 0 1
  * $p_amp
  clp
init, prepare, calc template lanes ("template instance id 2" example patch)

15.4 global

Lines inside the global: section are placed outside the current lane.
arg p_amp 0.7 0 8

def sineosc
  global:
    arg p_%%_freq 1 1 4
    arg p_%%_amp 0.7

  sin freq=$p_%%_freq
  * $p_%%_amp

<out:
  ~sineosc
  * rmp 0 1
  * $p_amp
  clp
test_template_6

15.5 luts

Templates may also contain procedural look-up-tables (LUTs). See test_template_5 example patch (and osc_sin_tri_1_lut.cy machine include file) for a LUT based oscillator template.
id test_template_5

inc osc_sin_tri_1_lut

<out:
  ~osc_sin_tri_1_lut
  clp
lut based template oscillator plugin

15.6 pre

If the first module in an input or template declaration is a template call and an operator other than the default (= / A) is used, the first module must be pre.
Example:
def tmp_mul
  * 0.1

def tmp_abs
  abs

def mytmp
  pre
  + ~tmp_mul
    #trc
    ~tmp_abs

<out:
  sin
  * 0.5
  ~mytmp
  clp
pre
This is currently required due to a parser-issue which may be proper-fixed later.

15.7 structs

Templates can also be used as variable containers (structures):
# example for a struct-like template
arg p_amp 0.7 0 8

def mystruct
  var x
  var y

  <init:  ## (note) init is evaluated before prepare
    set x 1.0
    set y 0.2

~mystruct id=s
~mystruct id=t

<prepare:
  s.x          ## print default x=1.0
  trc
  set t.y 0.7  ## override default y=0.3

<out:
  sin
  * s.x  ## => *1.0
  * t.y  ## => *0.7
  * rmp 0 1
  * $p_amp
  clp
test template struct 1

16 Includes

Re-usable code snippets can be placed in STConfig.cycle_machine_inc_path (eureka/machine_inc/ by default) and then inserted into patches:
id test_template_3

modparam m_lvl1   1
modparam m_lvl2   0
modparam m_trimix 0

# the (global) include file defines the template "osc_sin_tri_1"
inc osc_sin_tri_1

<out:
  ~osc_sin_tri_1 freq=1
  * $m_lvl1
  + ~osc_sin_tri_1 1.5
    * $m_lvl2
  ipl t=$m_trimix
    b:
      tri
      * $m_lvl1
      + tri freq=1.5
        * $m_lvl2
include file example
tip_s
in the Cycle machine source editor, press lctrl-g to edit the include file under the cursor, or lctrl-h to copy its native path name (for opening it in an external editor)

16.1 Patch-buffers

Patches may contain an arbitrary number of source buffers.
This can be used to split complex patches into multiple chunks (e.g. template definitions, lanes, ..).
Patch-buffer names must commence with a . character.
def mytemplate det=0
  sin
    freq:
      1
      + %det%
  + sin
      freq:
       1
       + %det%
       * 2
.mytemplateinc buffer
arg p_amp 0.7 0 8
arg p_det 0 -1 1

inc .mytemplateinc

<out:
  ~mytemplate
  + ~mytemplate det=$p_det
  * rmp 0 1
  * 0.25
  * $p_amp
  clp
main source
note_s
see multibuf test 3 example patch
Just like global includes, patch buffers are selected for editing by pressing lctrl-g while the cursor is on an inc or ~ template instantiation line.
A patch buffer is deleted by removing all its content.
note_s
the last seen cursor position is stored per buffer but it is not persistent across project file saves

17 Plugins

Cycle patches can also be exported as native code (C/C++) voice plugins.
The following restrictions apply:
note_s
sample zones referenced by modules will be embedded in the plugin (e.g. zon, zlp, ssl)
Example:
id fm
oversample 4
param p_mod_freq 0.5 min=0 max=1
param p_mod_amt  0       0     1
param p_vsync    0       1    16
mod m_mod_freq
mod m_mod_amt
<out:
  sin
    phase:
      sin vsync=$p_vsync window=sin
        freq:
          # 0..1 => 1/16 .. 0 .. *16
          $m_mod_freq
          fma 2 -1
          bts 16
      * $m_mod_amt
simple 2-op FM plugin

17.1 Plugin header

Voice plugin patches must declare a unique plugin id:
id     myplugin_v1
name   "my plugin v1"
author bsp
cat    osc
note_s
the id keyword enables voice-plugin mode. name, author and cat are optional.

17.2 Parameters and modulations

Plugins have "n" parameters (shared among all voices), and "n" mods (per-voice modulation).
It is recommended to not exceed 8 parameters and mods.
note_s
parameter names may be prefixed with p_ (prefix will be removed during export)
note_s
mod names may be prefixed with m_ (prefix will be removed during export)
If a mod has a parameter of the same name (ignoring the optional prefix), the effective mod value is the sum of the shared and mod values.
float modcutoff = shared->params[PARAM_CUTOFF] + voice->mods[MOD_CUTOFF];
auto-generated code example
Frequency, parameters, and modulation will be interpolated over processing blocks. The block update rate (in Eureka) is 1000Hz, i.e. a processing block consists of e.g. 48 sample frames at a sample rate of 48kHz.

17.2.1 Params

Parameters are always in the (normalized) range 0..1.
Parameters may have default (UI reset) values, which will be exported to the plugin:
param p_cutoff 0.9
default reset value
Normalized parameters are scaled to their (optional) min / max value range when they are accessed:
param p 0.5 1 16
p will be scaled to range 1..16. the effective default value is 1+0.5*(16-1)=8.5
Instead of param, its shortcut form p can be used instead:
p drv 0.5
param shortcut syntax

17.2.1.1 Param Preset Values

Each parameter can be assigned a list of preset values (shown when hold-clicking the param value widget).

Examples

param p_angle 0 0 1

param_values p_angle 0="0 deg" 0.25="90 deg" 0.5="180 deg" 0.75="270" 1="360 deg"
param preset values example 1
param p_mode 0 0 3

pv p_mode 0=red 1=green 2=blue
param preset values example 2 (shortcut syntax)
note_s
parameter preset values always refer to unscaled, normalize 0..1 values

17.2.1.2 Param Groups

Parameters can be assigned to a group (in order to classify them).
Grouped parameters can, for example, be randomized all at once in the UI.
param p_tune_1  0.5 -1 1  group=tune
param p_tune_2  0.5 -1 1  group=tune
param p_tune_3  0.5 -1 1  group=tune
param p_tune_4  0.5 -1 1  group=tune
param p_tune_5  0.5 -1 1  group=tune
parameter groups (excerpt from osc_hh_v1.cy)

17.2.1.3 Param Sections

Parameters can (also) be assigned to sections (in order to group them in the UI).
id more_than_8_params

param p_amp 1 0 1

def myosc id=0
  param p_myosc%id%_freq   0   0 1  section=myosc%id% group=tune
  param p_myosc%id%_amp    0.5 0 1  section=myosc%id% group=amp
  param p_myosc%id%_vsync  0   0 1  section=myosc%id% group=vsync
  sin
    freq:
      $p_myosc%id%_freq
      fma 2 -1
      bts 4 4
    vsync:
      $p_myosc%id%_vsync
      bts 1 16
  * $p_myosc%id%_amp


<out:
  + ~myosc id=0
  + ~myosc id=1
  + ~myosc id=2
  + ~myosc id=3

  * $p_amp
  clp
parameter sections ("more than 8 params" example patch plugin)

17.2.1.4 Array Params

Each parameter can be associated with an array (lut). Arrays have up to 32 variations, which are selected by the (normalized) parameter value.
The maximum array size is 256.
Each array param may also be associated with a curve, which defines the initial element values for a new plugin instance.
Example:
# additive synthesis using 16 sine partials
#  the 'level' array defines the partial magnitudes
#
id     array_test_1
name   array_test_1
cat    osc
author bsp

# selects 'level' array variation 'level' (0..1 => variation 1..7..0)
param p_level 0 0 1

# modulates 'level' array variation (per block)
mod   m_level

# connect curve 'level' to param 'p_level'
#   - array size = 16 elements
#   - num array variations = 8
#   - initial (variation 1) values are defined by curve 0
curve 0 level param=p_level size=16 var=8

var v_index
var v_out

<out:
  set v_index 0
  set v_freq 1
  set v_out 0
  rep 16
    $v_out
    + sin freq=$v_freq
        * $v_index
          lut level clamp=0 lin=0
          * 1/16
    sto v_out
    inc v_index 1/16
    inc v_freq
  $v_out
additive synthesis using 16 sine partials
17.2.1.4.1 array keyword and per-element names and ranges
The following alternative syntax for an array declaration allows to specify names, reset values, display min / max ranges, and editing precision (number of fractional digits) per element:
id array_param

param p_amp 0 0 1

# "0" is the index of the curve (0..15) which defines the initial array element values for new plugin instances
#   (note) this parameter can be set to -1, or skipped entirely
array 0 p_amp dpyMin=0 dpyMax=1 size=4 var=4
  0 fundamental 1
  1 partial2    0
  2 partial3    0
  3 partial4    0

mod m_amp

<out:
  0
  + sin
    * 0/4
      lut p_amp
  + sin freq=2
    * 1/4
      lut p_amp
  + sin freq=3
    * 2/4
      lut p_amp
  + sin freq=4
    * 3/4
      lut p_amp
four-partial additive array param oscillator
Plugin param arrays can also be addressed like structs:
id array_param_elname_test_2

param p_amp 0 0 1

array p_amp dpyMin=0 dpyMax=1 size=4 var=4
  0 fundamental 1
  1 partial2    0
  2 partial3    0
  3 partial4    0

mod m_amp

<out:
  0
  + sin
    * p_amp.fundamental
  + sin freq=2
    * p_amp.partial2
  + sin freq=3
    * p_amp.partial3
  + sin freq=4
    * p_amp.partial4
four-partial additive array param oscillator (struct-like element access)
Instead of array, its shortcut form ap (array parameter) can be used instead:
id array_shortcut_test_1
param cfg 0
ap cfg s=3 v=4  # == array 0 param=cfg size=3 var=4
  0 freq   0.5 0    1  3  # def=1 min=0 max=1  prec=3
  1 amp    1              # def=1 min=0 max=1  prec=5
  2 vsync  1   1    32 2  # def=1 min=1 max=32 prec=2
<out:
  sin window=hse4
    freq:
      cfg.freq
      fma 2 -1  #  0..1 => -1..1
      bts 4 16  # -1..1 => /4..*16
    vsync:
      cfg.vsync
      fma 31 1  # 0..1 => 1..32
  * cfg.amp
array parameter shortcut syntax
caution_s
while each array element has its own reset / default value, display value range, and editing precision, accessing it in a patch will always return the storage value, i.e. it is up to the patch to actually scale it to the display range
The following attributes are supported in an array declaration:
NameShortcut nameDescription
parampassociated parameter name (can be omitted when it's the third word)
sizesarray size (1..256)
varvnum variations (1..32)
min-minimum storage value (-1..1, def=0)
max-maximum storage value (-1..1, def=1)
dpyMindminminimum display value (arbitrary float) (def=storage min)
dpyMaxdmaxmaximum display value (arbitrary float) (def=storage max)
dpyPrecdprecdisplay value editing precision (num fractional digits, 0..7) (def=5)
Array parameter attributes
note_s
the initial values of the first variation of an array are defined by the associated (bezier) curve
caution_s
the curve resolution should equal the array size (a power of two), otherwise the initial (variation 1) array values will either be a partial copy of the curve (when the curve size is larger than the array size), or the remaining elements will be filled with 0 (when the curve size is smaller than the array size).
caution_s
when wrap-around mode is used (clamp=0), the array size should be a power of two
note_s
the effective, interpolated (per-voice) array values are updated at the beginning of a block whenever the curve selection (array control parameter and / or modulation) has changed, or when the voice has just been started.
note_s
while array param floating point elements are stored in signed 16bit format in Eureka sampler patches (.esa), from a plugin's point of view they are always 32bit IEEE floats.
tip_s
in the editor, array params have an 'a' button next to them which opens the array / variation editor dialog

17.2.2 Mods

Mods have an implicit reset value of 0 and are typically in the range -1..1.
mod m_cutoff
declare modmatrix destination
note_s
the actual range is unlimited (float value range), i.e. clipping it (if necessary) is up to the patch

17.2.3 modparam shortcut

In plugin mode, patches usually declare a mod for each param so it can be modulated via the modulation matrix.
The modparam and mp keywords declare both a param and a mod, and auto-prefixes the names with p_ and m_:
id myplugin

modparam amp   # declare both param p_amp and mod m_amp

<out:
  sin
  * $m_amp  # read ($p_amp + $m_amp)
modparam example 1
id myplugin

mp amp   # declare both param p_amp and mod m_amp

<out:
  sin
  * $m_amp  # read ($p_amp + $m_amp)  (recommended syntax)
  * $amp    # read ($p_amp + $m_amp)  (easier to read but might be ambiguous)
modparam example 2 (shortcut syntax)

17.2.4 Args and envelopes

(Regular) args and curves (spline envelopes) will be baked into the plugin source.

17.2.5 Audio input

The initial value is the output of the previous sound module (voice plugin, sample, VST effect, ..).
id     my_amp
name   "my amp"
author bsp
cat    amp

param p_level 1
mod   m_level

<out:
  * $m_level
simple "amp" plugin

17.2.6 init

The <init: section can be used to implement macro parameters that calculate additional internal (voice state) variables from user defined parameters. Code placed in this section will be executed everytime a new note is played.

17.2.7 prepare

The <prepare: section is evaluated before the main section(s), and after the (optional) <init: section.
Heavy calculations, which do not need to be evaluated per sample frame, should be placed in the prepare section.
Code placed in this section will be executed once per processed block. This can be used to implement macro modulation that reads the current modulation variables and writes one or many (internal voice-state) variables.

17.2.8 Free runnning oscillators

By default, the phase and noise generator states reset when a new note is played.
The sin, tri, saw, pul, pha, fsr, nos modules have a (constant) reset input that determines whether the reset occurs at note on (reset=1, default), or just once when the voice is allocated (reset=0). The cycle input should be set to 0 (cycle=0).
Example:
<out:
  fsr cycle=0 reset=0
free-running linear feedback shift register noise oscillator

17.3 Stereo processing

By default, the audio input and output is monophonic. However, when (at least one of) the rdl, rdr, wrl, wrr modules is used, the audio signal path becomes stereo.
In stereo mode, the default output variable (out in the generated code) is still available but will not be written to the audio input.
Instead, the wrl / wrr modules take the previous output value and write it to the left / right channel output.
Vice versa, rdl / rdr read the current left / channel input.
Example:
id stereo_test_1

# declare param p_amp and mod m_amp
modparam detune 0.5 0 1

var v_freq_l smooth=1
var v_freq_r smooth=1

<prepare:
  # calc detuned left / right channel oscillator frequencies
  $m_detune
  fma 0.1 -0.05
  . neg
    + 1
    sto v_freq_l
  +1
  sto v_freq_r

<out:
  sin freq=$v_freq_l
  * 0.7
  wrl # write left channel output

  sin freq=$v_freq_r
  * 0.7
  wrr # write right channel output
stereo sine oscillator plugin

17.4 Native code extension modules (C / C++)

In plugin mode, new Cycle modules can be implemented via native code (C / C++) extensions.
The xinc keyword is used to import a native code header file.
The eureka_config.tks:stfx_inc variable sets the default search path for native code extension files (usually $(PROGRAM_DIR)/include/xinc/).
Example:
id ext_mod_test_1
xinc "xamp.h"
modparam drive 0.5 0 1
<out:
  sin
  xamp sat=1
    drive:
      $drive
      fma 2 -1  #  0..1 => -1..1
      bts 4 16  # -1..1 => /4..*16
xamp example
tip_s
the xinc files are read everytime the script is compiled, i.e. changes to the C / C++ code take effect immediately and do not require an application restart

17.4.1 Header file interface definition keywords

During import, the header file is scanned to find the module name, statically compiled configuration items, dynamic inputs, module vars, voice struct fields, and init / prepare / render code to be inserted for each module instance:
/*
 * Native code extension module definition (parsed by Cycle):
 *
 *  @cycle_mod      xamp
 *  @cycle_pre      // %cymod% pre-include code line
 *  @cycle_static   // %cymod% module-static test code line
 *  @cycle_init     // %cymod% init function test code line
 *  @cycle_new      // %cyid% voice_new() test code line
 *  @cycle_delete   // %cyid% voice_delete() test code line
 *  @cycle_voice    // %cyid% voice_t field struct test code line
 *  @cycle_noteon   // %cyid% noteon function test code line
 *  @cycle_input    drive 0.5
 *  @cycle_config   sat 0  0=off 1=on
 *  @cycle_emit     %cyio% *= %cyin_drive%;
 *  @cycle_emit     if(%cycf_sat% > 0) %cyio% = tanhf(%cyio%);
 *
 */
xamp example extension module
In this (very simple) example module, the entire native code implementation is written using the @cycle_emit keywords (each one adds a line of code for each module instantation).
In a more complex module, the header file will contain additional C structs or C++ classes. See the x303.h, xldf.h, xrlf.h and xrm.h example files.
tip_s
while the main extension file is just a .h (or .hpp) header, this may use all C / C++ features and may include additional .c / .cpp / .cxx / .h / .hpp files.

17.4.2 Module input and output

Extension modules may read the output of the previous module from %cyio% (float) and also write new output to it.

17.4.3 Static configuration items

Configuration options declared via @cycle_config will be turned into #defines and can be inserted anywhere in a (module-instance specific) @cycle_ code line via the %cycf_<config_item_id>% syntax.
In a Cycle script, these will look like regular inputs (e.g. the sat input in the xamp example above).
tip_s
When static configuration items are used in conditional statements (if, switch, ..), the C/C++ compiler will not generate runtime checks and instead optimize the code for the current configuration
tip_s
an (optional) list of constant names can be assigned to each config item (e.g. 0=off 1=on)

17.4.4 Dynamic inputs

Inputs declared via @cycle_input will be evaluated at runtime. The current input value for a module instance is stored in a variable which can be refered to via %cyin_<input_id> anywhere in a (module-instance specific) @cycle_ code line.
note_s
Unless the module is very simple, the input values are usually passed to a user-defined calc() / process() function or method.

17.4.5 Voice fields

The native code implementation for a calc() method is usually placed in a class which is instantiated per voice, and per module instance (see include/xinc/xrm.h):
 [..]
 * @cycle_voice  je_ringmod_state_t %cyid%;
 [..]
je ringmod voice field instance
The field can (optionally) be reset at note on (see include/xinc/x303.h):
 [..]
 * @cycle_noteon voice->%cyid%.reset(voice->sample_rate, voice->sr_factor);
 [..]
303 filter voice field note on reset
tip_s
voice→sample_rate stores the host's output sample rate (e.g. 32000, 44100, 48000, 96000). voice→sr_factor stores the ratio between the patches reference sample rate (def=48kHz) and patch-internal oversampling factor and the host sample rate (e.g. when both host and reference sample rate are set to 48kHz and the oversampling factor is 4, sr_factor is set to 1/4). A properly written extension module behaves the same at all sample rates and oversampling factors so make sure to incorporate sr_factor in your code (e.g. when calculating envelopes or filter cutoffs).

17.4.6 Extension modules

17.4.6.1 x303

A 303-ish filter (written in 1998 by Andy Sloane).

Inputs

freqcutoff frequency (0..1)
qresonance (0..1)
trigenvelope trigger (set to 1 to trigger at note-on)
envmodfilter envelope modulation amount
envdcyfilter envelope decay

17.4.6.2 xdi

A diode wave shaper (based on JE's ringmod which is based on Julian Parker's A simple model of diode-based ring-modulator).

Inputs

constvbforward voltage-bias
constvbvllinear voltage threshold
constslopelinear voltage slope

17.4.6.3 xdly

A delay.

Inputs

tnormalized delay time (0..1 ⇒ 0..delaybuffersize-1)def=0.5
fbfeedback amount (0..1)def=0.0
wetdry / wet mixdef=0.5

17.4.6.4 xfs

A frequency shifter based on Squinky Lab's sql_bootyshifter.

Inputs

freqfrequency shift (-f..f)
rangerange in Hz

17.4.6.5 xfsxy

A frequency shifter based on Squinky Lab's sql_bootyshifter.
Dedicated x / y inputs allow for more fine-grained control over the sine / cosine inputs.

Inputs

xsine input
ycosine input

17.4.6.6 xldf

A Moog-ish ladder filter (adapted from a musicdsp.org post in 2010)

Inputs

freqcutoff frequency (0..1)
qresonance (0..1)
constmode0=lpf 1=hpf 2=bpf

17.4.6.7 xlrf

The sum of the Linkwitz-Riley (Butterworth squared) HP and LP outputs, will result an
 all-pass filter at Fc and flat magnitude response - close to ideal for crossovers.
Lubomir I. Ivanov
based on an implementation by moc.snoitcudorpnrec@mij

Inputs

freqcutoff frequency (0..1)
mode0=lpf 1=hpf

17.4.6.8 xrm

A four quadrant diode ring modulator (based on JE's ringmod which is based on Julian Parker's A simple model of diode-based ring-modulator.

Inputs

acarrier input
bmodulator input
alvlcarrier level
blvlmodulator level
offoffset
vbforward voltage-bias
vbvllinear voltage threshold
slopelinear voltage slope
constapolcarrier polarity (0..2)
constbpolmodulator polarity (0..2)
constmode0=ring 1=sum 2=diff 3=min 4=max

17.5 Export

Press lctrl-p or lctrl-w in the Cycle dialog to export the current patch as a plugin (and (re)load it).
Press lctrl-lshift-p to toggle auto-export when patch is saved / edited (lctrl-s or after arg changes).

17.6 Compiler installation

Cycle uses Clang / GCC to compile the plugins.
No knowledge of C/C++ is required to create new plugins but the following (free) build tools have to be installed so the application can invoke them in the background.

17.6.1 Windows

Please install MSYS2 https://www.msys2.org/, then (in MSYS2 bash console) type:
$ pacman -Syu
$ pacman -S base-devel gcc
to update the package database and install the compiler environment.
note_s
In case MSYS2 was not installed to its default directory, edit eureka_config.tks and update the stfx_cc_msys2_dir variable accordingly.

17.6.2 macOS

Please install brew https://brew.sh/ (which will also install the XCode commandline tools):
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
(see https://mac.install.guide/commandlinetools/3.html)
note_s
XCode updates are installed through the macOS Software Update and their availability appears to depend on the currently installed macOS version

17.6.3 Linux

On Debian or Ubuntu, type
$ sudo apt update
$ sudo apt install build-essential

Valid HTML 4.01 Transitional

Document created in 82ms on 24-Dec-2024 00:14:43