1 Table of Contents
1
Table of Contents
2
Preface
3
Global Keywords
4
Values and sample output
5
Output lanes
6
Constants
7
Args
8
Variables
9
Operators
10
Modules
10.1
abs - discard sign of previous output
10.2
adsr - four stage envelope generator
10.3
apf - all-pass filter
10.4
bit - bit-crush previous output
10.5
boo - boost previous output
10.6
box - box filter previous output
10.7
bpf - —reserved—
10.8
bts - bipolar to scale, e.g. -1..1 => 1/16..*1..*16
10.9
buf - —reserved—
10.10
bus - read sample from (cross-zone) voicebus
10.11
ceq - shortcut for cmp ==
10.12
cge - shortcut for cmp >=
10.13
cgt - shortcut for cmp >
10.14
cle - shortcut for cmp <=
10.15
clk - generate clock pulses
10.16
clp - clip previous output
10.17
clt - shortcut for cmp <
10.18
cmp - run conditional statement sequence
10.19
cne - shortcut for cmp !=
10.20
con - —reserved—
10.21
dec - decrement variable
10.22
div - divide
10.23
dly - delay
10.24
drv - —reserved—
10.25
end - close current lane
10.26
env - AD/S/R envelope generator
10.27
eq3 - 3-band equalizer
10.28
fed - detect logic signal falling edge
10.29
fix - fix NaN and Inf (debug aid)
10.30
fld - fold previous output
10.31
fam - fused add multiply
10.32
fma - fused multiply add
10.33
frc - fractional part of previous output
10.34
fsr - linear feedback shift register noise
10.35
fwr - full-wave rectification
10.36
hbx - high-pass box filter
10.37
hwr - half-wave rectification
10.38
hld - sample and hold previous output
10.39
hpf - high-pass filter previous output
10.40
if - conditional code generation
10.41
inc - increment variable
10.42
int - emit sub-tree in integer mode
10.43
ipl - Interpolate from a to b at time t
10.44
itg - integral part of previous output
10.45
kbd - MIDI note keyboard tracking
10.46
lle - log / lin / exp scaling
10.47
log - calc logarithm of previous output
10.48
lop - loop statement sequence
10.49
lpf - 1RC low pass filter
10.50
lut - read from look-up table
10.51
mac - update interpolated macro fields
10.52
map - remap value
10.53
mkv - markov-chain element
10.54
mul - multiply variable
10.55
neg - negate previous output
10.56
nos - generate xor-shift white noise
10.57
not - invert previous logic signal output
10.58
note - return voice note number (plugins)
10.59
nth - pass every nth clock signal
10.60
out - write sample to (cross-zone) voicebus
10.61
pha - phase accumulator
10.62
p2s - exponential power-of-two scaling
10.63
pow - raise previous output to the power of 'f'
10.64
pre - Return previous output
10.65
pul - generate pulse wave
10.66
qua - quantize previous output
10.67
rcp - reciprocal (1 / x)
10.68
rdl - read left channel (plugin) input
10.69
rdr - read right channel (plugin) input
10.70
red - detect logic signal rising edge
10.71
rep - loop / unroll statement sequence
10.72
rev - —reserved—
10.73
rmp - generate linear ramp
10.74
saw - generate sawtooth wave
10.75
sat - saturate previous output
10.76
set - set variable
10.77
sin - generate sine wave
10.78
slf - read past sample output
10.79
slw - slew
10.80
spd - return voice note speed (plugins)
10.81
spr - dampened spring
10.82
ssl - sine slice oscillator
10.83
sta - store variable in LUT
10.84
sto - store previous output in variable
10.85
svf - state-variable filter (lpf, hpf, bpf, notch)
10.86
tan - tangens
10.87
tanh - saturate previous output
10.88
tmp - instantiate template
10.89
trc - debug-trace last output value
10.90
tri - generate triangle wave
10.91
tsq - generate trigger sequence
10.92
vel - return voice velocity (plugins)
10.93
vpl - invoke voice plugin
10.94
vsq - generate value sequence
10.95
vst - declare variable and store previous output
10.96
wrl - write left channel (plugin) output
10.97
wrp - wrap previous output
10.98
wrr - write right channel (plugin) output
10.99
xfd - crossfade
10.100
zlp - read looped sample frame from other zone
10.101
zon - read sample frame from other zone
10.102
zsq - sequence zone sample
11
Curve Lookup Tables (LUTs)
12
Procedural Lookup Tables (LUTs)
13
Macros
14
Maps
15
Templates
16
Includes
17
Plugins
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:
- hi-fi uses 32bit floating point calculations while lo-fi uses 16bit fixed point calculations with signed 8bit sample output
- in lo-fi mode,
Cycle
can be used to calculate sample data for Amiga MOD or TSR (Tiny Synergy Replay) files.
- lo-fi sub-trees can be embedded in hi-fi patches (see int).
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.
Keyword | Description |
arg | Declare argument (will be editable in UI) (shortcut form: a ) |
arg_values | Argument preset values (shortcut form: av ) |
var | Declare variable (shortcut form: v ) |
oversample_factor
oversample | Oversample factor (0.03125..32, def=1) (also supports fractional values, e.g. "1/8")
|
Oversampling is ignored when exporting to lo-fi 8bit "C" sample generator source |
|
8 is usually a good factor when targeting ~16kHz samples (=128kHz internal rate) |
|
oversample_quality | Oversample quality (0..10, def=4)
|
Oversampling is ignored when exporting to lo-fi 8bit "C" sample generator source |
|
dither | y , n , or mode (0=off, 2=dithering, 3=distortion shaping, ..8=adaptive noise shaping) |
rate | Override zone sample-rate |
freq | Output 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 |
oct | Octave shift (-4..4) |
dur | Override sample duration (frames or, when followed by "ms", milliseconds) |
wt_w | Override wavetable width (1..16 in 2D mode, 1..128 in 1D mode) |
wt_h | Override wavetable height (1..16 in 2D mode, 1 in 1D mode) |
wt_cyclelen | Override wavetable cycle length (number of frames. 1..1048576) |
wt_freqs | Per-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
|
the current cycle freq is available via $wt_freq variable during synthesis |
|
skip | Skip output of first "n" sample frames or, when followed by "ms", milliseconds |
xfade | Crossfade end of loop with samples before loop start. Sample frames or loop-len percentage (when followed by %) |
mirror | Calculate first half of waveform and mirror to second half (single cycle waveforms and wavetables) |
curve | Declare curve lookup-table. Must be followed by curve index (0..15) and curve id (e.g. "amp") (shortcut form: c ) |
lut | Declare procedural lookup-table. Must be followed by LUT id, optional mode (voice/shared), optional table size (default=256), and optional "inv" (inverse) flag |
zone | Declare external zone reference. Must be followed by ref idx (0..) and zone id (e.g. "myzone", or "myothersmp.myzone") (shortcut form: z ) |
macro | Declare macro parameter set(s). See macros. |
inc | include 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:
Keyword | Description |
id | Plugin id (mandatory) |
name
| Plugin name (optional. def=<id>)
|
the plugin name (and dll / so / dylib) will be prefixed with cycle_ |
|
author | Plugin author (optional) |
cat
category
|
Plugin category (optional. def="osc")
| unknown | limiter | pan | transient |
| ringmod | enhance | pitch | convolver |
| waveshaper | dac | chorus | mixer |
| filter | lofi | flanger | comparator |
| eq | gate | delay | utility |
| compressor | amp | reverb | osc |
|
param | Declare plugin parameter (shortcut form: p ) |
param_values | Declare plugin preset values (shortcut form: pv ) |
mod | Declare plugin modulation target (shortcut form: m ) |
modparam | Declare plugin parameter and modulation target (param will be prefixed with p_ , modulation with m_ ) (shortcut form: mp ) |
xinc | import 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:
<layer_1:
sin
<layer_2:
sin f=1.5
hello, layered world.
Each lane consists of a list or tree of
modules:
<out:
sin
phase:
sin
* 0.1
abs
fma
lane module tree
|
fma converts from unipolar (0..1) to bipolar (-1..1) values by default ( *2 - 1 ) |
|
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
|
the init section must occur before the first audio lane |
|
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
|
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
|
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.
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
:
|
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
|
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
|
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
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
|
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.
Operator | Id | Description |
= | OP_REP | Replace |
+ | OP_ADD | Add |
+sr | OP_ADD_SR | Add (over)samplerate-independent value |
- | OP_SUB | Subtract (a-b) |
r- | OP_RSUB | Reverse-Subtract (b-a) |
* | OP_MUL | Multiply |
*sr | OP_MUL_SR | Multiply by (over)samplerate-independent value |
& | OP_AND | Bitwise and |
| | OP_OR | Bitwise or |
^ | OP_EOR | Bitwise exclusive-or |
&& | OP_LAND | Logical and (a > 0) & (b > 0) |
|| | OP_LOR | Logical or (a > 0) | (b > 0) |
^^ | OP_LEOR | Logical eor (a > 0) ^ (b > 0) |
!& | OP_LNAND | Logical nand !((a > 0) & (b > 0)) |
!| | OP_LNOR | Logical nor !((a > 0) | (b > 0)) |
!^ | OP_LNEOR | Logical neor !((a > 0) ^ (b > 0)) |
m | OP_MIN | Minimum |
x | OP_MAX | Maximum |
M | OP_ABSMIN | Absolute minimum |
X | OP_ABSMAX | Absolute maximum |
. | OP_NONE | Emit but discard result of sub-tree |
_ | OP_SKIP | Skip sub-tree (do not emit) |
am | OP_AM | Arithmetic mean |
qm | OP_QM | Quadratic mean sqrt(aa+bb) |
QM | OP_QMU | Unipolar Quadratic mean rescaled to -1..1 |
hm | OP_HM | Harmonic mean (2*a*b)/(a+b) |
HM | OP_HMU | Unipolar Harmonic mean rescaled to -1..1 |
gm | OP_GM | Geometric mean sign(a*b)*sqrt(abs(a*b)) |
GM | OP_GMU | Unipolar Geometric mean rescaled to -1..1 |
Operators
|
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
|
|
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
):
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
|
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.
|
some module inputs have implicit previous output values, like the pd (phase-distortion) insert of the pha module. |
|
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
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).
|
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
|
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
10.5 boo
Boost sample deltas of previous output.
Example
<out:
sin
boo amount=0.6
boo 0.6
boost sample deltas
10.6 box
Box filter previous output.
Simple but computationally inexpensive.
Example
<out:
saw
box freq=0.5
box 0.5
box lowpass filter
10.7 bpf
reserved
|
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
|
uses div input when mul is not connected |
|
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)
|
only supported in STFX plugin mode (returns 0 otherwise) |
10.11 ceq
var t
<out:
$t
ceq 0
1
sto t
ceq example
10.12 cge
var t
<init:
set t 0
<out:
$t
+sr 0.0001
cge 1
1
sto t
cge example
10.13 cgt
var t
<init:
set t 0
<out:
$t
+sr 0.0001
cgt 1
1
sto t
cgt example
10.14 cle
var t
<init:
set t 0
<out:
$t
cle 0.5
+sr 0.001
sto t
cle example
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
10.16 clp
Clip previous output
Examples
<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)
10.17 clt
var t
<init:
set t 1
<out:
$t
+sr -0.001
clt 0
0
sto t
clt example
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
10.19 cne
var t
<out:
$t
cne 1
1
sto t
cne example
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
10.22 div
Divide values.
Example
arg p_num 16 1 32
<out:
div 1.0 $p_num
output 1 / $p_num
|
useful in conjunction with rep / lop , e.g. when indexing LUTs |
|
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
|
in lo-fi / integer mode, the delay buffer size is limited to 32767 frames |
|
the delay size is multiplied by the oversample factor |
|
linear interpolation is only supported in hi-fi / float mode |
10.24 drv
reserved
10.25 end
Close current lane.
|
optional. can be used for placing lut declarations in between lanes. |
|
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 s
ustain position.
This can (also) be used for audio-rate waveform synthesis.
|
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
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
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
|
this module has no inputs |
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
|
ceil sets min=-ceil, max=+ceil
|
<out:
sin
* 2.0
fld min=-1.0 max=1.0
fold sine oscillator (separate min / max)
|
min / max override default ceil
|
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
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
|
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
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
|
the (default) unsigned mode is especially useful for oscillator phases (i.e. to wrap any value into the normalized 0..1 range) |
|
itg does the opposite and discards all fractional bits |
10.34 fsr
Generate linear feedback shift register ("C64") noise (normalized to -1..1).
Examples
id noise
<out:
fsr seed=$4489 bits=1.0 reset=0
noise voice plugin with custom seed and no note-on reset
|
changing the number of bits does not make an audible difference, except when the output is used as a controller value |
|
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.
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
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.
10.38 hld
Sample and hold previous output.
Example
|
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
10.39.1 Filter configurations
cfg | cutoff | resonance |
0 | 127 | 0 |
1 | 127 | 108 |
2 | 122 | 0 |
3 | 122 | 108 |
4 | 115 | 0 |
5 | 115 | 100 |
6 | 110 | 0 |
7 | 110 | 100 |
8 | 100 | 0 |
9 | 100 | 97 |
10 | 90 | 0 |
11 | 90 | 70 |
12 | 77 | 0 |
13 | 77 | 79 |
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
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
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
|
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:
- when
a
is connected and b
is not, the previous output is used as the destination
- when
b
is connected and a
is not, the previous output is used as the source
- when
a
and b
are connected and t
is not, the previous output is used as the interpolation amount
- when
a
, b
and t
are connected, the previous output is ignored
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
|
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
|
frc does the opposite and discards all integer bits |
10.45 kbd
mode=0
:
- scale input by 127 (normalized float to MIDI note), add
(current_note - 64 + (off * 12))
, clamp to 0..127 (when clamp=1
), then convert the note to its corresponding frequency and divides it by the sample rate (back to normalized 0..1 range).
- ignore
ctr
input (can use off
instead)
- useful for filter cutoff frequencies (linear to exponential).
- factors in current samplerate and oversamplefactor
- caution when passing the output to another module which also does this !
mode=1
:
- equal tempered rate scaling
- multiply input by note rate. keyboard tracking is centered around
ctr
note (def=60=5*12=C-5).
- e.g. at center note and
off=0
, rate is 1.0. at one octave above it's 2.0, and at one octave below it's 0.5
- ignore
scl
and clamp
inputs
- useful for scaling oscillator
freq
(set osc fixed=1
to disable built-in keyboard tracking)
mode=2
:
- linear(exponent) rate scaling
- multiply input by note rate. keyboard tracking is centered around
ctr
note (def=60=5*12=C-5).
- e.g. at center note and
off=0
, rate is 1.0. at one octave above it's 2.0, and at one octave below it's 0.5
- ignore
scl
and clamp
inputs
- can also be used for scaling oscillator
freq
(set osc fixed=1
to disable built-in keyboard tracking)
- the CPU load is lighter but the rate does not exactly follow the equal tempered scale
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
|
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
|
only supported in float-mode (fall back to float in int mode) |
10.47 log
Return logarithm of previous output.
Example
|
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
|
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. |
|
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
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
|
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.
10.52 map
Remap previous output value using a non-contiguous table.
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.
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
10.55 neg
Negate previous output
Example
10.56 nos
Render white noise via xor-shift pseudo random number generator
Example
<out:
nos seed=0x44894489 cycle=0
xor-shift noise
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.
|
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
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)
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
|
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)
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
|
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 ! |
|
only supported in float-mode (fall back to float in int mode) |
|
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:
pow
base:
2.71828182845904523 # e
exp:
sin
generic pow with base input connected
|
useful for bending rmp envelopes into non-linear shapes |
|
constant exponents 2 and 3 behave differently than arbitrary (or dynamic) exponents, i.e. -2²=+4 while e.g. -2^4=-16 |
|
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).
|
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.
..must be written as:
This is currently required due to a parser-issue which may be proper-fixed later. |
10.65 pul
Generate pulse wave
Example
<out:
pul freq=1 phase=0 width=0.5 vsync=2.5
vsynced pulse (rectangle) oscillator
|
windowed vsync is only supported in float-mode (ignored in integer mode) |
|
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
|
useful for controllers, e.g. FM ratios |
|
when num=1, simply discard fractional bits (cast to integer) |
|
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.
|
available only in plugin mode. |
10.69 rdr
Read right channel input sample.
|
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
|
this module has no inputs |
10.71 rep
Loop / unroll statement sequence.
|
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
10.72 rev
reserved
10.73 rmp
Generate linear ramp
Example
<out:
rmp millisec=8 start=0 end=1 cycle=1
linear 8ms ramp
linear ramp from 0..1 that spans entire sample
10.74 saw
Generate sawtooth wave
Example
oversample 2
<out:
saw freq=2 phase=0 vsync=2.5
vsynced sawtooth oscillator (second harmonic)
|
windowed vsync is only supported in float-mode (ignored in integer mode) |
|
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)
|
useful for saturation-like non-linear distortion (converges to -1..1) |
|
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:
set f 1.23
set f value=1.23
rep 16
inc f 0.1
increment f 16 times
10.77 sin
Generate sine wave
Examples
<out:
sin freq=1 phase=0 vsync=2.5 window=sin
windowed / vsynced sine oscillator
sine osc phase distortion
|
windowed vsync is only supported in float-mode (ignored in integer mode) |
|
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. |
|
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
10.79 slw
Slew from a
to b
with configurable up / down amounts.
The module behaves differently depending on which inputs are connected:
- when
a
is connected and b
is not, the previous output is used as the destination
- when
b
is connected and a
is not, the previous output is used as the source
- when neither
a
and b
are connected, the previous output is used as the destination and an implicit (source) state variable is allocated
(initially 0, then continously updated to the slw
output value)
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
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)
|
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
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.
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
|
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
10.86 tan
Calculate the tangens of the previous output value.
|
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) |
|
forces fallback to hifi / float mode |
10.87 tanh
10.88 tmp
This is the (deprecated) long-form of the ~
template module instantiation command.
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
10.90 tri
Generate triangle wave
Examples
<out:
tri freq=1 phase=0 vsync=2.5
vsynced triangle oscillator
|
windowed vsync is only supported in float-mode (ignored in integer mode) |
|
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
|
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
|
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.
|
See Eureka sampler "FX" tab for a list of available plugins. |
|
Plugin setups can be exported to single- or multi-line Cycle scripts (clipboard) via the Eureka sampler "FX" tab context menu |
|
vpl modules cannot be exported to "C" source
|
10.94 vsq
Generate value sequence.
|
please use step /s input to return sequence value for given sequence step index (initial value when evaluating step input) |
10.95 vst
Declare variable and store previous output.
|
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.
|
available only in plugin mode. |
|
implicitely enables STEREO plugin mode |
10.97 wrp
Wrap previous output value at ceiling.
Examples
<out:
sin
* 2.0
wrp ceil=0.8
wrap into -0.8..0.8 range
|
ceil sets min=-ceil, max=+ceil
|
<out:
sin
* 2.0
wrp min=-0.5 max=0.9
wrap into -0.5..0.9 range
|
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
10.98 wrr
Write right channel output sample.
|
available only in plugin mode. |
|
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
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
|
sample len is determined by last loop length of the specified zone |
|
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
|
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
|
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. |
|
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.
|
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
|
lookup tables can greatly improve the rendering performance by precalculating recurring values |
|
the default table size is 64 |
|
the default table mode is voice |
|
LUT init sections must be named lut_<id> (e.g. <lut_pow: ) |
|
if no init section has been declared for a LUT used by a "lut" module, compilation will fail |
|
the shared LUTs are calculated after the <init: section |
|
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)
|
the maximum number of (auto-generated) permutations is limited to 16384 |
|
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
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).
|
variables declared within a template are automatically prefixed with a unique id during instatiation |
Template applications:
- act as a namespace for variables (when instantiated in- or outside of a lane)
- generate args, params, mods (when instantiated outside of a lane or when using the global section)
- generate audio layers (when instantiated outside of a lane, see Audio lanes)
- insert parametrizable code into a lane (when instantiated inside a lane or when inserting a lane of an explicitily named global template instance (see init, prepare, calc lanes))
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.
|
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
|
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
|
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.
|
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:
- slf and zsq modules may not be used
- no references to voice plugins (
vpl
)
dur
, wt_w
, wt_h
, wt_cyclelen
, wt_freqs
, skip
, xfade
, mirror
, zone
keywords are not allowed
|
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
Voice plugin patches must declare a unique plugin id:
id myplugin_v1
name "my plugin v1"
author bsp
cat osc
|
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.
|
parameter names may be prefixed with p_ (prefix will be removed during export) |
|
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:
Normalized parameters are scaled to their (optional) min / max value range when they are accessed:
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:
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)
|
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
|
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:
Name | Shortcut name | Description |
param | p | associated parameter name (can be omitted when it's the third word) |
size | s | array size (1..256) |
var | v | num variations (1..32) |
min | - | minimum storage value (-1..1, def=0) |
max | - | maximum storage value (-1..1, def=1) |
dpyMin | dmin | minimum display value (arbitrary float) (def=storage min) |
dpyMax | dmax | maximum display value (arbitrary float) (def=storage max) |
dpyPrec | dprec | display value editing precision (num fractional digits, 0..7) (def=5) |
Array parameter attributes
|
the initial values of the first variation of an array are defined by the associated (bezier) curve |
|
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). |
|
when wrap-around mode is used (clamp=0 ), the array size should be a power of two |
|
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. |
|
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. |
|
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
.
declare modmatrix destination
|
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.
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
|
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 |
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.
|
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. |
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 #define
s 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).
|
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 |
|
an (optional) list of constant names can be assigned to each config item (e.g. 0=off 1=on ) |
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.
|
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
|
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).
17.4.6.2 xdi
17.4.6.3 xdly
A delay.
17.4.6.4 xfs
A frequency shifter based on Squinky Lab's sql_bootyshifter.
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.
17.4.6.6 xldf
A Moog-ish ladder filter (adapted from a musicdsp.org post in 2010)
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
17.4.6.8 xrm
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
$ pacman -Syu
$ pacman -S base-devel gcc
to update the package database and install the compiler environment.
|
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)"
|
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
Document created in 82ms on 24-Dec-2024 00:14:43