1 Table of Contents
2 Preface
Cycle
is a modular soft-synth which emits TKS
or C
sample generation code.
It operates either in hi-fi (script code only), or lo-fi mode (script or C code).
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.
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.
3 Global Keywords
Global keywords may appear at the beginning of a Cycle script.
Keyword | Description |
arg | Declare argument (will be editable in UI) |
var | Declare variable |
oversample_factor | Oversample factor (0.03125..32, def=1) (also supports fractional values, e.g. "1/8")
note: 8 is usually a good factor when targeting ~16kHz samples (=128kHz internal rate)
caution: Oversampling is ignored when exporting to "C" source |
oversample_quality | Oversample quality (0..10, def=4)
caution: Oversampling is ignored when exporting to "C" source |
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 ) |
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) |
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
note: 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 | Calc first half waveform and mirror to second half (single cycle waveforms and wavetables) |
curve | Declare curve lookup-table. Must be followed by curve index (0..3) and curve id (e.g. "amp") |
zone | Declare external zone reference. Must be followed by ref idx (0..) and zone id (e.g. "myzone", or "myothersmp.myzone") |
Global 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 Curve Lookup Tables (LUTs)
Cycles supports up to 16 cubic-bezier curve tables that can be used as amp or filter envelopes, or sample waveshapers.
Curve tables are internally converted (sampled) to short, power-of-two sized arrays. The array sizes can be 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 0 amp
<out:
sin
* rmp 0 0 0.99
lut amp
Using a curve LUT as an amp envelope
6 Output lanes
Lanes divide the script into multiple output sections.
Each script must contain the main section (named out
):
<out:
sin
6.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).
6.2 Init lane
A script may contain an optional init
section that is evaluated before the main section.
However, this is still work in progress (and should not be used for now).
7 Constants
Constants are typically used for static module parameters, e.g.
<out:
sin freq=1.5
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
8 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
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
|
args and variables share the same namespace |
9 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
|
args and variables share the same namespace |
10 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 |
- | OP_SUB | Subtract (a-b) |
r | OP_RSUB | Reverse-Subtract (b-a) |
* | OP_MUL | Multiply |
& | OP_AND | Bitwise and |
| | OP_OR | Bitwise or |
^ | OP_EOR | Bitwise exclusive-or |
m | OP_MIN | Minimum |
x | OP_MAX | Maximum |
M | OP_ABSMIN | Absolute minimum |
X | OP_ABSMAX | Absolute maximum |
. | OP_NONE | Discard / Ignore |
Operators
The default operator is replace
.
11 Modules
Modules generate new sample frames or process the output of the previous module.
Modules may be nested (stacked), i.e. module inputs can be controlled by the output of other modules.
Example:
<out:
sin
freq:
sin 5.0
* 0.25
+ 1
* 3.0
clp
Static module parameters are set by key=value
pairs, or simply by value (in internal parameter order)
lpf freq=0.4 res=0.5
lpf 0.4 0.5
Dynamic module parameters must be placed on separate, indented lines.
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.
11.1 abs
Return absolute value of argument.
Example:
<out:
abs -42
11.2 bit
Bit-crush (shift) previous output.
Example:
<out:
sin
bit shift=0.5
bit 0.5
shift
range is 0..1
11.3 boo
Boost sample deltas of previous output.
Example:
<out:
sin
boo amount=0.6
boo 0.6
amount
range is 0..1
11.4 box
Box filter previous output.
Example:
<out:
saw
box freq=0.5
box 0.5
freq
range is 0..1
11.5 bpf
not implemented, yet
|
use svf in mode=2 instead |
11.6 buf
not implemented, yet
11.7 clp
Clip previous output
Example:
<out:
sin
* 7
clp
ceil
range is 0..1 (default=1)
11.8 dec
Decrement variable
var f
<out:
set f 0
rep 16
dec f 0.1
dec f delta=0.1
dec f
delta:
0.05
* 2
11.9 div
Divide constant values.
Example:
arg p_num 16 1 32
<out:
div 1.0 $p_num
|
useful in conjunction with rep / lop , e.g. when indexing LUTs |
11.10 dly
not implemented, yet
11.11 drv
not implemented, yet
11.12 env
not implemented, yet, may be removed
(use
rmp and
lut instead)
11.13 fld
Fold previous output value at ceiling.
Example:
<out:
sin
* 2.0
fld ceil=1.0
|
ceil sets min=-ceil, max=+ceil
|
Example:
<out:
sin
* 2.0
fld min=-1.0 max=1.0
|
min / max override default ceil
|
|
all oscillators can be used as wavefolders by setting their speed to 0, then modulating their phase input |
11.14 frc
Calc fractional part of previous output
Example:
<out:
sin
* 3.3
frc
11.15 fsr
Generate linear feedback shift register ("C64") noise (normalized to -1..1).
Example 1:
<out:
fsr
Example 2:
<out:
fsr seed=$4489 bits=1.0 cycle=1
seed
is the initial shift register state (16 bits). must not be 0. Default is $4489
bits
determines the number of output bits (1.0=all bits, 0=1 bit (actually two, sign+msb). Default is 1.0.
cycle
determines whether the shift register state resets at each (wavetable) cycle. Default is 1 (true).
shift1
is the first feedback shift (negative=shift left). -15..15. def=7
shift2
is the second feedback shift. -15..15. def=-9
shift3
is the third feedback shift. -15..15. def=13
|
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
|
11.16 hbx
High-pass Box filter previous output.
Example:
<out:
saw
hbx freq=0.5
hbx 0.5
freq
range is 0..1
11.17 hld
Sample and hold previous output.
Example:
<out:
hld rate=0.5
hld
range is 0..1
|
if 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 |
11.18 hpf
High-pass filter previous output. Uses preconfigured, static cutoff/resonance configurations (biquad coefficients).
Example:
<out:
saw
hpf cfg=3
11.18.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 |
11.19 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
11.20 inc
Increment variable
var f
<out:
set f 0
rep 16
inc f 0.1
inc f delta=0.1
inc f
delta:
0.05
* 2
11.21 lop
Loop statement sequence. Supports dynamic loop iterators.
var i
var num
<out:
2.0
* 4.0
sto num
0.0
sto i
lop $num
+ 0.01
num
range is 0..1023
|
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 |
11.22 lpf
1RC low pass filter
<out:
saw
lpf freq=0.4 res=0.5
lpf 0.4 0.5
freq
range is 0..1
res
range is 0..1
11.23 lut
Use previous output as index for curve look-up table (LUT)
Example:
curve 0 amp
<out:
sin
* rmp 0 0 0.99
lut amp clamp=0 lin=0
clamp
must be either 0 (repeat lut) or 1 (clamp to last index)
lin
must be either 0 (nearest neighbour) or 1 (linear interpolation)
|
the lut module is often combined with a rmp module that generates the look-up index |
11.24 neg
Negate previous output
Example:
<out:
saw
neg
11.25 nos
Render white noise via xor-shift pseudo random number generator
Example:
<out:
nos seed=0x44894489 cycle=0
seed
is the random seed (a 32bit integer). the default seed is 0x3d9fb971
cycle
controls whether the RNG is reset for each wavetable cycle (default=1)
11.26 pha
Generate phase output (0..1 normalized saw-wave).
Example:
curve 0 mywaveshape
<out:
pha freq=1.5 phase=0.25
lut mywaveshape
freq
range is 0.25..4
phase
range is 0..1 (start phase)
11.27 pow
Return previous output raised to the power of 2 or 3
Example:
<out:
sin
pow 3
exp
range is
|
useful for bending rmp envelopes into non-linear shapes |
11.28 pul
Generate pulse wave
Example:
<out:
pul freq=1 phase=0 width=0.5 vsync=2.5
freq
range is 0.25..4
phase
range is 0..1 (start phase)
width
range is 0..1 (length of duty cycle)
vsync
range is 0.01..4 (virtual oscillator sync)
11.29 rep
Loop / unroll statement sequence. The number of iterations must be constant.
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
num
range is 0..1023
11.30 rev
not implemented, yet
11.31 rmp
Generate linear ramp
Example:
<out:
rmp millisec=8 start=0 end=1 cycle=1
millisec
range is 0..4000. 0 auto-adjusts the duration to match the target zone sample length
start
range is 0..15 (start value)
end
range is 0..15 (end value)
cycle
determines whether the ramp position is reset at each new wavetable cycle (0 or 1)
11.32 saw
Generate sawtooth wave
Example:
<out:
saw freq=1 phase=0 vsync=2.5
freq
range is 0.25..4
phase
range is 0..1 (start phase)
vsync
range is 0.01..4 (virtual oscillator sync)
11.33 set
Set variable
var f
<out:
set f 1.23
set f value=1.23
rep 16
inc f 0.1
11.34 sin
Generate sine wave
Example:
<out:
sin freq=1 phase=0 vsync=2.5
freq
range is 0.25..4
phase
range is 0..1 (start phase)
vsync
range is 0.01..4 (virtual oscillator sync)
11.35 slf
Read past sample output ("self" feedback)
<out:
pul
+ slf rate=0.98
clp
rate
range is 0..1
11.36 ssl
Sine slice oscillator.
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).
<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).
When the sequence is less than 8 entries, it will be repeated.
freq
is in the range 0.25..4
phase
is in the range 0..1
mod
is in the range -1..1 (slice modulation)
seq
is in the range 0..1 (0=base slice sequence, 00000001..7777777). Must be const.
modmask
is in the range 0..1 (0=no slices, 1=all slices)
zeromask
is in the range 0..1 (0=no slices cleared, 1=all slices cleared)
mode
is in the range 0..5 (must be const):
Mode | Description |
0 point | ignore fractional (mod) bits, simply recombine waveforms (fastest) |
1 phase | fractional (mod) bits determine source region start offset shift ("phase" mod 1/8 sin) |
2 lerp | fractional (mod) bits are used to lerp sine source region to cosine wave |
3 shift | fractional (mod) bits are used to shift source region (dc offset + reflect/fold) |
4 ang | fractional (mod) bits determine source waveform start offset shift ("phase" +- 2PI) |
5 amp | fractional (mod) bits are used to amplify source region |
SLL mode settings
seqmod
is in the range 0..3 (per-sample sequence modulation)
11.37 sto
Store previous output in variable
Example:
var t
<out:
sin
sto t
* $t
* $t
The variable is initialized with 0 but retains its content over multiple sample frame iterations.
Example:
var sin_val
<out:
$sin_val
+ sin
sto $sin_val
clp
11.38 svf
Process previous output with state-variable filter
Example:
<out:
saw
svf freq=0.4 res=0.5 mode=0
svf 0.4 0.5 0
freq
range is 0..1
res
range is 0..1
mode
0=lpf, 1=hpf, 2=bpf, 3=notch
11.39 tri
Generate triangle wave
Example:
<out:
tri freq=1 phase=0 vsync=2.5
freq
range is 0.25..4
phase
range is 0..1 (start phase)
vsync
range is 0.01..4 (virtual oscillator sync)
11.40 vpl
Process previous output via
stfx voice plugin.
<out:
sin 5.0
vpl "bsp ws quintic" "Dry / Wet"=0.5 Drive=0.39
Example (single line)
arg drive 0.39 min=0 max=1
<out:
sin 5.0
vpl "bsp ws quintic" "Dry / Wet"=0.5 "Drive"=$drive
Example (singe line 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
Example (multi line 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 setup can be exported to single- or multi-line Cycle script (clipboard) via Eureka sampler "FX" tab context menu |
|
vpl modules cannot be exported to "C" source
|
11.41 xfd
Crossfade between previous output and input b
.
arg mod_waveform 0 -1 1
<out:
tri
xfd amount=$mod_waveform
b:
saw
Example (crossfade between triangle and saw)
11.42 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:
zon 0 rate=0.3 phase=0.5
+ zon 1 0.9
clp
rate
range is 0..1
phase
range is 0..1
|
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)) |
11.43 zon
Read sample frame from other zone. Can be used to mix samples or create chords.
Example:
zone 0 otherzone
zone 1 othersmp.otherzone
<out:
zon 0 rate=0.3
+ zon 1 0.9
clp
rate
range is 0..1
|
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 |
Example #2 (minor chord):
zone 0 singlenote_g3
<out:
zon 0
+ zon 0 d#3/g-3
+ zon 0 c-3/g-3
* 0.5
clp
11.44 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
clp
note
range is 1..256 (step note duration)
bpm
range is 10..1000
swing
range is -0.5..0.5
rate
range is 0.125..8
|
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. |
Document created on 31-Mar-2023 11:15:32