Document:Cycle Sample Generator / Soft Synth
Author:bsp
Date:28-Mar-2023

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.
KeywordDescription
argDeclare argument (will be editable in UI)
varDeclare variable
oversample_factorOversample 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_qualityOversample quality (0..10, def=4)
caution: Oversampling is ignored when exporting to "C" source
rateOverride zone sample-rate
freqOutput frequency. Default is 261.63 (middle C, MIDI note 60 / C-5).
alternatively: MIDI note C-1..G-A (followed by finetune, e.g. G-4 -0.2)
octOctave shift (-4..4)
durOverride sample duration (frames or, when followed by "ms", milliseconds)
wt_wOverride wavetable width (1..16 in 2D mode, 1..128 in 1D mode)
wt_hOverride wavetable height (1..16 in 2D mode, 1 in 1D mode)
wt_cyclelenOverride wavetable cycle length (number of frames)
wt_freqsPer-cycle note frequencies/rates (multisampled keymaps)
e.g. wt_freqs c-3 g-3 c-4 "g-4+0.11" c-5 g-5 c-6 g-6 c-7 g-7
note: current cycle freq is available via $wt_freq variable during synthesis
skipSkip output of first "n" sample frames or, when followed by "ms", milliseconds
xfadeCrossfade end of loop with samples before loop start. Sample frames or loop-len percentage (when followed by %)
mirrorCalc first half waveform and mirror to second half (single cycle waveforms and wavetables)
curveDeclare curve lookup-table. Must be followed by curve index (0..3) and curve id (e.g. "amp")
zoneDeclare 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 
caution_s
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 
caution_s
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.
OperatorIdDescription
=OP_REPReplace
+OP_ADDAdd
-OP_SUBSubtract (a-b)
rOP_RSUBReverse-Subtract (b-a)
*OP_MULMultiply
&OP_ANDBitwise and
|OP_ORBitwise or
^OP_EORBitwise exclusive-or
mOP_MINMinimum
xOP_MAXMaximum
MOP_ABSMINAbsolute minimum
XOP_ABSMAXAbsolute maximum
.OP_NONEDiscard / 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
tip_s
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 
tip_s
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 
note_s
ceil sets min=-ceil, max=+ceil
Example:
<out: 
  sin 
  * 2.0 
  fld min=-1.0 max=1.0 
note_s
min / max override default ceil
tip_s
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
note_s
changing the number of bits does not make an audible difference, except when the output is used as a controller value
note_s
fsr noise contains less low frequencies than nos noise

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
note_s
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

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

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
caution_l
some modules (e.g. sin) use variables to store their state. evaluating such modules in a loop will cause all iterations to use the same state.
tip_l
use rep to generate unrolled loops where each iteration will have its own state

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)
note_s
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
tip_s
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):
ModeDescription
0 pointignore fractional (mod) bits, simply recombine waveforms (fastest)
1 phasefractional (mod) bits determine source region start offset shift ("phase" mod 1/8 sin)
2 lerpfractional (mod) bits are used to lerp sine source region to cosine wave
3 shiftfractional (mod) bits are used to shift source region (dc offset + reflect/fold)
4 angfractional (mod) bits determine source waveform start offset shift ("phase" +- 2PI)
5 ampfractional (mod) bits are used to amplify source region
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.
note_s
See Eureka sampler "FX" tab for a list of available plugins.
note_s
Plugin setup can be exported to single- or multi-line Cycle script (clipboard) via Eureka sampler "FX" tab context menu
caution_s
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
note_s
sample len is determined by last loop length of the specified zone
note_s
sample len should be a power of two (read offset is ANDed with (sampleLen-1))

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
tip_s
in order to create chords, synthesize the highest note to a zone, then in a following zone read and mix the previously rendered zone at different speeds / rates
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
tip_s
When the input zones (one-shot samples) are placed in the same samplebank as the sequenced loop, ticking the Recalc All checkbox (next to the F11: Synth button at the bottom of the screen) will cause all procedural zones to be recalculated when any synth patch changes. This makes it possible to tweak the input sounds and immediately hear the edits in the context of the loop.

Valid HTML 4.01 Transitional

Document created on 31-Mar-2023 11:15:32