Welcome
... to the afseq scripting guide!
Introduction
afseq, also known as nerdo-rhythm, is an experimental imperative-style music sequence generator engine.
It allows you to programmatically create music sequences either in plain Rust as library (static, compiled) or in Lua as a scripting engine (dynamic, interpreted). So it's also suitable for live coding music.
In addition to its imperative event generator approach, it also supports the creation of musical events using tidalcycle's mini-notation.
Installation
afseq is a Rust library that deals with raw musical event generation only. It does not generate any audio. You must use an application with built-in support for afseq to use it.
You can also use play-script.rs
from the examples in the git repository to test out afseq scripts using a basic sample player that plays a sample from the example assets folder using the script which has the same name as the audio file.
Scripting
afseq uses Lua as a scripting language to dynamically generate content.
If you're not familiar with Lua, don't worry. Lua is very easy to pick up if you have used another imperative programming language before, and fortunately there are great tutorials out there, such as this one.
Creating Rhythms
Ready to program some music? Then let's dive into the next chapter which will give you an overview of the overall architecture of a rhythm, the main building block in afseq.
Note: This guide covers the afseq Lua scripting API. For instructions on creating rhythms in plain Rust, see the afseq crate docs.
Rhythm
afseq consumes Lua script files that define rhythms, the main building block in afseq.
A rhythm programatically generates musical events.
The Lua API uses configuration tables to define the rhythm and their sub-components, so the main building blocks of a script are defined via Lua tables and functions as specified in the API documentation.
Components
- TimeBase defines the time unit of a rhythm.
- Pattern -> Gate -> Emitter do perform the basic event generation in 3 stages.
- Parameters change behaviour of components during runtime.
*Inputs*
Optional user controlled parameters.
↓ ↓ ↓
┌────────────────────────────────────────────────────────┐
│ *Time base* │
│ Basic unit of time and the increment of a pulse. │
│ ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┐ │
│ │ *Pattern* │ e.g. { 0, 1, 0, 1, 1 } │
│ └┄┄┄┄┄┄┄┄┄┄┄┄┄┘ │
│ Defines the basic rhythmic pattern as a pulse train. │
│ ↓ │
│ ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┐ │
│ │ *Gate* │ e.g. { return pulse > 0.5 } │
│ └┄┄┄┄┄┄┄┄┄┄┄┄┄┘ │
│ Passes or suppresses pattern pulses. │
│ ↓ │
│ ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┐ │
│ │ *Emitter* │ e.g. sequence{ "c4", "d#4", "g#4" } │
│ └┄┄┄┄┄┄┄┄┄┄┄┄┄┘ │
│ Generates events for each incoming filtered pulse. │
└────────────────────────────────────────────────────────┘
↓ ↓ ↓
[o] [x] [o]
*Event Stream*
By separating the rhythmical pattern from the tonal part of a musical sequence, each part of a sequence can be freely modified, composed and (re)combined.
All content in rhythms can be either static, a Lua table of events, or dynamic, a Lua function that generates events on the fly.
Dynamic functions or generators can also be controlled, automated via input parameters to change their behaviour at runtime in response to user input (e.g. MIDI controllers, DAW parameter automation). This also allows creating more flexible rhythm templates.
Examples
A simple rhythm with a static pattern and emitter, using the default gate implementation.
-- sequence of 1/4th c4 and two 1/8 c5 notes.
return rhythm {
unit = "1/4",
pattern = { 1, { 1, 1 } },
emit = { "c4", "c5", "c5" }
}
A rhythm with default pattern and gate, emitting a Tidal Cycle.
-- emit a tidal cycle every bar
return rhythm {
unit = "1/1",
emit = cycle("a4 e4@2 <c4 c5>")
}
A rhythm, using a Lua function as dynamic pattern generator.
-- maybe trigger a c4 on every 2nd 1/4.
return rhythm {
unit = "1/4",
pattern = function(context)
if context.pulse_step % 2 == 1 then
return math.random() > 0.5 and 1 or 0
else
return 1
end
end,
emit = "c4"
}
A rhythm with a static pattern and emitter and a dynamic seeded probablility gate.
-- change for other variations or set to nil to get *really* random behavior
local seed = 12345678
return rhythm {
unit = "1/4",
pattern = { 1, { 1, 0.1, 0.2 }, 1, { 1, 0, 0, 0.3 } },
gate = function(init_context)
local rand = math.randomstate(seed)
return function(context)
-- use pulse values as trigger probabilities
return context.pulse_value >= rand()
end
end,
emit = { "c4'-7 v0.6", "d#4'M v0.4", "g4'- v0.3" },
}
See Examples in this guide for more advanced and guided examples.
Timebase
A rhythm's timebase
represents the unit of time for the rhythm, either in musical beats or wall-clock time (seconds, ms). It defines the unit and duration of a single step in patterns. The time base is static and thus can't be changed during runtime.
The default time unit of rhythm is a beat.
The BPM and signature (beats per bar) settings are configured by the application which is running the rhythm.
Supported Time Units
Beat-Time
"bars"
using the host's beats per bar setting"beats"
alias for 1/4"1/1"
4 * 1/4"1/2"
2 * 1/4"1/4"
a beat"1/8"
0.5 * 1/4"1/16"
0.25 * 1/4"1/32"
0.125 * 1/4"1/64"
0.0625 * 1/4
Wallclock-Time
"ms"
millisecond"seconds"
whole seconds
Resolution
The resolution
parameter acts as an additional multiplier to the time unit and can be any positive real number. You can use it to scale the unit or to create odd time signatures.
Examples
A slightly off beat time unit.
return rhythm {
unit = "beats",
resolution = 1.01,
emit = "c4"
}
Sixteenth tripplets
return rhythm {
unit = "1/16",
resolution = 4/3,
emit = "c4"
}
2 Seconds
return rhythm {
unit = "seconds",
resolution = 2,
emit = "c4"
}
Pattern
A rhythm's pattern
is a sequence of pulse values that defines the temporal occurence of events. It feeds the emitter with pulse values that are optionally filtered out by a gate. It is created from a list of pulses with possible subdivisions, an optional number of repeats and an optional time offset.
A pattern can be generated by algorithms such as Euclidean rhythms, or it can be expressed as a static Lua table, or it can be generated by a dynamic generator - a Lua function.
The default pattern is an endless repeated pulse train of 1's.
Offset
By default, the pattern starts running immediately when the rhythm is triggered. Using the offset
property you can delay the start by the amount specified in the rhythm's unit.
» offset = 4
delay start by 4 * rhythm's unit.
Repeat
By default, a pattern repeats endlessly. To create one-shot patterns, or patterns that repeat only a few times, you can use the repeats
property.
» repeats = false
one-shot
» repeats = 1
play pattern twice, then stop
Static Patterns
The simplest form of a pattern is a Lua table (array) of numbers or boolean values:
» pattern = {0, 1, 1}
skip, trigger, trigger - repeat
» pattern = {1, 0, 1, 1}
1/2th followed by two 1/4th triggers in a 1/4th unit rhythm
» pattern = {0.8, 0, 1, 0.2}
pulse values are passed as numbers to gates and emitters in function contexts
tip
The default gate implementation skips all 0 pulses and passes all other pulse values to the emitter. When using a custom gate function, you can e.g. use the pulse value as a probability or you can use the pulse value as a volume value in a custom emitter function to create accents.
Sub-Divisions
Each number value in the specified Lua table represents a single pulse in the rhythm's specified time unit. By using tables instead of pulse numbers as values, you can cram sub-patterns into a pattern to create more complex rhythms.
» pattern = {{1, 1, 1}, {1, 1}}
triplet followed by two quarter notes with unit 1/2
» pattern = {{1, 0}, {0, 1}}
basic bossanova rhythm with unit 1/4
Dynamic Patterns
When using a Lua function instead of a table as a pattern generator, you can dynamically generate pulse values.
» pattern = function(context) return math.random() end
randomly emit pulse values between 0 and 1
The expected return value of a dynamic pattern function is a pulse value (true
, false
, 0
, 1
) or nil
.
Use functions in order to create dynamic patterns that can interact with user interaction, or to create probability based pattern rules. To connect the functions to its runtime use the passed context
argument.
See generators for more info about using functions as generators.
Pattern Library
afseq comes with a built-in pattern library, which contains a bunch of helper functions and generators to ease creating patterns.
» pattern = pattern.from{0, 1} * 3 + {1, 0}
combine sub patterns
» pattern = pattern.new(12, function(k, v) return k % 3 == 1 end),
functionally create patterns
» pattern = pattern.euclidean{3, 8, -1}
create euclidean patterns
See Pattern API Lua reference for more info and examples.
Examples
Static pattern.
return rhythm {
pattern = { 1, 0, 0, 1 },
emit = "c4"
}
Cram pulses into a single pulse slot via subdivisions in static patterns.
return rhythm {
pattern = { 1, { 1, 1, 1 } },
emit = "c4"
}
Static pattern created using the "pattern" lib
return rhythm {
pattern = pattern.from{1, 0} * 5 + {1, 1},
emit = "c4"
}
Euclidean pattern created using the "patterns" lib.
return rhythm {
pattern = pattern.euclidean(7, 16, 2),
emit = "c4"
}
Stateless function.
return rhythm {
pattern = function(context)
return math.random(0, 1)
end,
emit = "c4"
}
Stateful generator.
return rhythm {
pattern = function(init_context)
local rand = math.randomstate(12345)
local triggers = table.new{ 0, 6, 10 }
return function(context)
local step = (context.pulse_step - 1) % 16
return rand() > 0.8 and triggers:contains(step)
end
end,
emit = "c4"
}
See generators for more info about stateful generators.
Gate
A rhythm's gate
is an optional filter unit that determines whether or not an event should be passed from the pattern to the emitter. It can be used to dynamically filter out pulse events.
The default gate is a threshold gate, which passes all pulse values > 0.
Examples
Seeded probability gate, using the pattern pulse values as probability.
return rhythm {
pattern = { 0, { 0.5, 1 }, 1, { 1, 0.8 } },
gate = function(init_context)
local rand = math.randomstate(12366)
return function(context)
return context.pulse_value > rand()
end
end,
emit = { "c4" }
}
A gate which filters out pulse values with on a configurable threshold.
return rhythm {
inputs = {
parameter.number("threshold", 0.5, {0, 1})
},
pattern = {
0.2, { 0.5, 1 }, 0.9, { 1, 0.8 }
},
gate = function(context)
return context.pulse_value >= context.inputs.threshold
end,
emit = { "c4" }
}
See generators for more info about stateful generators and parameters about rhythm input parameters.
Emitter
A rhythm's emitter
generates events for incoming pulse values. Just like the pattern, it can be made up of a static list of events or it can be a dynamic generator - a Lua function.
In addition to dynamic Lua functions, you can also use a tidal cycle as an emitter.
The default emitter generates a single middle C note value for each incoming pulse.
Event Types
Currently afseq only supports monophonic or polyphonic note events as emitter output. This is likely to change in the future to allow other musically interesting events to be emitted.
Note values can be expressed as:
- Raw integer values like
48
, which are interpreted as MIDI note numbers. - Raw note strings such as
"c4"
(single notes) or"d#4'maj"
(chords). - Lua note tables like
{ key = 48, volume = 0.1 }
. - Lua API note objects such as
note(48):volume(0.1)
ornote("c4", "g4")
ornote("c4'min"):transpose({-12, 0, 0})
. - Lua
nil
values, empty tables{}
, empty strings""
, or"-"
strings are interpreted as rests. - The string
"off"
or"~"
is interpreted as note off.
See notes & scales for more information about the different ways to create and manipulate notes and chords.
Static Emitters
The simplest form of a emitter is a Lua table (array) of note or nil values (a rest).
Static emitter arrays define note event sequences. Each incoming, possibly filtered, gated pulse from the pattern picks the next event in the sequence, then moves on in the sequence. Sequences are repeated as long as the pattern is running.
» emit = { "c4", "d#4", "g4" }
arpeggio - sequence
» emit = { { "c4", "d#4", "g4" } }
single chord - single event
To ease distinguishing polyponic contents, use sequence
and note
:
» emit = sequence("c4", "d#4", "g4")
arpeggio - sequence
» emit = note("c4", "d#4", "g4")
single chord - single event
Dynamic Emitters
Dynamic emitter functions return single note events. Each incoming, possibly filtered, gated impulse from the pattern will trigger the emit function to create the next event as long as the pattern is running.
» emit = function(context) return math.random() > 0.5 and "c4" or "c5" end
randomly emit c4 or c5 notes
» emit = function(context) return context.pulse_count % 2 == 1 and "c4" or "c5" end
alternate c4 and c5 notes
The expected return value of a dynamic emitter function is a single monophonic or polyphonic note value, or a value that can be converted into a note.
See API docs for context for more info about the context passed to dynamic functions.
Cycle Emitters
Cycle emitters emit a whole cycle for a single pulse. So any incoming, possibly filtered, gated pulse from the pattern will trigger a full cycle as long as the pattern is running.
» emit = cycle("[c4, d#4, g4]")
a single chord
You probably won't use custom patterns or gate functions with cycles, but it's possible to sequence entire cycles with them, or use cycles as single note generators too:
» emit = cycle("[c4 d#4 g4]")
arpeggio
» emit = cycle("[c4 <d#4 d4> g4|g5]")
arpeggio with variations
See cycles for more info about Tidal Cycles support in afseq.
Examples
Sequence of c4, g4 notes.
return rhythm {
emit = { "c4", "g4" }
}
Chord of c4, d#4, g4 notes.
return rhythm {
emit = sequence(
{ "c4", "d#4", "g4" }, -- or "c4'min"
{ "---", "off", "off" }
)
}
Sequence of c4, g4 with volume 0.5.
return rhythm {
emit = sequence{"c4", "g4"}:volume(0.5)
}
Stateless function.
return rhythm {
emit = function(context)
return 48 + math.random(1, 4) * 5
end
}
Stateful generator.
return rhythm {
emit = function(init_context)
local count, step, notes = 1, 2, scale("c5", "minor").notes
return function(context)
local key = notes[count]
count = (count + step - 1) % #notes + 1
return { key = key, volume = 0.5 }
end
end
}
Note pattern using the "pattern" lib.
local tritone = scale("c5", "tritone")
return rhythm {
emit = pattern.from(tritone:chord(1, 4)):euclidean(6) +
pattern.from(tritone:chord(5, 4)):euclidean(6)
}
Tidal cycle.
return rhythm {
emit = cycle("<[a3 c4 e4 a4]*3 [d4 g3 g4 c4]>")
}
See generators for more info about stateful generators.
Notes, Chords & Scales
Note values, such as those specified in the emitter, can be expressed and modified in various ways. Sometimes it's easier to generate notes programmatically using note numbers. Other times you may want to write down a chord in a more expressible form.
Notes
Note Numbers
Raw integer values like 48
, are interpreted as MIDI note numbers in the note
function and emitter. Valid MIDI notes are 0-127
.
» emit = 48
emit a single c4 note
Note Tables
Instead of using a string, you can also specify notes via a Lua table with the following properties.
"key"
- REQUIRED - MIDI Note number such as48
or a string, such as"c4"
"instrument"
OPTIONAL - Instrument/Sample/Patch number >= 0"volume"
- OPTIONAL - Volume number in range [0.0 - 1.0]"panning"
- OPTIONAL - Panning factor in range [-1.0 - 1.0] where 0 is center"delay"
- OPTIONAL - Delay factor in range [0.0 - 1.0]
» emit = { key = 48, volume = 0.1 }
a c4 with volume 0.1
Note Strings
Note strings such as "c4"
are interpreted as {KEY}{MOD}{OCT}
where MOD and OCT are optional.
Valid keys are c,d,e,f,g,a,b
. Valid modifiers are #
and b
. Valid octaves are values 0-10
» emit = { "c4" }
emit a single c4 note
Other note properties can be specified in the string notation as well.
'#'
instrument'v'
volume'p'
panning'd'
delay
» emit = { "f#4 #1 v0.2" }
emit a f sharp with instrument 1 and volume 0.2
Note Chord Strings
To create a chords from a note string, append a '
character to the key and specify a chord mode.
» emit = "d#4'maj"
d#4 major chord
See chord Lua API for a list of all supported modes.
Just like regular notes, additional note properties can be added to the chord string as well.
» emit = "c4'69 #1 v0.5"
patch 1, volume 0.5
Note Objects
Note numbers, strings and tables, as described above can be fed into a note object in the LuaAPI, which allows further transformation of the note.
This is especially handy for chords, but also can be more verbose than using note string attributes.
» emit = note(48):volume(0.1)
c4 note with volume of 0.1
» emit = note({key = "c4"}):volume(0.2)
c4 note with volume of 0.2
» emit = note("c4'min"):transpose({-12, 0, 0})
1st chord inversion
See note Lua API for details.
Note Chord Objects
Notes objects can also be created using the chords
function.
» emit = chord(48, "major")
c4 major notes
This also allows the use of custom interval tables.
» emit = chord(48, {0,4,7})):volume(0.2)
custom c4 major chord with volume 0.2
See chord Lua API for details.
NB: The sequence Lua API has a similar interface to modify notes within a sequence.
Note Offs and Rests
To create rest values use Lua nil
values, empty tables {}
, empty strings ""
, or "-"
strings.
To create off notes use the string "off"
or "~"
.
Scales
To make working with chords and chord progressions, and programming music in general, easier, afseq also has a simple scale API to create chords and notes from scales.
Scale objects
Scale objects can be created from a note key and mode name, or custom intervals.
» scale("c4", "minor").notes
"c4", "d", "d#4", "f4", "g4", "g#4" "a#4"
» scale("c4", {0,2,3,5,7,8,10}).notes
same as above
Common Scales
See scale Lua API for a list of all supported modes.
Custom Scales
Custom scales can be created by using an interval table with numbers from 0-11
in ascending order.
» scale("c4", {0,3,5,7}).notes
"c4", "d#4", "f4", "g4", "a4"
Scale Chords
The scale's chord
function allows to generate chords from the scale's intervals.
local cmin = scale("c4", "minor")
return rhythm {
emit = sequence(
note(cmin:chord("i", 4)), --> note{48, 51, 55, 58}
note(cmin:chord(5)):transpose({12, 0, 0}), --> Gm 1st inversion
)
}
See scale Lua API for more information about scale objects.
Cycles
In addition to static arrays of notes or dynamic generator functions, emitters in afseq can also emit cycles using the tidal cycles mini-notation.
Tidal Cycles allows you to make patterns with code using a custom functional approach. It includes a language for describing flexible (e.g. polyphonic, polyrhythmic, generative) sequences of sounds, notes, parameters, and all kind of information.
Usage
To create cycles in afseq, use the cycle
function in the emitter and pass it a mini-notation as string.
> emit = cycle("c4 d#4 <a5 g5> _")
note
Please see Tidal Cycles Mini-Notation Reference for a complete overview of the cycle notation.
Limitations
There's no exact specification for how tidal cycles work, and it's constantly evolving, but at the moment we support the mini notation as it works in Tidal, with the following limitations and changes:
-
Stacks and random choices are valid without brackets (
a | b
is parsed as[a | b]
) -
Operators currently only accept numbers on the right side (
a3*2
is valid,a3*<1 2>
is not) -
:
- Sets the instrument or remappable target instead of selecting samples
Timing
The base time of a pattern in tidal is specified as cycles per second. In afseq, the time of a cycle instead is given in cycles per pattern pulse units.
-- emits an entire cycle every bar
return rhythm {
unit = "bars",
emit = cycle("c d e f")
}
Sequencing
An emitter in afseq gets triggered for each incoming non-gated pattern pulse. This is true for cycles are well and allows you to sequence entire cycles too.
-- emit an entire cycle's every bar, then pause for a bar, then repeat
return rhythm {
unit = "bars",
pattern = {1, 0},
emit = cycle("c d e f")
}
You can also use the mini notation to emit single notes only, making use of tidal's note alternating and randomization features only:
-- emit a single note from a cycle in an euclidean pattern
return rhythm {
unit = "beats",
pattern = pattern.euclidean(5, 8),
emit = cycle("<c d e f g|b>")
}
Seeding
afseq's general random number generator is also used in cycles. So when you seed the global number generator, you can also seed the cycle's random operations with math.randomseed(12345)
.
Mapping
Notes and chords in cycles are expressed as note strings in afseq. But you can also dynamically evaluate and map cycle identifiers using the cycle map
function.
This allows you, for example, to inject input parameters into cycles or to use custom identifiers.
Using custom identifiers with a static map (a Lua table):
return rhythm {
unit = "bars",
emit = cycle("[bd*4], [_ sn]*2"):map({
["bd"] = note("c4 #0"),
["sn"] = note("g4 #1")
})
}
Using custom identifiers with a dynamic map function (a Lua function):
return rhythm {
unit = "bars",
emit = cycle("[bd*4], [_ sn]*2"):map(function(context, value)
if value == "bd" then
return note("c4 #0")
elseif value == "sn" then
return note("g4 #1")
end
end)
}
Examples
A simple polyrhythm
return rhythm {
unit = "1/1",
emit = cycle("[C3 D#4 F3 G#4], [[D#3?0.2 G4 F4]/64]*63")
}
Mapped multi channel beats
return rhythm {
unit = "1/1",
emit = cycle([[
[<h1 h2 h2>*12],
[kd ~]*2 ~ [~ kd] ~,
[~ s1]*2,
[~ s2]*8
]]):map({
kd = "c4 #0", -- Kick
s1 = "c4 #1", -- Snare
s2 = "c4 #1 v0.1", -- Ghost snare
h1 = "c4 #2", -- Hat
h2 = "c4 #2 v0.2", -- Hat
})
}
Dynamically mapped roman chord numbers with user defined scale
return rhythm {
unit = "1/1",
resolution = 4,
inputs = {
parameter.enum("mode", "major", {"major", "minor"})
},
emit = cycle("I V III VI"):map(
function(init_context, value)
local s = scale("c4", init_context.inputs.mode)
return function(context, value)
return value ~= "_" and s:chord(value) or value
end
end
)
}
Input Parameters
Rhythm inputs
allow user controlled parameter values to be injected into a rhythm. This allows you to write more flexible rhythms that can be used as templates or to automate functions within the rhythm.
Input parameters can be accessed in dynamic pattern, gate, emitter or cycle function contexts
.
Parameter Types
Currenty available parameter types are:
- boolean - on/off switches -
parameter.boolean
- integer - integer value ranges -
parameter.integer
- number - real number value ranges -
parameter.number
- string - enumeration value sets -
parameter.enum
Parameter access
When defining a parameter, each parameter has a unique string id set. This id can then be used to access the actual paramter value in the function contexts.
Definition:
» inputs = { parameter.boolean("enabled", true) }
Usage:
» emit = function(context) return context.inputs.enabled and "c5" or nil }
Usage, if you've got spaces in your ids (not recommended):
» emit = function(context) return context.inputs["enabled"] and "c5" or nil }
Examples
Euclidean pattern generator with user configurable steps, pulses, offset value.
return rhythm {
inputs = {
parameter.integer('steps', 12, {1, 64}, "Steps",
"Number of on steps in the pattern"),
parameter.integer('pulses', 16, {1, 64}, "Pulses",
"Total number of on & off pulses"),
parameter.integer('offset', 0, {-16, 16}, "Offset",
"Rotates on pattern left (values > 0) or right (values < 0)"),
},
unit = "1/1",
pattern = function(context)
return pattern.euclidean(
math.min(context.inputs.steps, context.inputs.pulses),
context.inputs.pulses,
context.inputs.offset)
end,
emit = "c4"
}
Random bass line generator with user defined custom scales and variations (seeds).
local scales = {"Chromatic", "Minor", "Major"}
return rhythm {
inputs = {
parameter.enum('scale', scales[1], scales, "Scale"),
parameter.integer('notes', 7, {1, 12}, "#Notes"),
parameter.integer('variation', 0, {0, 0xff}, "Variation"),
},
unit = "1/1",
pattern = function(context)
local rand = math.randomstate(2345 + context.inputs.variation)
return pattern.euclidean(rand(3, 16), 16, 0)
end,
emit = function(context)
local notes = scale("c4", context.inputs.scale).notes
local rand = math.randomstate(127364 + context.inputs.variation)
local notes = pattern.new(context.inputs.notes):map(function(_)
return notes[rand(#notes)]
end)
return notes[math.imod(context.step, #notes)]
end
}
Drum pattern cycle with configurable note values for each drumkit instrument.
return rhythm {
unit = "1/1",
inputs = {
parameter.integer("bd_note", 48, {0, 127}),
parameter.integer("sn_note", 70, {0, 127}),
parameter.integer("hh_note", 98, {0, 127})
},
emit = cycle([[
[<hh1 hh2 hh2>*12],
[bd1 ~]*2 ~ [~ bd2] ~,
[~ sn1]*2,
[~ sn2]*8
]]):map(function(context, value)
for _, id in pairs{"bd", "sn", "hh"} do
local number = value:match(id.."(%d+)")
if number then
return note(context.inputs[id.."_note"]):volume(
number == "2" and 0.2 or 1.0)
end
end
end)
}
Advanced Topics
This chapter contains more advanced topics about rhythms.
Please read the basic concepts from the guide first to get you started...
Generators
Patterns, Gates and Emitters can use Lua functions to dynamically generate or evaluate content.
Anonymous Lua functions, as used in rhythms, are actually closures. They keep a record of their environment, so all (up)values which are declared outside of the anonymous function are accessible from within the function itself.
We can use this in afseq scripts to keep track of a rhythm's global or local state.
Runtime
To better understand how local and global states are relevant here, we first need to understand how rhythms are evaluated.
Let's say we're in a DAW that supports afseq. This DAW triggers your rhythm script when a single note is triggered. If we now want to allow polyphonic playback of scripts, only one script instance is actually created per instrument or track. So a single script will be triggered multiple times with multiple notes.
This means that all notes triggered by the DAW will share the same global state within a rhythm script. But this also means that in order to create local states for each individual note trigger, you'll need to keep track of a local state somehow.
Functions
In the following example, an emitter function keeps track of its state by referencing a globally defined counter
variable.
local counter = 0
return rhythm {
emit = function(context)
local midi_note = counter
counter = (counter + 1) % 128
return note(midi_note)
end,
}
When playing a single instance of this rhythm, you'll get an event stream of increasing note values. As expected. But when triggering this script multiple times polyphonically, each triggerd script instance increases the counter on its own, so you'll get multiple streams with note values increased by multiple note steps.
Contexts
The easiest way to deal with this, is using the function's passed context. Apart from global playback information such as the BPM or sample rate, the context also keeps track of the rhythm's internal playback state.
A context
passed to pattern functions only contains the global playback status. A context
passed to gate and emitter functions contains the global playback status and status of the pattern.
See pattern context API, gate context API, emitter context API for details.
Contexts also may contain user controlled input variables. See input parameters for more info about this.
By making use of the context we can now rewrite the example above to:
return rhythm {
emit = function(context)
-- NB: pulse_step is an 1 based index, midi notes start with 0
local midi_note = (context.pulse_step - 1) % 128
return note(midi_note)
end
}
Because the context is unique for each newly triggered rhythm instance, we now get multiple continously increasing note event streams again.
Generators
Generators in afseq are pattern, gate or emit functions, that do return another function. This is similar to how iterators work in Lua. By returning a function from a function you can create a new local state that is valid for the returned function only.
Let's use our counter example again with such a generator:
return rhythm {
emit = function(init_context)
local counter = 0 -- local state!
return function(context)
local midi_note = counter
counter = (counter + 1) % 128
return note(midi_note)
end
end,
}
Here the outer function is called once when the rhythm is started - just to create the local state and to return the actual emit function. The returned function is then called repeatedly while the rhythm instance is running, operating on the local state it was initialised with.
When to use what?
-
If you have a function that does not depend on an (external) state, simply use a global or anonymous function.
-
If you have a function which only depends on the rhythm playback context, use a global or anonymous function too and only make use of the passed context.
-
If you need to keep track of local states separately for each new rhythm run, use a generator.
-
If you need a mix of local and global state, use a generator which also reaches out to global and local variables.
See also advanced topic about randomization, which makes use the the generator concept to keep track of local random states.
Randomization
Controlled randomness can be a lot of fun when creating music algorithmically, so afseq supports a number of randomisation techniques to deal with pseudo randomness.
Random Number Generation
You can use the standard Lua math.random()
to create pseudo-random numbers in afseq, and can use math.randomseed()
to seed them.
Note that the standard Lua random implementation is overridden by afseq, to use a Xoshiro256PlusPlus random number generator. This ensures that seeded random operations behave the same on all platforms and architectures.
Here's a simple example which creates a random melody line based on a scale.
-- create a scale to pick notes from
local cmin = scale("c", "minor")
-- pick 10 random notes from the scale
local random_notes = pattern.new(10, function()
return cmin.notes[math.random(#cmin.notes)]
end)
return rhythm {
emit = random_notes
}
Random Number Seeding
You can use math.randomseed()
to seed the global random number generator.
-- create a scale to pick notes from
local cmin = scale("c", "minor")
-- pick the same random 10 notes from the scale every time
math.randomseed(1234)
local random_notes = pattern.new(10, function()
return cmin.notes[math.random(#cmin.notes)]
end)
return rhythm {
emit = random_notes
}
Local Random Number Generators
When seeding the RNG, each time a rhythm is (re)started, an existing rhythm instance will continue to run. The global state of a rhythm script is not recreated each time the rhythm is played again.
See generators for details of how afseq handles global and local states in general.
To create multiple separate local random states, use the non standard math.randomstate(seed)
function to create local, possibly seeded random number generators.
local cmin = scale("c", "minor")
return rhythm {
emit = function(init_context)
local rand = math.randomstate(1234) -- a local random number generator
return function(context)
return note(cmin.notes[rand(#cmin.notes)])
end
end
}
In the example above, each newly triggered rhythm instance will result in the same sequence of random notes, and multiple running instances will not interfere with each other.
Examples
TODO
here will be a few guided example scripts...
Lua API Reference
The following chapters contain a complete listing of the afseq Lua scripting API. The content has been auto-generated from the LuaLS Type Definitions, so you can read and use the definition files directly too.
You can also use the LuaLS type definitions directly for autocompletion and to view the API documentation in e.g. vscode and other editors that support the LuaLS language server.
First install the sumneko.lua vscode extension.
Then download a copy of the afseq type definitions folder (./types/nerdo
) and configure your workspace to use the files in your project. To do this, add the following to your project's /.vscode/settings.json
file
{
"Lua.workspace.library": ["PATH/TO/NERDO_DEFINITION_FOLDER"]
}
chord
Global
Functions
chord(key : NoteValue
, mode : ChordName
)
->
Note
Create a new chord from the given key notes and a chord name or an array of custom intervals.
NB: Chords also can also be defined via strings in function
note
and via the a scale'schord
function. See examples below.Chord names also can be shortened by using the following synonyms:
- "min" | "m" | "-" -> "minor"
- "maj" | "M" | "^" -> "major"
- "minMaj" | "mM" | "-^" -> "minMajor"
- "+" | "aug" -> "augmented"
- "o" | "dim" -> "diminished"
- "5 -> "five"
- "6 -> "six"
- "69" -> "sixNine"
- "9 -> "nine"
- "11" -> "eleven"
examples:
chord("c4", "minor") --> {"c4", "d#4", "f4"} chord({key = 48, volume = 0.5}, "minor") --> {"c4 v0.5", "d#4 v0.5", "f4 v0.5"} --same as: chord("c4", {0, 4, 7}) chord("c4 v0.5", {0, 4, 7}) --or: note("c4'major") note("c4'major v0.5") --or: note(scale("c4", "major"):chord("i", 3)) note(scale("c4", "major"):chord("i", 3)):volume(0.5)
-- Available chords. mode: | "major" | "major7" | "major9" | "major11" | "major13" | "minor" | "minor#5" | "minor6" | "minor69" | "minor7b5" | "minor7" | "minor7#5" | "minor7b9" | "minor7#9" | "minor9" | "minor11" | "minor13" | "minorMajor7" | "add9" | "add11" | "add13" | "dom7" | "dom9" | "dom11" | "dom13" | "7b5" | "7#5" | "7b9" | "five" | "six" | "sixNine" | "nine" | "eleven" | "thirteen" | "augmented" | "diminished" | "diminished7" | "sus2" | "sus4" | "7sus2" | "7sus4" | "9sus2" | "9sus4"
chord_names()
->
string
[]
Return supported chord names.
Aliases
ChordName
string
| "7#5"
| "7b5"
| "7b9"
| "7sus2"
| "7sus4"
| "9sus2"
| "9sus4"
| "add11"
| "add13"
| "add9"
| "augmented"
| "diminished"
| "diminished7"
| "dom11"
| "dom13"
| "dom7"
| "dom9"
| "eleven"
| "five"
| "major"
| "major11"
| "major13"
| "major7"
| "major9"
| "minor"
| "minor#5"
| "minor11"
| "minor13"
| "minor6"
| "minor69"
| "minor7"
| "minor7#5"
| "minor7#9"
| "minor7b5"
| "minor7b9"
| "minor9"
| "minorMajor7"
| "nine"
| "six"
| "sixNine"
| "sus2"
| "sus4"
| "thirteen"
-- Available chords. ChordName: | "major" | "major7" | "major9" | "major11" | "major13" | "minor" | "minor#5" | "minor6" | "minor69" | "minor7b5" | "minor7" | "minor7#5" | "minor7b9" | "minor7#9" | "minor9" | "minor11" | "minor13" | "minorMajor7" | "add9" | "add11" | "add13" | "dom7" | "dom9" | "dom11" | "dom13" | "7b5" | "7#5" | "7b9" | "five" | "six" | "sixNine" | "nine" | "eleven" | "thirteen" | "augmented" | "diminished" | "diminished7" | "sus2" | "sus4" | "7sus2" | "7sus4" | "9sus2" | "9sus4"
NoteValue
string
| number
| NoteTable
| nil
cycle
Global
Functions
cycle(input : string
)
->
Cycle
Create a note sequence from a Tidal Cycles mini-notation string.
cycle
accepts a mini-notation as used by Tidal Cycles, with the following differences:
- Stacks and random choices are valid without brackets (
a | b
is parsed as[a | b]
)- Operators currently only accept numbers on the right side (
a3*2
is valid,a3*<1 2>
is not):
- Sets the instrument or remappable target instead of selecting samples Tidal Cycles Referenceexamples:
--A chord sequence cycle("[c4, e4, g4] [e4, g4, b4] [g4, b4, d5] [b4, d5, f#5]")
--Arpeggio pattern with variations cycle("<c4 e4 g4> <e4 g4> <g4 b4 d5> <b4 f5>")
--Euclidean Rhythms cycle("c4(3,8) e4(5,8) g4(7,8)")
--Map custom identifiers to notes cycle("bd(3,8)"):map({ bd = "c4 #1" })
Aliases
CycleMapFunction
(context : CycleMapContext
, value : string
) ->
CycleMapNoteValue
CycleMapGenerator
(context : CycleMapContext
, value : string
) ->
CycleMapFunction
CycleMapNoteValue
Note
| NoteValue
| NoteValue
[]
NoteValue
string
| number
| NoteTable
| nil
PlaybackState
"running"
| "seeking"
-- - *seeking*: The emitter is auto-seeked to a target time. All results are discarded. Avoid -- unnecessary computations while seeking, and only maintain your generator's internal state. -- - *running*: The emitter is played back regularly. Results are audible. PlaybackState: | "seeking" | "running"
Cycle
Functions
map(self, map : CycleMapFunction
| CycleMapGenerator
| { })
->
Cycle
Map names in in the cycle to custom note events.
By default, strings in cycles are interpreted as notes, and integer values as MIDI note values. Custom identifiers such as "bd" are undefined and will result into a rest, when they are not mapped explicitly.
examples:
--Using a static map table cycle("bd [bd, sn]"):map({ bd = "c4", sn = "e4 #1 v0.2" })
--Using a static map table with targets cycle("bd:1 <bd:5, bd:7>"):map({ -- instrument #1,5,7 are set additionally, as specified bd = { key = "c4", volume = 0.5 }, })
--Using a dynamic map function cycle("4 5 4 <5 [4|6]>"):map(function(context, value) -- emit a random note with 'value' as octave return math.random(0, 11) + value * 12 end)
--Using a dynamic map function generator cycle("4 5 4 <4 [5|7]>"):map(function(init_context) local notes = scale("c", "minor").notes return function(context, value) -- emit a 'cmin' note arp with 'value' as octave local note = notes[math.imod(context.step, #notes)] local octave = tonumber(value) return { key = note + octave * 12 } end end)
--Using a dynamic map function to map values to chord degrees cycle("1 5 1 [6|7]"):map(function(init_context) local cmin = scale("c", "minor") return function(context, value) return note(cmin:chord(tonumber(value))) end end)
Aliases
CycleMapFunction
(context : CycleMapContext
, value : string
) ->
CycleMapNoteValue
CycleMapGenerator
(context : CycleMapContext
, value : string
) ->
CycleMapFunction
CycleMapNoteValue
Note
| NoteValue
| NoteValue
[]
NoteValue
string
| number
| NoteTable
| nil
PlaybackState
"running"
| "seeking"
-- - *seeking*: The emitter is auto-seeked to a target time. All results are discarded. Avoid -- unnecessary computations while seeking, and only maintain your generator's internal state. -- - *running*: The emitter is played back regularly. Results are audible. PlaybackState: | "seeking" | "running"
CycleMapContext
Context passed to 'cycle:map` functions.
Properties
playback : PlaybackState
Specifies how the cycle currently is running.
channel : integer
channel/voice index within the cycle. each channel in the cycle gets emitted and thus mapped separately, starting with the first channel index 1.
step : integer
Continues step counter for each channel, incrementing with each new mapped value in the cycle. Starts from 1 when the cycle starts running or after it got reset.
step_length : number
step length fraction within the cycle, where 1 is the total duration of a single cycle run.
trigger_note : integer
?
Note value that triggered, started the rhythm, if any.
trigger_volume : number
?
Note volume that triggered, started the rhythm, if any.
trigger_offset : integer
?
Note slice offset value that triggered, started the rhythm, if any.
inputs : table<string
, boolean
| string
| number
>
Current input parameter values, using parameter ids as keys and the actual parameter value as value.
beats_per_min : number
Project's tempo in beats per minutes.
beats_per_bar : integer
Project's beats per bar setting.
samples_per_sec : integer
Project's sample rate in samples per second.
Aliases
PlaybackState
"running"
| "seeking"
-- - *seeking*: The emitter is auto-seeked to a target time. All results are discarded. Avoid -- unnecessary computations while seeking, and only maintain your generator's internal state. -- - *running*: The emitter is played back regularly. Results are audible. PlaybackState: | "seeking" | "running"
input
- InputParameter
- Parameter
- Functions
- boolean(id :
InputParameterId
, default :InputParameterBooleanDefault
, name :InputParameterName``?
, description :InputParameterDescription``?
) - integer(id :
InputParameterId
, default :InputParameterIntegerDefault
, range :InputParameterIntegerRange``?
, name :InputParameterName``?
, description :InputParameterDescription``?
) - number(id :
InputParameterId
, default :InputParameterNumberDefault
, range :InputParameterNumberRange``?
, name :InputParameterName``?
, description :InputParameterDescription``?
) - enum(id :
InputParameterId
, default :InputParameterEnumDefault
, values :string
[], name :InputParameterName``?
, description :InputParameterDescription``?
)
- boolean(id :
- Aliases
- Functions
InputParameter
Opaque input parameter user data. Construct new input parameters via the
parameter.XXX(...)
functions.
Parameter
Contains functions to construct new input parameters. Input parameter values can be accessed via functionn
contexts
in pattern, gate and emitter functions or generators.
Functions
boolean(id : InputParameterId
, default : InputParameterBooleanDefault
, name : InputParameterName
?
, description : InputParameterDescription
?
)
Creates an InputParameter with "boolean" Lua type with the given default value and other optional properties.
integer(id : InputParameterId
, default : InputParameterIntegerDefault
, range : InputParameterIntegerRange
?
, name : InputParameterName
?
, description : InputParameterDescription
?
)
Creates an InputParameter with "integer" Lua type with the given default value and other optional properties.
number(id : InputParameterId
, default : InputParameterNumberDefault
, range : InputParameterNumberRange
?
, name : InputParameterName
?
, description : InputParameterDescription
?
)
Creates an InputParameter with "number" Lua type with the given default value and other optional properties.
enum(id : InputParameterId
, default : InputParameterEnumDefault
, values : string
[], name : InputParameterName
?
, description : InputParameterDescription
?
)
Creates an InputParameter with a "string" Lua type with the given default value, set of valid values to choose from and other optional properties.
Aliases
InputParameterBooleanDefault
Default boolean value.
InputParameterDescription
Optional long description of the parameter describing what the parameter does.
InputParameterEnumDefault
Default string value. Must be a valid string within the specified value set.
InputParameterId
Unique id of the parameter. The id will be used in the
input
context table as key.
InputParameterIntegerDefault
Default integer value. Must be in the specified value range.
InputParameterIntegerRange
Optional value range. When undefined (0.0 - 1.0)
InputParameterName
Optional name of the parameter as displayed to the user. When undefined, the id is used.
InputParameterNumberDefault
Default number value. Must be in the specified value range.
InputParameterNumberRange
Optional value range. When undefined (0 - 100)
note
Global
Functions
note(...NoteValue
)
->
Note
Create a new monophonic or polyphonic note (a chord) from a number value, a note string, chord string or array of note values.
In note strings the following prefixes are used to specify optional note attributes:
-'#' -> instrument (integer > 0) -'v' -> volume (float in range [0-1]) -'p' -> panning (float in range [-1-1]) -'d' -> delay (float in range [0-1])
examples:
note(60) -- middle C note("c4") -- middle C note("c4 #2 v0.5 d0.3") -- middle C with additional properties note({key="c4", volume=0.5}) -- middle C with volume 0.5 note("c4'maj v0.7") -- C4 major chord with volume 0.7 note("c4", "e4 v0.5", "off") -- custom chord with a c4, e4 and 'off' note
Aliases
NoteValue
string
| number
| NoteTable
| nil
Note
Properties
notes : NoteTable
[]
Functions
transpose(self, step : integer
| integer
[])
->
Note
Transpose the notes key with the specified step or steps.
Values outside of the valid key range (0 - 127) will be clamped.
examples:
note("c4"):transpose(12) note("c'maj"):transpose(5) note("c'maj"):transpose({0, 0, -12})
amplify(self, factor : number
| number
[])
->
Note
Multiply the note's volume attribute with the specified factor or factors.
Values outside of the valid volume range (0 - 1) will be clamped.
examples:
note({"c4 0.5", "g4"}):amplify(0.5) note("c'maj 0.5"):amplify({2.0, 1.0, 0.3})
volume(self, volume : number
| number
[])
->
Note
Set the note's volume attribute to the specified value or values.
examples:
note({"c4", "g4"}):volume(0.5) note("c'maj"):volume(0.5) note("c'maj"):volume({0.1, 0.2, 0.3})
instrument(self, instrument : number
| number
[])
->
Note
Set the note's instrument attribute to the specified value or values.
panning(self, panning : number
| number
[])
->
Note
Set the note's panning attribute to the specified value or values.
delay(self, delay : number
| number
[])
->
Note
Set the note's delay attribute to the specified value or values.
NoteTable
Properties
key : string
| number
Note Key
instrument : number
?
Instrument/Sample/Patch >= 0
volume : number
?
Volume in range [0.0 - 1.0]
panning : number
?
Panning factor in range [-1.0 - 1.0] where 0 is center
delay : number
?
Delay factor in range [0.0 - 1.0]
pattern
- Pattern
- Functions
- new(length :
integer``?
, value :PulseValue
| (index :integer
)->
PulseValue``?
) - from(...
PulseValue
|PulseValue
[]) - copy(self :
Pattern
) - distributed(steps :
integer
|table
, length :integer
, offset :integer``?
, empty_value :PulseValue``?
) - euclidean(steps :
integer
|table
, length :integer
, offset :integer``?
, empty_value :PulseValue``?
) - unpack(self :
Pattern
) - subrange(self :
Pattern
, i :integer
, j :integer``?
, empty_value :PulseValue``?
) - take(self :
Pattern
, length :integer
, empty_value :PulseValue``?
) - clear(self :
Pattern
) - init(self :
Pattern
, value :PulseValue
| (index :integer
)->
PulseValue
, length :integer``?
) - map(self :
Pattern
, fun : (index :integer
, value :PulseValue
)->
PulseValue
) - reverse(self :
Pattern
) - rotate(self :
Pattern
, amount :integer
) - push_back(self :
Pattern
, ...PulseValue
[] |PulseValue
) - pop_back(self :
Pattern
) - repeat_n(self :
Pattern
, count :integer
) - spread(self :
Pattern
, amount :number
, empty_value :PulseValue``?
) - tostring(self :
Pattern
)
- new(length :
- Aliases
- Functions
Pattern
Array alike table with helper functions to ease creating rhythmic patterns.
examples:
-- using + and * operators to combine patterns pattern.from{ 0, 1 } * 3 + { 1, 0 }
-- repeating, spreading and subsets pattern.from{ 0, 1, { 1, 1 } }:repeat_n(4):spread(1.25):take(16)
-- euclidean patterns pattern.euclidean(12, 16) pattern.from{ 1, 0.5, 1, 1 }:euclidean(12)
-- generate/init from functions pattern.new(8):init(1) --> 1,1,1,1,1,1,1,1 pattern.new(12):init(function() return math.random(0.5, 1.0) end ) pattern.new(16):init(scale("c", "minor").notes_iter())
-- generate note patterns pattern.from{ "c4", "g4", "a4" } * 7 + { "a4", "g4", "c4" }
-- generate chords from degree values pattern.from{ 1, 5, 6, 4 }:map(function(index, degree) return scale("c", "minor"):chord(degree) end)
Functions
new(length : integer
?
, value : PulseValue
| (index : integer
) ->
PulseValue
?
)
->
Pattern
Create a new empty pattern or pattern with the given length and pulse value.
examples:
pattern.new(4,1) --> {1,1,1,1} pattern.new(4, function() return math.random() end)
from(...PulseValue
| PulseValue
[])
->
Pattern
Create a new pattern from an existing set of values or tables. When passing tables, those will be flattened.
examples:
pattern.from(1,0,1,0) --> {1,0,1,0} pattern.from({1,0},{1,0}) --> {1,0,1,0}
copy(self : Pattern
)
->
Pattern
create a shallow-copy of the given pattern (or self)
examples:
local p = pattern.from(1, 0) local p2 = p:copy() --> {1,0}
distributed(steps : integer
| table
, length : integer
, offset : integer
?
, empty_value : PulseValue
?
)
->
Pattern
Create an new pattern or spread and existing pattern evenly within the given length. Similar, but not exactly like
euclidean
.Shortcut for
pattern.from{1,1,1}:spread(length / #self):rotate(offset)
examples:
pattern.distributed(3, 8) --> {1,0,0,1,0,1,0} pattern.from{1,1}:distributed(4, 1) --> {0,1,0,1}
euclidean(steps : integer
| table
, length : integer
, offset : integer
?
, empty_value : PulseValue
?
)
->
Pattern
Create a new euclidean rhythm pattern with the given pulses or number of new pulses in the given length and optionally rotate the contents. Euclidean Rhythm
examples:
pattern.euclidean(3, 8) --> {1,0,0,1,0,0,1,0} pattern.from{"a", "b", "c"}:euclidean(8, 0, "-") --> {"a","-","-","b","-","-","c","-"}
unpack(self : Pattern
)
->
... : PulseValue
Shortcut for table.unpack(pattern): returns elements from this pattern as var args.
examples:
local p = pattern.from{1,2,3,4} local v1, v2, v3, v4 = p:unpack()
subrange(self : Pattern
, i : integer
, j : integer
?
, empty_value : PulseValue
?
)
->
Pattern
Get sub range from the pattern as new pattern. When the given length is past end of this pattern its filled up with empty values.
examples:
local p = pattern.from{1,2,3,4} p = p:subrange(2,3) --> {2,3} p = p:subrange(1,4,"X") --> {2,3,"X","X"}
take(self : Pattern
, length : integer
, empty_value : PulseValue
?
)
->
Pattern
Get first n items from the pattern as new pattern. When the given length is past end of this pattern its filled up with empty values.
examples:
local p = pattern.from{1,2,3,4} p = p:take(2) --> {1,2} p = p:take(4, "") --> {1,2,"",""}
clear(self : Pattern
)
->
Pattern
Clear a pattern, remove all its contents.
examples:
local p = pattern.from{1,0} p:clear() --> {}
init(self : Pattern
, value : PulseValue
| (index : integer
) ->
PulseValue
, length : integer
?
)
->
Pattern
Fill pattern with the given value or generator function in length.
examples:
local p = pattern.from{0,0} p:init(1) --> {1,1} p:init("X", 3) --> {"X","X", "X"}
map(self : Pattern
, fun : (index : integer
, value : PulseValue
) ->
PulseValue
)
->
Pattern
Apply the given function to every item in the pattern.
examples:
local p = pattern.from{1,3,5} p:map(function(k, v) return scale("c", "minor"):degree(v) end) --> {48, 51, 55}
reverse(self : Pattern
)
->
Pattern
Invert the order of items.
examples:
local p = pattern.from{1,2,3} p:reverse() --> {3,2,1}
rotate(self : Pattern
, amount : integer
)
->
Pattern
Shift contents by the given amount to the left (negative amount) or right.
examples:
local p = pattern.from{1,0,0} p:rotate(1) --> {0,1,0} p:rotate(-2) --> {0,0,1}
push_back(self : Pattern
, ...PulseValue
[] | PulseValue
)
->
Pattern
Push a single or multiple number of items or other pattern contents to the end of the pattern. Note: When passing array alike tables or patterns, they will be unpacked.
examples:
local p = pattern.new() p:push_back(1) --> {1} p:push_back(2,3) --> {1,2,3} p:push_back{4} --> {1,2,3,4} p:push_back({5,{6,7}) --> {1,2,3,4,5,6,7}
pop_back(self : Pattern
)
Remove an entry from the back of the pattern. returns the popped item.
examples:
local p = pattern.from({1,2}) p:pop_back() --> {1} p:pop_back() --> {} p:pop_back() --> {}
repeat_n(self : Pattern
, count : integer
)
->
Pattern
Duplicate the pattern n times.
examples:
local p = pattern.from{1,2,3} patterns:repeat_n(2) --> {1,2,3,1,2,3}
spread(self : Pattern
, amount : number
, empty_value : PulseValue
?
)
->
Pattern
Expand (with amount > 1) or shrink (amount < 1) the length of the pattern by the given factor, spreading allowed content evenly and filling gaps with 0 or the given empty value.
examples:
local p = pattern.from{1,1} p:spread(2) --> {1,0,1,0} p:spread(1/2) --> {1,1}
tostring(self : Pattern
)
->
string
Serialze a pattern for display/debugging purposes.
examples:
pattern.euclidean(3, 8):tostring() --> "{1, 1, 1, 0}"
Aliases
PulseValue
boolean
| string
| number
| table
Valid pulse value in a pattern
rhythm
- Global
- EmitterContext
- Properties
- trigger_note :
integer``?
- trigger_volume :
number``?
- trigger_offset :
integer``?
- inputs : table<
string
,boolean
|string
|number
> - beats_per_min :
number
- beats_per_bar :
integer
- samples_per_sec :
integer
- pulse_step :
integer
- pulse_time_step :
number
- pulse_time :
number
- pulse_value :
number
- playback :
PlaybackState
- step :
integer
- trigger_note :
- Aliases
- Properties
- GateContext
- PatternContext
- RhythmOptions
- Properties
- unit :
"ms"
|"seconds"
|"bars"
|"beats"
|"1/1"
|"1/2"
|"1/4"
|"1/8"
|"1/16"
|"1/32"
|"1/64"
- resolution :
number``?
- offset :
number``?
- inputs :
InputParameter
[] - pattern :
boolean
|number
|0
|1
|Pulse
|nil
[] | (context :PatternContext
)->
boolean
|number
|0
|1
|Pulse
|nil
| (context :PatternContext
)->
(context :PatternContext
)->
boolean
|number
|0
|1
|Pulse
|nil
- repeats :
boolean
|integer
- gate : (context :
GateContext
)->
boolean
| (context :GateContext
)->
(context :GateContext
)->
boolean
- emit :
Cycle
|Sequence
|Note
|NoteValue
|Note
|NoteValue
[] | (context :EmitterContext
)->
NoteValue
| (context :EmitterContext
)->
(context :EmitterContext
)->
NoteValue
- unit :
- Aliases
- Properties
Global
Functions
rhythm(options : RhythmOptions
)
->
userdata
Create a new rhythm with the given configuration.
examples:
--trigger notes in an euclidean triplet pattern return rhythm { unit = "1/16", resolution = 4/3, pattern = pattern.euclidean(12, 16), emit = { "c4", "g4", { "c5 v0.7", "g5 v0.4" }, "a#4" } }
-- trigger a chord sequence every 4 bars after 4 bars return rhythm { unit = "bars", resolution = 4, offset = 1, emit = { note("c4'm"), note("g3'm7"):transpose({ 0, 12, 0, 0 }) } }
--trigger notes in a seeded, random subdivision pattern local seed = 23498 return rhythm { unit = "1/8", pattern = { 1, { 0, 1 }, 0, 0.3, 0.2, 1, { 0.5, 0.1, 1 }, 0.5 }, gate = function(init_context) local rand = math.randomstate(seed) return function(context) return context.pulse_value > rand() end end, emit = { "c4" } }
--trigger random notes in a seeded random pattern from a pentatonic scale local cmin = scale("c5", "pentatonic minor").notes return rhythm { unit = "1/16", pattern = function(context) return (context.pulse_step % 4 == 1) or (math.random() > 0.8) end, emit = function(context) return { key = cmin[math.random(#cmin)], volume = 0.7 } end }
--emit a tidal cycle every new bar return rhythm { unit = "bars", emit = cycle("[c4 [f5 f4]*2]|[c4 [g5 g4]*3]") }
Aliases
NoteValue
string
| number
| NoteTable
| nil
Pulse
boolean
| number
| boolean
| number
| 0
| 1
| Pulse
| nil
[] | 0
| 1
| nil
-- Single pulse value or a nested subdivision of pulses within a pattern. Pulse: | 0 | 1
EmitterContext
Context passed to 'emit' functions.
Properties
trigger_note : integer
?
Note value that triggered, started the rhythm, if any.
trigger_volume : number
?
Note volume that triggered, started the rhythm, if any.
trigger_offset : integer
?
Note slice offset value that triggered, started the rhythm, if any.
inputs : table<string
, boolean
| string
| number
>
Current input parameter values, using parameter ids as keys and the actual parameter value as value.
beats_per_min : number
Project's tempo in beats per minutes.
beats_per_bar : integer
Project's beats per bar setting.
samples_per_sec : integer
Project's sample rate in samples per second.
pulse_step : integer
Continues pulse counter, incrementing with each new skipped or emitted pulse. Unlike
step
in emitter this includes all pulses, so it also counts pulses which do not emit events. Starts from 1 when the rhythm starts running or is reset.
pulse_time_step : number
Continues pulse time counter, incrementing with each new skipped or emitted pulse. Starts from 0 and increases with each new pulse by the pulse's step time duration.
pulse_time : number
Current pulse's step time as fraction of a full step in the pattern. For simple pulses this will be 1, for pulses in subdivisions this will be the reciprocal of the number of steps in the subdivision, relative to the parent subdivisions pulse step time.
examples:
{1, {1, 1}} --> step times: {1, {0.5, 0.5}}
pulse_value : number
Current pulse value. For binary pulses this will be 1, 0 pulse values will not cause the emitter to be called, so they never end up here. Values between 0 and 1 will be used as probabilities and thus are maybe emitted or skipped.
playback : PlaybackState
Specifies how the emitter currently is running.
step : integer
Continues step counter, incrementing with each new emitted pulse. Unlike
pulse_step
this does not include skipped, zero values pulses so it basically counts how often the emit function already got called. Starts from 1 when the rhythm starts running or is reset.
Aliases
PlaybackState
"running"
| "seeking"
-- - *seeking*: The emitter is auto-seeked to a target time. All results are discarded. Avoid -- unnecessary computations while seeking, and only maintain your generator's internal state. -- - *running*: The emitter is played back regularly. Results are audible. PlaybackState: | "seeking" | "running"
GateContext
Context passed to
gate
functions.
Properties
trigger_note : integer
?
Note value that triggered, started the rhythm, if any.
trigger_volume : number
?
Note volume that triggered, started the rhythm, if any.
trigger_offset : integer
?
Note slice offset value that triggered, started the rhythm, if any.
inputs : table<string
, boolean
| string
| number
>
Current input parameter values, using parameter ids as keys and the actual parameter value as value.
beats_per_min : number
Project's tempo in beats per minutes.
beats_per_bar : integer
Project's beats per bar setting.
samples_per_sec : integer
Project's sample rate in samples per second.
pulse_step : integer
Continues pulse counter, incrementing with each new skipped or emitted pulse. Unlike
step
in emitter this includes all pulses, so it also counts pulses which do not emit events. Starts from 1 when the rhythm starts running or is reset.
pulse_time_step : number
Continues pulse time counter, incrementing with each new skipped or emitted pulse. Starts from 0 and increases with each new pulse by the pulse's step time duration.
pulse_time : number
Current pulse's step time as fraction of a full step in the pattern. For simple pulses this will be 1, for pulses in subdivisions this will be the reciprocal of the number of steps in the subdivision, relative to the parent subdivisions pulse step time.
examples:
{1, {1, 1}} --> step times: {1, {0.5, 0.5}}
pulse_value : number
Current pulse value. For binary pulses this will be 1, 0 pulse values will not cause the emitter to be called, so they never end up here. Values between 0 and 1 will be used as probabilities and thus are maybe emitted or skipped.
PatternContext
Context passed to
pattern
andgate
functions.
Properties
trigger_note : integer
?
Note value that triggered, started the rhythm, if any.
trigger_volume : number
?
Note volume that triggered, started the rhythm, if any.
trigger_offset : integer
?
Note slice offset value that triggered, started the rhythm, if any.
inputs : table<string
, boolean
| string
| number
>
Current input parameter values, using parameter ids as keys and the actual parameter value as value.
beats_per_min : number
Project's tempo in beats per minutes.
beats_per_bar : integer
Project's beats per bar setting.
samples_per_sec : integer
Project's sample rate in samples per second.
pulse_step : integer
Continues pulse counter, incrementing with each new skipped or emitted pulse. Unlike
step
in emitter this includes all pulses, so it also counts pulses which do not emit events. Starts from 1 when the rhythm starts running or is reset.
pulse_time_step : number
Continues pulse time counter, incrementing with each new skipped or emitted pulse. Starts from 0 and increases with each new pulse by the pulse's step time duration.
RhythmOptions
Construction options for a new rhythm.
Properties
unit : "ms"
| "seconds"
| "bars"
| "beats"
| "1/1"
| "1/2"
| "1/4"
| "1/8"
| "1/16"
| "1/32"
| "1/64"
Base time unit of the emitter. Use
resolution
to apply an additional factor, in order to create other less common rhythm bases.examples:
-- slightly off beat pulse unit = "beats", resolution = 1.01
-- triplet unit = "1/16", resolution = 4/3
resolution : number
?
Factor which is applied on
unit
to specify the final time resolution of the emitter.examples:
-- slightly off beat pulse unit = "beats", resolution = 1.01
-- triplet unit = "1/16", resolution = 4/3
offset : number
?
Optional offset in
unit * resolution
time units. By default 0. When set, the rhythm's event output will be delayed by the given offset value.examples:
-- start emitting after 4*4 beats unit = "1/4", resolution = 4, offset = 4
inputs : InputParameter
[]
Define optional input parameters for the rhythm. Input parameters can dynamically change a rhythms behavior everywhere where
context
s are passed, e.g. in pattern, gate, emitter or cycle map generator functions.examples:
-- trigger a single note as specified by input parameter 'note' -- when input parameter 'enabled' is true, else triggers nothing. return rhythm { inputs = { parameter.boolean("enabled", true), parameter.integer("note", 48, { 0, 127 }) }, emit = function(context) if context.inputs.enabled then -- boolean value return note(context.inputs.note) -- integer value else return nil end end }
pattern : boolean
| number
| 0
| 1
| Pulse
| nil
[] | (context : PatternContext
) ->
boolean
| number
| 0
| 1
| Pulse
| nil
| (context : PatternContext
) ->
(context : PatternContext
) ->
boolean
| number
| 0
| 1
| Pulse
| nil
Specify the rhythmical pattern of the rhythm. With the default
gate
implementation, each pulse with a value of1
ortrue
will cause an event from theemitter
property to be triggered in the emitters time unit.0
,false
ornil
values do not trigger.Patterns may contains subdivisions, sub tables of pulses, to "cram" multiple pulses into a single pulse's time interval. This way more complex rhythmical patterns can be created.
When no pattern is defined, a constant pulse value of
1
is triggered.Just like the
emitter
property, patterns can either be a static array of values or a function or generators which produces values dynamically.examples:
-- static pattern pattern = { 1, 0, 0, 1 }
-- "cram" pulses into a single pulse slot via subdivisions pattern = { 1, { 1, 1, 1 } }
-- patterns created via the "patterns" lib pattern = pattern.from{ 1, 0 } * 3 + { 1, 1 } pattern = pattern.euclidean(7, 16, 2)
-- stateless pattern function pattern = function(context) return math.random(0, 1) end
-- stateful generator function pattern = function(init_context) local triggers = table.new{ 0, 6, 10 } return function(context) local step = (context.pulse_step - 1) % 16 return triggers:contains(step) end end
repeats : boolean
| integer
If and how many times a pattern should repeat. When 0 or false, the pattern does not repeat and plays back only once. When true, the pattern repeats endlessly, which is the default. When a number > 0, this specifies the number of times the pattern repeats until it stops.
Note: When
pattern
is a function or iterator, the repeat count is the number of function calls or iteration steps. When the pattern is a pulse array, this is the number of times the whole pattern gets repeated.examples:
-- one-shot repeat = 0 -- also a one-shot repeat = false -- play the pattern 4 times repeat = 3 -- play & repeat forever (default) repeat = true
gate : (context : GateContext
) ->
boolean
| (context : GateContext
) ->
(context : GateContext
) ->
boolean
Optional pulse train filter function or generator function which filters events between the pattern and emitter. By default a threshold gate, which passes all pulse values greater than zero.
Custom function should returns true when a pattern pulse value should be passed, and false when the emitter should be skipped.
examples:
-- probability gate: skips all 0s, passes all 1s. pulse alues in range (0, 1) are -- maybe passed, using the pulse value as probablility. gate = function(context) return context.pulse_value > math.random() end
-- threshold gate: skips all pulse values below a given threshold value gate = function(context) return context.pulse_value > 0.5 end
emit : Cycle
| Sequence
| Note
| NoteValue
| Note
| NoteValue
[] | (context : EmitterContext
) ->
NoteValue
| (context : EmitterContext
) ->
(context : EmitterContext
) ->
NoteValue
Specify the melodic pattern of the rhythm. For every pulse in the rhythmical pattern, the event from the specified emit sequence. When the end of the sequence is reached, it starts again from the beginning.
To generate notes dynamically, you can pass a function or a function iterator, instead of a static array or sequence of notes.
Events can also be generated using the tidal cycle mini-notation. Cycles are repeated endlessly by default, and have the duration of a single pulse in the pattern. Patterns can be used to sequence cycles too.
examples:
-- a sequence of c4, g4 emit = {"c4", "g4"}
-- a chord of c4, d#4, g4 emit = {{"c4", "d#4", "g4"}} -- or {"c4'min"}
-- a sequence of c4, g4 with volume 0.5 emit = sequence{"c4", "g4"}:volume(0.5)
-- stateless generator function emit = function(context) return 48 + math.random(1, 4) * 5 end
-- stateful generator function emit = function(init_context) local count, step, notes = 1, 2, scale("c5", "minor").notes return function(context) local key = notes[count] count = (count + step - 1) % #notes + 1 return { key = key, volume = 0.5 } end end
-- a note pattern local tritone = scale("c5", "tritone") --[...] emit = pattern.from(tritone:chord(1, 4)):euclidean(6) + pattern.from(tritone:chord(5, 4)):euclidean(6)
-- a tidal cycle emit = cycle("<[a3 c4 e4 a4]*3 [d4 g3 g4 c4]>"),
Aliases
NoteValue
string
| number
| NoteTable
| nil
Pulse
boolean
| number
| boolean
| number
| 0
| 1
| Pulse
| nil
[] | 0
| 1
| nil
-- Single pulse value or a nested subdivision of pulses within a pattern. Pulse: | 0 | 1
scale
Global
Functions
scale(key : string
| number
, mode : ScaleMode
)
->
Scale
Create a new scale from the given key notes and a mode name.
Mode names can also be shortened by using the following synonyms:
- "8-tone" -> "eight-tone"
- "9-tone" -> "nine-tone"
- "aug" -> "augmented"
- "dim" -> "diminished"
- "dom" -> "Dominant"
- "egypt" -> "egyptian"
- "harm" -> "harmonic"
- "hungary" -> "hungarian"
- "roman" -> "romanian"
- "min" -> "minor"
- "maj" -> "major"
- "nat" -> "natural"
- "penta" -> "pentatonic"
- "span" -> "spanish",
examples:
scale("c4", "minor").notes -> {"c4", "d4", "d#4", "f4", "g4", "g#4", "a#4"}
-- Available scale mode names. mode: | "chromatic" | "major" | "minor" | "natural major" | "natural minor" | "pentatonic major" | "pentatonic minor" | "pentatonic egyptian" | "blues major" | "blues minor" | "whole tone" | "augmented" | "prometheus" | "tritone" | "harmonic major" | "harmonic minor" | "melodic minor" | "all minor" | "dorian" | "phrygian" | "phrygian dominant" | "lydian" | "lydian augmented" | "mixolydian" | "locrian" | "locrian major" | "super locrian" | "neapolitan major" | "neapolitan minor" | "romanian minor" | "spanish gypsy" | "hungarian gypsy" | "enigmatic" | "overtone" | "diminished half" | "diminished whole" | "spanish eight-tone" | "nine-tone"
scale_names()
->
string
[]
Return supported scale mode names.
Aliases
DegreeValue
integer
| "i"
| "ii"
| "iii"
| "iv"
| "v"
| "vi"
| "vii"
-- Roman number or plain number as degree in range [1 - 7] DegreeValue: | "i" | "ii" | "iii" | "iv" | "v" | "vi" | "vii"
NoteValue
string
| number
| NoteTable
| nil
ScaleMode
string
| "all minor"
| "augmented"
| "blues major"
| "blues minor"
| "chromatic"
| "diminished half"
| "diminished whole"
| "dorian"
| "enigmatic"
| "harmonic major"
| "harmonic minor"
| "hungarian gypsy"
| "locrian major"
| "locrian"
| "lydian augmented"
| "lydian"
| "major"
| "melodic minor"
| "minor"
| "mixolydian"
| "natural major"
| "natural minor"
| "neapolitan major"
| "neapolitan minor"
| "nine-tone"
| "overtone"
| "pentatonic egyptian"
| "pentatonic major"
| "pentatonic minor"
| "phrygian dominant"
| "phrygian"
| "prometheus"
| "romanian minor"
| "spanish eight-tone"
| "spanish gypsy"
| "super locrian"
| "tritone"
| "whole tone"
-- Available scale mode names. ScaleMode: | "chromatic" | "major" | "minor" | "natural major" | "natural minor" | "pentatonic major" | "pentatonic minor" | "pentatonic egyptian" | "blues major" | "blues minor" | "whole tone" | "augmented" | "prometheus" | "tritone" | "harmonic major" | "harmonic minor" | "melodic minor" | "all minor" | "dorian" | "phrygian" | "phrygian dominant" | "lydian" | "lydian augmented" | "mixolydian" | "locrian" | "locrian major" | "super locrian" | "neapolitan major" | "neapolitan minor" | "romanian minor" | "spanish gypsy" | "hungarian gypsy" | "enigmatic" | "overtone" | "diminished half" | "diminished whole" | "spanish eight-tone" | "nine-tone"
Scale
Properties
notes : integer
[]
Scale note values as integers, in ascending order of the mode, starting from the scale's key note.
Functions
chord(self, degree : DegreeValue
, note_count : integer
?
)
->
notes : integer
[]
Create a chord from the given degree, built from the scale's intervals. Skips nth notes from the root as degree, then takes every second note from the remaining scale to create a chord. By default a triad is created.
examples:
local cmin = scale("c4", "minor") cmin:chord("i", 4) --> {48, 51, 55, 58} note(cmin:chord(5)):transpose({12, 0, 0}) --> Gm 1st inversion
-- Roman number or plain number as degree in range [1 - 7] degree: | "i" | "ii" | "iii" | "iv" | "v" | "vi" | "vii"
degree(self, ...DegreeValue
)
->
... : integer
Get a single or multiple notes by its degree from the scale, using the given roman number string or a plain number as interval index. Allows picking intervals from the scale to e.g. create chords with roman number notation.
examples:
local cmin = scale("c4", "minor") cmin:degree(1) --> 48 ("c4") cmin:degree(5) --> 55 cmin:degree("i", "iii", "v") --> 48, 50, 55
-- Roman number or plain number as degree in range [1 - 7] ...(param): | "i" | "ii" | "iii" | "iv" | "v" | "vi" | "vii"
notes_iter(self, count : integer
?
)
Create an iterator function that returns up to
count
notes from the scale. If the count exceeds the number of notes in the scale, then notes from the next octave are taken.The iterator function returns nil when the maximum number of MIDI notes has been reached, or when the given optional
count
parameter has been exceeded.examples:
--collect 16 notes of a c major scale local cmaj = scale("c4", "major") local notes = {} for note in cmin:notes_iter(16) do table.insert(notes, note) end -- same using the `pattern` library local notes = pattern.new(16):init(cmaj.notes_iter())
fit(self, ...NoteValue
)
->
integer
[]
Fit given note value(s) into scale by moving them to the nearest note in the scale.
examples:
local cmin = scale("c4", "minor") cmin:fit("c4", "d4", "f4") --> 48, 50, 53 (cmaj -> cmin)
Aliases
DegreeValue
integer
| "i"
| "ii"
| "iii"
| "iv"
| "v"
| "vi"
| "vii"
-- Roman number or plain number as degree in range [1 - 7] DegreeValue: | "i" | "ii" | "iii" | "iv" | "v" | "vi" | "vii"
NoteValue
string
| number
| NoteTable
| nil
sequence
Global
Functions
sequence(...Note
| NoteValue
)
->
Sequence
Create a sequence from an array of note values or note value varargs.
Using
sequence
instead of a raw{}
table can be useful to ease transforming the note content and to explicitly pass a sequence of e.g. single notes to the emitter.examples:
-- sequence of C4, C5 and an empty note sequence(48, "c5", {}) -- sequence of a +5 transposed C4 and G4 major chord sequence("c4'maj", "g4'maj"):transpose(5)
Aliases
NoteValue
string
| number
| NoteTable
| nil
Sequence
Properties
notes : NoteTable
[][]
Functions
transpose(self, step : integer
| integer
[])
->
Sequence
Transpose all notes key values with the specified step value or values.
Values outside of the valid key range (0 - 127) will be clamped.
examples:
sequence("c4", "d#5"):transpose(12) sequence(note("c'maj"), note("c'maj")):transpose({0, 5})
amplify(self, factor : number
| number
[])
->
Sequence
Multiply all notes volume values with the specified factor or factors.
Values outside of the valid volume range (0 - 1) will be clamped.
examples:
sequence({"c4 0.5", "g4"}):amplify(0.5) sequence("c'maj 0.5"):amplify({2.0, 1.0, 0.3})
instrument(self, instrument : number
| number
[])
->
Note
Set the instrument attribute of all notes to the specified value or values.
volume(self, volume : number
| number
[])
->
Sequence
Set the volume attribute of all notes to the specified value or values.
examples:
sequence({"c4", "g4"}):volume(0.5) sequence("c'maj"):volume(0.5) sequence("c'maj"):volume({0.1, 0.2, 0.3})
panning(self, panning : number
| number
[])
->
Note
Set the panning attribute of all notes to the specified value or values.
delay(self, delay : number
| number
[])
->
Sequence
Set the delay attribute of all notes to the specified value or values.
Lua Module Extensions
math
Functions
imod(index : integer
, length : integer
)
->
integer
Wrap a lua 1 based integer index into the given array/table length.
->
(index - 1) % length + 1
random(m : integer
, n : integer
)
->
integer
math.random()
: Returns a float in the range [0,1).math.random(n)
: Returns a integer in the range [1, n].math.random(m, n)
: Returns a integer in the range [m, n].
randomseed(x : integer
)
math.randomseed(x, y)
: Concatenatex
andy
into a 128-bitseed
to reinitialize the pseudo-random generator.math.randomseed(x)
: Equate tomath.randomseed(x, 0)
.math.randomseed()
: Generates a seed with a weak attempt for randomness.
randomstate(seed : integer
?
)
->
(m : integer
?
, n : integer
?
) ->
number
Create a new local random number state with the given optional seed value.
When no seed value is specified, the global
math.randomseed
value is used. When no global seed value is available, a new unique random seed is created.Random states can be useful to create multiple, separate seeded random number generators, e.g. in pattern, gate or emit generators, which get reset with the generator functions.
examples:
return rhythm { emit = function(init_context) -- use a unique random sequence every time the rhythm gets (re)triggered local rand = math.randomstate(12345) return function(context) if rand(1, 10) > 5 then return "c5" else return "g4" end end }
See:
- math.random function arguments
- math.randomseed seeding
table
Functions
new(t : table
?
)
->
table
| tablelib
Create a new empty table, or convert an exiting table to an object that uses the global 'table.XXX' functions as methods, just like strings in Lua do.
examples:
t = table.new(); t:insert("a"); print(t[1]) -> "a"; t = table.new{1,2,3}; print(t:concat("|")); -> "1|2|3";
contains(t : table
, value : any
, start_index : integer
?
)
->
boolean
Test if the table contains an entry matching the given value, starting from element number start_index or 1.
examples:
t = {"a", "b"}; table.contains(t, "a") --> true t = {a=1, b=2}; table.contains(t, 2) --> true t = {"a", "b"}; table.contains(t, "c") --> false
find(t : table
, value : any
, start_index : integer
?
)
->
key : any
Find first match of given value, starting from element number start_index or 1.
Returns the first key that matches the value or nil
examples:
t = {"a", "b"}; table.find(t, "a") --> 1 t = {a=1, b=2}; table.find(t, 2) --> "b" t = {"a", "b", "a"}; table.find(t, "a", 2) --> "3" t = {"a", "b"}; table.find(t, "c") --> nil
tostring(t : table
)
->
string
Serialze a table to a string for display/debugging purposes.
copy(t : table
)
->
table
Copy the metatable and all elements non recursively into a new table. Creates a clone with shared references.
Lua Builtin Types
any
A type for a dynamic argument, it can be anything at run-time.
boolean
A built-in type representing a boolean (true or false) value, see details
function
A built-in type representing functions, see details
integer
A helper type that represents whole numbers, a subset of number
lightuserdata
A built-in type representing a pointer, see details
nil
A built-in type representing a non-existant value, see details. When you see
?
at the end of types, it means they can be nil.
number
A built-in type representing floating point numbers, see details
self
A type that represents an instance that you call a function on. When you see a function signature starting with this type, you should use
:
to call the function on the instance, this way you can omit this first argument.local object = SomeClass() object:do_something(123)
string
A built-in type representing a string of characters, see details
table
A built-in type representing associative arrays, see details
unknown
A dummy type for something that cannot be inferred before run-time.
userdata
A built-in type representing array values, see details.