Welcome
... to the pattrns scripting guide! pattrns is an experimental imperative music sequence generator engine that lets you create musical patterns programmatically using Lua scripts for algorithmic or live music coding.
This guide focuses on the Lua scripting API. For Rust development, see the pattrns crate docs.
Key Features
Programmatic
: Build musical patterns using dynamic scripts or precompiled code.
Modular
: Combine and reuse patterns components dynamically.
Flexible
: Create anything from simple beats to complex musical phrases.
Generative
: Make evolving compositions that change over time.
Dynamic
: Modify patterns live, during playback.
External Control
: Connect with MIDI/OSC controllers for hands-on parameter tweaking.
Templates
: Create user configurable pattern templates.
Cycles Mini-Notation
: Use familiar Tidal Cycles mini-notation for rapid pattern creation.
Applications
Online Playground
: Experiment with pattrns scripts directly in your browser
Rust Examples
: Write or test scripts in Rust or Lua with a basic sample player.
Documentation
Quickstart
: Start with a quick introduction through practical examples.
Guide
: Get into details with a more comprehensive documentation.
Examples
: More advanced examples, created and explained step by step.
Reference
: Read raw technical specifications of the Lua API.
Quickstart
This guide will take you from basic patterns to advanced rhythmic sequencing through practical examples. Each example builds on previous concepts and includes comments to help you understand how things work.
NOTE: pattrns uses Lua as a scripting language. If you're not familiar with Lua, don't worry. Lua is very easy to pick up and fortunately there are great tutorials out there, such as this one.
Online Playground
All the examples in this quickstart are available in the Online Playground as well, where you can run and modify them directly in your browser — no installation required.
Table of Contents
- Basic Patterns
- Rhythm Variations
- Notes and Scales
- Cycles Mini-Notation
- Dynamic Pulses & Events
- Advanced Techniques
Basic Patterns
Quarter Note Pulse
-- The most basic rhythm: steady quarter notes
return pattern {
unit = "1/4", -- Quarter note timing grid
event = "c4" -- Play middle C on each pulse
}
- TRY THIS: Change unit to
"1/8"
for eighth notes - TRY THIS: Replace
"c4"
with"e4"
or"g4"
for different notes
This creates a steady pulse playing C4 on every quarter note. The unit
parameter sets the timing grid, while event
defines what note to play.
Alternating Notes
-- Create a pattern that alternates between notes
return pattern {
unit = "1/8", -- Eighth note timing grid
pulse = {1, 0, 1, 1}, -- Play-rest-play-play pattern
event = {"c4", "d4"} -- Alternates between C4 and D4
}
- TRY THIS: Change pattern to
{1, 1, 0, 1}
for a different rhythm - TRY THIS: Add more note events like
{"c4", "d4", "e4", "g4"}
Here, pulse
controls when notes play (1) or rest (0). The event
parameter cycles through the provided notes when a step triggers.
Note properties
return pattern {
unit = "1/8",
event = { "c4 v0.2", "off d0.5", "g4 v0.8" }
}
- TRY THIS: Play specific instruments with # such as
c4 #8
- TRY THIS: Add delays to some of the notes
Note events can be expressed in different ways. Use properties such as v
= volume [0 - 1] p
= panning [-1 - 1] d
= delay [0 - 1] #
= instrument to modify notes.
See Notes & Scales for more info out how to express note events.
Rhythm Variations
Subdivided Pulses
-- Pattern with mixed note lengths
return pattern {
unit = "1/4",
pulse = {1, {1, 1, 1, 1}}, -- One quarter note, then four sixteenth notes
event = {"c4", "c5", "d4", "e4", "g4"} -- C4 (quarter), C5, d4, e4, g4 (sixteenth)
}
- TRY THIS: Try more complex subdivisions like {{1, 1}, {1, {1, 1}}}
- TRY THIS: Change the unit to "1/8" to make everything faster
Nested arrays in the pulse create subdivisions, allowing for more complex rhythms within the basic unit.
Triplet Resolution
-- Create swing or triplet feel
return pattern {
unit = "1/8",
resolution = 2/3, -- Triplet feel (3 notes in space of 2)
event = {"c4 v0.3", "e4 v0.5", "g4 v0.8"} -- v specifies volume, d delay, p panning
}
- TRY THIS: Change resolution to
"5/4"
for a different swing feel - TRY THIS: Add values such as
d0.2
between 0 and 1 to delay specific notes
The resolution
parameter modifies the timing grid, enabling triplet feels, swing rhythms, and polyrhythms.
Euclidean Rhythms
-- Distributes notes evenly across steps (common in many music traditions)
return pattern {
unit = "1/16",
pulse = pulse.euclidean(3, 8), -- 3 hits spread over 8 steps
event = "c4" -- Basic note
}
- TRY THIS: Try different combinations like
(5, 8)
or(7, 16, -2)
- TRY THIS: Use
pulse = pulse.euclidean(3, 8) + pulse.euclidean(5, 8)
to chain different patterns
Euclidean patterns distribute a number of notes evenly across steps, creating naturally pleasing rhythmic patterns found in many musical traditions.
The Pulse API contains various tools to programatically create patterns.
Notes and Scales
Basic Note Stacks
-- Simple chord by stacking notes
return pattern {
unit = "1/1",
event = {{"c4", "e4", "g4"}, "c4"} -- C major chord followed by a single C4
}
- TRY THIS: Try different chord combinations like
{"d4", "f4", "a4"}
for D minor - TRY THIS: Add
v
values to create dynamics:{"c4 v0.8", "e4 v0.6", "g4 v0.4"}
A table of notes allows emitting multiple notes at once, creating chords and harmonies.
Chord Notation
-- Using chord notation shortcuts
return pattern {
unit = "1/1",
event = {
"c4'M", -- C major using ' chord notation
"d4'm", -- D minor
"g4'dom7" -- G dominant 7th
}
}
- TRY THIS: Use other chord modes like
'm5
,'+
, or'dim
- TRY THIS: Add inversions with
note("c4'M"):transpose({12, 0, 0})
Chord notation provides a quick way to specify common chord types without listing individual notes.
Working with Scales
-- Advanced chord and scale operations
return pattern {
unit = "1/1",
event = {
chord("c4", "major"), -- C major via the chord function
chord("c4", {0, 4, 7}), -- C major via custom intervals
scale("c", "major"):chord(1), -- C major from 1st degree of C major scale
scale("c", "major"):chord(5) -- G major from 5th degree of C major scale
}
}
- TRY THIS: Use other scales like
"minor"
,"dorian"
, or"pentatonic"
- TRY THIS: Try different chord degrees:
scale("c", "major"):chord(2)
for D minor
The scale()
function allows creating chords from scale degrees, enabling an easy way to e.g. create chord progressions.
See available modes and scales at the API docs. See Notes and Scales for more ways to create and manipulate notes and chords.
Tidal Cycles Mini-Notation
Basic Cycle
-- Using tidal cycles notation for concise patterns
return pattern {
unit = "1/4", -- Emit a cycle every beat
event = cycle("c4 e4 g4") -- C major arpeggio
}
-- The simplified notation emits a cycle **per bar**
return cycle("c4 e4 g4")
Tidal Cycles' mini-notation provides a concise way to express patterns.
Alternating Cycles
-- Switching between different patterns
return pattern {
unit = "1/4",
event = cycle("[c4 e4 g4]|[d4 f4 a4]") -- Randomly select one of two chords
}
- TRY THIS: Add more patterns with
|
like[c4|c5 e4 g4]|[d4 f4|g5 a4]|[e4 g4 b4]
- TRY THIS: Try simultaneous notes with square brackets
[c4 e4]
The |
operator in cycles randomly selects different patterns.
Euclidean Rhythms in Cycles
-- Euclidean patterns in tidal cycles notation
return cycle("c4(3,8) e4(5,8) g4(7,8)") -- Different Euclidean rhythms
- TRY THIS: Combine with alternation:
c4(3,8)|e4(5,8)
- TRY THIS: Change the numbers for different distributions
Tidal Cycles mini-notation also supports Euclidean patterns with the (n,k)
notation, where n
is the number of notes and k
is the number of steps.
See Cycles Guide for more example and info about Tidal Cycles in pattrns.
Dynamic Pulses & Events
Random Note Selection
-- Randomly select notes from a list
local notes = {"c4", "d4", "e4", "g4"}
return pattern {
unit = "1/8",
event = function(context)
return notes[math.random(#notes)] -- Pick random note from array
end
}
- TRY THIS: Use notes from a specific scale with
local notes = scale("c4", "major").notes
- TRY THIS: Add amplitude variation with
note(some_note):amplify(0.5 + math.random() * 0.5)
Using functions for emitters enables dynamic behavior, like randomly selecting notes from a predefined set.
Probability-Based Emitting
-- Emit notes with certain probability
return pattern {
unit = "1/8",
pulse = {1, 1, 1, 1}, -- Regular pattern
event = function(context)
if math.random() < 0.3 then -- 30% chance to emit
return "c4"
end
end
}
- TRY THIS: Vary probability by step position:
if math.random() < (context.step % 4) / 4 then
- TRY THIS: Higher probability on downbeats:
if math.random() < ((context.pulse_step - 1) % 2 == 0 and 0.8 or 0.2) then
This pattern uses a gate function to filter notes with a 30% probability, creating a sparse, probabilistic pulse. Gates are evaluated after patterns but before emission, giving you control over which triggered notes actually play.
Stateful Arpeggiator
-- Create patterns that remember previous states
return pattern {
unit = "1/8",
event = function(init_context)
local notes = {"c4", "e4", "g4", "b4"}
local index = 1
return function(context)
local note = notes[index]
index = math.imod(index + 1, #notes) -- Cycle through notes
return note
end
end
}
- TRY THIS: Add direction changes:
if index >= #notes or index <= 1 then direction = direction * -1 end
- TRY THIS: Change notes based on time:
notes = scale("C4", {"major","minor"}[math.floor(context.time) % 2 + 1])
.notes
This example demonstrates stateful emitters that remember their position between calls, enabling sequences and other time-dependent behaviors.
Advanced Techniques
Conditional Gate
-- Filter which notes actually play using gates
return pattern {
unit = "1/8",
pulse = {1, 0.1, 1, 0.5, 1, 0.2, 1, 0.1}, -- probability values
gate = function(context)
-- always play on even-numbered step values
return (context.pulse_step - 1) % 2 == 0 or
-- else use pulse values as probablities
context.pulse_value >= math.random()
end,
event = "c4"
}
- TRY THIS: Create a threshold gate:
context.pulse_value > 0.5
- TRY THIS: Only play when a specific MIDI note is held:
context.trigger.notes[1].key == "C4"
Gates filter which triggered notes actually play, adding another layer of control to your patterns.
Dynamic Cycles
-- Identifiers in cycles can be dynamically mapped to something else
local s = scale("C4", "minor")
return pattern {
unit = "1/4",
event = cycle("I III V VII"):map(function(context, value)
-- value here is a single roman number from the cycle above
local degree = value
-- apply value as roman number chord degree
return s:chord(degree)
end)
}
- TRY THIS: Change scale to
"major", "dorian", or "pentatonic minor"
- TRY THIS: Add parameters:
parameter.enum("scale", "minor", {"major", "minor", "pentatonic"})
This example uses musical scale knowledge to generate chord progressions based on scale degrees.
See Parameters on how to add template parameters to patterns.
Generative Melody with Constraints
-- Create melodies that follow musical rules
return pattern {
unit = "1/8",
event = function(init_context)
local pentatonic = scale("c4", "pentatonic minor").notes
local last_note = 1
return function(context)
local next_note = math.random(#pentatonic)
-- Prefer steps of 1 or 2 scale degrees (smoother melodies)
while math.abs(next_note - last_note) > 2 do
next_note = math.random(#pentatonic)
end
last_note = next_note
return pentatonic[next_note]
end
end
}
- TRY THIS: Add occasional jumps:
if math.random() < 0.1 then ...
(allow larger intervals) - TRY THIS: Change directions based on contour: add direction variable that occasionally flips
This generates melodies that follow musical constraints, like preferring small intervals for more natural-sounding melodies.
Further Resources
Remember to experiment by modifying these examples! The best way to learn is by tweaking parameters and seeing what happens.
Guide
A Pattern
is the main building block in pattrns. It lets you define when and what to play.
pattrns consumes Lua script files that define patterns as specified in the API documentation.
Components
- TimeBase defines the time unit of a pattern.
- Pulse → Gate → Event perform the basic event generation in 3 stages.
- Parameters allow changing behavior of components during runtime.
All content in patterns can be either static or dynamic:
-
Static content is defined as a Lua table. There are various helpers included in the API, such as note scales, chords, and pulse table to ease creating static content.
-
Dynamic content is generated on the fly by Lua functions while the pattern runs. Generators are functions with local state, which can e.g. be used to apply replicable randomization.
Cycle events use Tidal Cycles mini-notation to create patterns in a flexible, condensed form.
Examples
See Quickstart for a set of simple examples to start and to play around with.
The Examples section contains more advanced and guided examples.
Timebase
A pattern's timebase
represents the unit of time for the pattern, 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 pattern is a beat.
The BPM and signature (beats per bar) settings are configured by the application which is running the pattern.
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
property 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 pattern {
unit = "beats",
resolution = 1.01,
event = "c4"
}
Sixteenth tripplets.
return pattern {
unit = "1/16",
resolution = 2/3,
event = "c4"
}
A beat expressed with resolution.
return pattern {
unit = "1/1",
resolution = 1/4,
event = "c4"
}
2 Seconds.
return pattern {
unit = "seconds",
resolution = 2,
event = "c4"
}
Pulse
A pattern's pulse
defines the temporal occurence of events - the rhythm. It triggers events 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.
Pulses can be generated by algorithms such as Euclidean rhythms, can be expressed as a static Lua tables, or can be generated by a dynamic generator - a Lua function.
The default pulse is an endless repeated pulse train of 1's.
Offset
By default, the pattern starts running immediately when the pattern is triggered. Using the offset
property you can delay the start by the amount specified in the pattern's unit.
» offset = 4
delay start by 4 * pattern'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 pulse is a Lua table (array) of numbers or boolean values:
» pulse = {0, 1, 1}
skip, trigger, trigger - repeat
» pulse = {1, 0, 1, 1}
1/2th followed by two 1/4th triggers in a 1/4th unit rhythm
» pulse = {0.8, 0, 1, 0.2}
pulse values are passed as numbers to gate and event in function contexts
tip
The default gate implementation skips all 0 pulses and passes all other pulse values to the event 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 event function to create accents.
Sub-Divisions
Each number value in the specified Lua table represents a single pulse in the pattern'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.
» pulse = {{1, 1, 1}, {1, 1}}
triplet followed by two quarter notes with unit 1/2
» pulse = {{1, 0}, {0, 1}}
basic bossanova rhythm with unit 1/4
Dynamic Patterns
When using a Lua function instead of a table as a pulse generator, you can dynamically generate pulse values.
» pulse = function(context) return math.random() end
randomly emit pulse values between 0 and 1
The expected return value of a dynamic pattern function is (true
, false
, 0
, 1
) or nil
and will be converted to a number when passed to the event emitter.
Use functions in order to create dynamic rhythms that can interact with the user, or to create probability based trigger rules. To connect the functions to its runtime use the passed context
argument.
See generators for more info about using functions as generators.
Pulse Library
pattrns comes with a built-in pulse
Lua library, which contains a bunch of helper functions and generators to ease creating pulse arrays.
» pulse = pulse.from{0, 1} * 3 + {1, 0}
combine sub pulses
» pulse = pulse.new(12, function(k, v) return k % 3 == 1 end),
functionally create pulses
» pulse = pulse.euclidean{3, 8, -1}
create euclidean rhythms
See Pattern API Lua reference for more info and examples.
Examples
Static pulse.
return pattern {
pulse = { 1, 0, 0, 1 },
event = "c4"
}
Cram pulses into a single pulse slot via subdivisions.
return pattern {
pulse = { 1, { 1, 1, 1 } },
event = "c4"
}
Static pulse pattern created using the "pulse" library.
return pattern {
pulse = pulse.from{1, 0} * 5 + {1, 1},
event = "c4"
}
Euclidean pattern created using the "pulse" library.
return pattern {
pulse = pulse.euclidean(7, 16, 2),
event = "c4"
}
Stateless function.
return pattern {
pulse = function(context)
return math.random(0, 1)
end,
event = "c4"
}
Stateful function.
return pattern {
pulse = function(init_context)
local rand = math.randomstate(12345)
local triggers = table.from{ 0, 6, 10 }
return function(context)
local step = (context.pulse_step - 1) % 16
return rand() > 0.8 and triggers:contains(step)
end
end,
event = "c4"
}
See generators for more info about stateful functions.
Gate
A pattern's gate
is an optional filter unit that determines whether or not an event should be passed from the pulse to the event stage. It can be used to statically or dynamically filter out pulses.
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 pattern {
pulse = { 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,
event = { "c4" }
}
A gate which filters out pulse values with on a configurable threshold.
return pattern {
parameter = {
parameter.number("threshold", 0.5, {0, 1})
},
pulse = {
0.2, { 0.5, 1 }, 0.9, { 1, 0.8 }
},
gate = function(context)
return context.pulse_value >= context.parameter.threshold
end,
event = { "c4" }
}
See generators for more info about stateful generators and parameters about pattern parameters.
Event
A pattern's event
defines which events should be played for incoming pulse values. Just like the pulse, 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 event emitter.
The default event implementation emits a single middle C note value for each incoming pulse.
Event Types
Currently pattrns only supports monophonic or polyphonic note events as event output. This is likely to change in the future to allow other musically interesting events to be emitted.
Note values can be expressed as:
- Integer values like
48
, which are interpreted as MIDI note numbers. - 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 an event (sequence) is a Lua array of note values.
Static event arrays define note event sequences. Each incoming, possibly filtered, gated pulse from the pulse picks the next event in the sequence, then moves on in the sequence. Sequences are repeated as long as the pattern is running.
» event = { "c4", "d#4", "g4" }
arpeggio - sequence
» event = { { "c4", "d#4", "g4" } }
single chord - single event
To ease distinguishing polyponic contents, use sequence
and note
:
» event = sequence("c4", "d#4", "g4")
arpeggio - sequence
» event = note("c4", "d#4", "g4")
single chord - single event
Dynamic Emitters
Dynamic event functions return single note events. Each incoming, possibly filtered, gated value from the pulse will trigger the event function to create the next event as long as the pattern is running.
» event = function(context) return math.random() > 0.5 and "c4" or "c5" end
randomly emit c4 or c5 notes
» event = 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 event 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 Events
Cycle event emitters emit a whole cycle for a single pulse. So any incoming, possibly filtered, gated value from the pulse will trigger a full cycle as long as the pattern is running.
» event = cycle("[c4, d#4, g4]")
a single chord
You likey won't use custom pulse or gate functions with cycles, but it's possible to sequence entire cycles with them, or use cycles as single note generators too:
» event = cycle("[c4 d#4 g4]")
arpeggio
» event = cycle("[c4 <d#4 d4> g4|g5]")
arpeggio with variations
See cycles for more info about Tidal Cycles support in pattrns.
Examples
Sequence of c4, g4 notes.
return pattern {
event = { "c4", "g4" }
}
Chord of c4, d#4, g4 notes.
return pattern {
event = sequence(
{ "c4", "d#4", "g4" }, -- or "c4'min"
{ "---", "off", "off" }
)
}
Sequence of c4, g4 with volume 0.5.
return pattern {
event = sequence{"c4", "g4"}:volume(0.5)
}
Stateless function.
return pattern {
event = function(context)
return 48 + math.random(1, 4) * 5
end
}
Stateful generator.
return pattern {
event = 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 "pulse" lib.
local tritone = scale("c5", "tritone")
return pattern {
event = pulse.from(tritone:chord(1, 4)):euclidean(6) +
pulse.from(tritone:chord(5, 4)):euclidean(6)
}
Tidal cycle.
return pattern {
event = 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 event, can be expressed and modified in various ways. Sometimes it's easier to generate notes programmatically using MIDI note numbers. Other times you may want to write down a chord in a more expressible form via a string.
Notes
Note Numbers
Raw integer values like 48
, are interpreted as MIDI note numbers in the note
function and event
property. Valid MIDI notes are 0-127
.
» event = 48
emit a single c4 note event
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]
» event = { 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
» event = "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
» event = { "f#4 #1 v0.2" }
emit a f sharp for instrument 1 with 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.
» event = "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.
» event = "c4'69 #1 v0.5"
instrument 1, volume 0.5
Note Objects
Note numbers, strings and tables, as described above, can be fed into a note object in the LuaAPI as well, which allows further transformation of the note.
This is especially handy for chords, but also can be more verbose than using note string attributes.
» event = note(48):volume(0.1)
c4 note with volume of 0.1
» event = note({key = "c4"}):volume(0.2)
c4 note with volume of 0.2
» event = note("c4'min"):transpose({12, 0, 0})
C minor 1st inversion
See note Lua API for details.
Note Chord Objects
Notes objects can also be created using the chords
function.
» event = chord(48, "major")
c4 major notes
This also allows the use of custom interval tables.
» event = 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, pattrns 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 pattern {
event = 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, event
in pattrns can also use cycles using the tidal cycles mini-notation
Introduction
Cycles let you create repeating musical patterns using a simple text notation. Think of it like writing drum patterns or melodies in a compact, readable way.
-- Basic pattern with notes and rests
return cycle("c4 d4 <e4 g4> _")
This plays:
- C4
- D4
- Alternate between E4 and G4
- play no event:
_
means rest
Pattern Basics
Key symbols to know:
Symbol | Meaning | Example |
---|---|---|
| Separates steps | c4 d4 |
, | Parallel patterns | [c4,e4], [g4,a4] |
< > | Alternates between values | <c4 e4 g4> |
| | Random choice | c4|d4|e4 |
* | Repeat | c4*4 |
_ | Elongate | c4 _ d4 |
~ | Rest | c4 ~ d4 |
tip
Please see Tidal Cycles Mini-Notation Reference for a complete overview of the cycle notation.
Basic Examples
Basic scale:
return cycle("c4 d4 e4 f4 g4 a4 b4 c5")
Drum pattern:
return cycle("[c1 ~ e1 ~]*2")
Random melody:
return cycle("[c4|d4|e4|f4|g4|a4]*8")
Combining with Pulses
Control when cycles play using pulse
:
return pattern {
unit = "bars", -- Timing unit
pulse = {1, 0}, -- Play on odd bars only
event = cycle("c4 d4 e4 f4")
}
Advanced Usage
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]
) -
:
sets the instrument or remappable target instead of selecting samples but also allows setting note attributes such as instrument/volume/pan/delay (e.g.c4:v0.1:p0.5
) -
In bjorklund expressions, operators within are not supported (e.g.
bd(<3 2>, 8)
is not supported)
Timing
The base time of a pattern in tidal is specified as cycles per second. In pattrns, the time of a cycle instead is given in cycles per pattern pulse units.
-- emits an entire cycle every beat
return pattern {
unit = "beats",
event = cycle("c4 d4 e4") -- triplet
}
Sequencing
An event in pattrns gets triggered for each incoming non-gated pattern pulse. This is true for cycles are well and allows you to sequence cycles too.
-- emit an entire cycle's every bar, then pause for two bars, then repeat
return pattern {
unit = "bars",
pulse = { 1, 0, 0 },
event = cycle("c d e f")
}
You can also use the mini notation to emit single notes only, e.g. making use of the cycle's note alternating and randomization features only:
-- emit a single note from a cycle in an euclidean pattern
return pattern {
unit = "beats",
pulse = pulse.euclidean(5, 8),
event = cycle("<c d e f g|b>")
}
Seeding
pattrns'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)
.
Note Attributes
You can set note attributes in cycle patterns using chained :
expressions:
-- Set instrument (2), panning (-0.5), and delay (0.25)
cycle("d4:2:p-0.5:d0.25")
-- Set instrument (1) with alternating volumes (0.1, 0.2)
cycle("c4:1:<v0.1 v0.2>")
-- Set multiple attributes with randomization
cycle("c4:[v0.5:d0.1|v0.8]")
Supported note attributes are:
- Instrument:
:#X
- same as:X
, without the#
- Volume:
:vX
- with X [0.0-1.0] - Panning:
:pX
- with X [-1.0 to 1.0] - Delay:
:dX
- with X [0.0-1.0)
Note that X
must be written as floating point number for volume, panning and delay: c4:p-1.0
and c4:p.8
is valid, while c4:p-1
is not valid!
If you want to use expressions (like slowing down) for an attribute pattern on the right side, you'll have to wrap it in square brackets, otherwise the expression applies to the entire pattern, not just the attributes'.
-- This slows down the output
cycle("[c4 d#4 e4]:<v.1 v.2>/2")
-- This slows down the alternating for the volume
cycle("[c4 d#4 e4]:[<v.1 v.2>/2]")
A shorthand for assigning attributes exists in the form of :v=X
where X
can be a pulse. This way, you can supply float values without having to repeat the name of the target attribute.
-- Set volume to rise for each cycle
cycle("[c4 d#4 e4]:v=<.1 .2 .3 .4>")
-- This would be the same as
cycle("[c4 d#4 e4]:<v.1 v.2 v.3 v.4>")
Mapping
Notes and chords in cycles are expressed as note strings in pattrns. But you can also dynamically evaluate and map cycle identifiers using the cycle map
function.
This allows you, for example, to inject parameters into cycles or to use custom identifiers.
Using custom identifiers with a static map (a Lua table):
return 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 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)
Advanced Examples
Chord progression
return cycle("[c'M g'M a'm f'M]/4")
A polyrhythm
return cycle("[C3 D#4 F3 G#4], [[D#3?0.2 G4 F4]/64]*63")
Alternate panning with note attributes
cycle("c4:<p-0.5 p0.0 p0.5>")
Mapped multi channel beats
-- use [=[ and ]=] as Lua multiline string
return 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 pattern {
unit = "1/1",
resolution = 4,
parameter = {
parameter.enum("mode", "major", { "major", "minor" })
},
event = cycle("I V III VI"):map(
function(init_context, value)
local s = scale("c4", init_context.parameter.mode)
return function(context, value)
return value ~= "_" and s:chord(value) or value
end
end
)
}
Parameters
Pattern parameters
allow user controlled parameter values to be injected into a pattern. This allows you to write more flexible patterns that can be used as templates or to automate functions within the pattern.
Input parameters can be accessed in dynamic pulse
, gate
, event
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 is used to access the actual paramter value in the function contexts.
Definition:
» parameter = { parameter.boolean("enabled", true) }
Usage:
» event = function(context) return context.parameter.enabled and "c5" or nil }
Usage, if you've got spaces in your ids (not recommended):
» event = function(context) return context.parameter["enabled"] and "c5" or nil }
Examples
Euclidean pattern generator with user configurable steps, pulses, offset value.
return pattern {
parameter = {
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",
pulse = function(context)
return pulse.euclidean(
math.min(context.parameter.steps, context.parameter.pulses),
context.parameter.pulses,
context.parameter.offset)
end,
event = "c4"
}
Random bass line generator with user defined custom scales and variations (seeds).
local scales = {"Chromatic", "Minor", "Major"}
return pattern {
parameter = {
parameter.enum('scale', scales[1], scales, "Scale"),
parameter.integer('notes', 7, {1, 12}, "#Notes"),
parameter.integer('variation', 0, {0, 0xff}, "Variation"),
},
unit = "1/1",
pulse = function(context)
local rand = math.randomstate(2345 + context.parameter.variation)
return pulse.euclidean(rand(3, 16), 16, 0)
end,
event = function(context)
local notes = scale("c4", context.parameter.scale).notes
local rand = math.randomstate(127364 + context.parameter.variation)
local notes = pulse.new(context.parameter.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 pattern {
unit = "1/1",
parameter = {
parameter.integer("bd_note", 48, {0, 127}),
parameter.integer("sn_note", 70, {0, 127}),
parameter.integer("hh_note", 98, {0, 127})
},
event = 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 patterns.
Please read the basic concepts from the guide first to get you started...
Generators
Pulse, Gate and Event can be specified as Lua functions to dynamically generate or evaluate content.
Anonymous Lua functions, as used in patterns, 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 pattrns scripts to keep track of a pattern's global or local state.
Runtime
To better understand how local and global states are relevant here, we first need to understand how patterns are evaluated.
Let's say we're in a DAW that supports pattrns. This DAW triggers your pattern 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 pattern 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 event function keeps track of its state by referencing a globally defined counter
variable.
local counter = 0
return pattern {
event = function(context)
local midi_note = counter
counter = (counter + 1) % 128
return note(midi_note)
end,
}
When playing a single instance of this pattern, 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 pattern's internal playback state.
A context
passed to pattern functions only contains the global playback status. A context
passed to gate and event functions contains the global playback status and status of the pulse.
See pattern context API, gate context API, event context API for details.
Contexts also may contain user controlled input variables. See parameters for more info about this.
By making use of the context we can now rewrite the example above to:
return pattern {
event = 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 pattern instance, we now get multiple continously increasing note event streams again.
Generators
Generators in pattrns 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 pattern {
event = 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 pattern is started - just to create the local state and to return the actual emit function. The returned function is then called repeatedly while the pattern 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 pattern 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 pattern 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 pattrns 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 pattrns, and can use math.randomseed()
to seed them.
Note that the standard Lua random implementation is overridden by pattrns, 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 = pulse.new(10, function()
return cmin.notes[math.random(#cmin.notes)]
end)
return pattern {
event = 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 = pulse.new(10, function()
return cmin.notes[math.random(#cmin.notes)]
end)
return pattern {
event = random_notes
}
Local Random Number Generators
When seeding the RNG, each time a pattern is (re)started, an existing pattern instance will continue to run. The global state of a pattern script is not recreated each time the pattern is played again.
See generators for details of how pattrns 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 pattern {
event = 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 pattrns 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 pattrns type definitions folder (./types/pattrns
) 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"
- "7 -> "seven"
- "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" | "seven" | "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"
| "seven"
| "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" | "seven" | "nine" | "eleven" | "thirteen" | "augmented" | "diminished" | "diminished7" | "sus2" | "sus4" | "7sus2" | "7sus4" | "9sus2" | "9sus4"
NoteValue
string
| number
| Note
| 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]
):
sets the instrument or remappable target instead of selecting samples but also allows setting note attributes such as instrument/volume/pan/delay (e.g.c4:v0.1:p0.5
)- In bjorklund expressions, operators within and on the right side are not supported (e.g.
bd(<3 2>, 8)
andbd(3, 8)*2
are not supported)examples:
--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
NoteValue
string
| number
| Note
| NoteTable
| nil
PlaybackState
"running"
| "seeking"
-- - *seeking*: The pattern is auto-seeked to a target time. All events are discarded. Avoid -- unnecessary computations while seeking, and only maintain your generator's internal state. -- - *running*: The pattern is played back regularly. Events are emitted and 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
NoteValue
string
| number
| Note
| NoteTable
| nil
PlaybackState
"running"
| "seeking"
-- - *seeking*: The pattern is auto-seeked to a target time. All events are discarded. Avoid -- unnecessary computations while seeking, and only maintain your generator's internal state. -- - *running*: The pattern is played back regularly. Events are emitted and 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
?
Note that triggered the pattern, if any. Usually will ne a monophic note. To access the raw note number value use:
context.trigger.notes[1].key
parameter : table<string
, boolean
| string
| number
>
Current parameter values: parameter ids are keys, parameter values are values. To access a parameter with id
enabled
use:context.parameter.enabled
beats_per_min : number
Project's tempo in beats per minutes.
beats_per_bar : integer
Project's beats per bar settings - usually will be 4.
samples_per_sec : integer
Project's audio playback sample rate in samples per second.
Aliases
PlaybackState
"running"
| "seeking"
-- - *seeking*: The pattern is auto-seeked to a target time. All events are discarded. Avoid -- unnecessary computations while seeking, and only maintain your generator's internal state. -- - *running*: The pattern is played back regularly. Events are emitted and audible. PlaybackState: | "seeking" | "running"
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 (number in range [0-1]) -'p' -> panning (number in range [-1-1]) -'d' -> delay (number in range [0-1])
examples:
note(48) --> 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
note_number(note : NoteValue
)
->
integer
Convert a note string or note table to a raw MIDI note number in range 0-127 or 0xFE for nil or empty note strings or 0xFF for note offs
Examples:
note_number("c4") --> 48 note_number(note("c4")) --> 48 note_number("-") --> 0xFE note_number("off") --> 0xFF note_number("xyz") --> error
Aliases
NoteValue
string
| number
| Note
| NoteTable
| nil
Note
Properties
notes : NoteTable
[]
Functions
transpose(self, step : integer
| integer
[])
->
Note
Transpose the note 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 & octave string (or MIDI note number as setter)
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]
parameter
- Parameter
- Functions
- boolean(id :
ParameterId
, default :ParameterBooleanDefault
, name :ParameterName``?
, description :ParameterDescription``?
) - integer(id :
ParameterId
, default :ParameterIntegerDefault
, range :ParameterIntegerRange``?
, name :ParameterName``?
, description :ParameterDescription``?
) - number(id :
ParameterId
, default :ParameterNumberDefault
, range :ParameterNumberRange``?
, name :ParameterName``?
, description :ParameterDescription``?
) - enum(id :
ParameterId
, default :ParameterEnumDefault
, values :string
[], name :ParameterName``?
, description :ParameterDescription``?
)
- boolean(id :
- Aliases
- Functions
Parameter
Opaque parameter user data. Construct new parameters via the
parameter.XXX(...)
functions.
Functions
boolean(id : ParameterId
, default : ParameterBooleanDefault
, name : ParameterName
?
, description : ParameterDescription
?
)
Creates an Parameter with "boolean" Lua type with the given default value and other optional properties.
integer(id : ParameterId
, default : ParameterIntegerDefault
, range : ParameterIntegerRange
?
, name : ParameterName
?
, description : ParameterDescription
?
)
Creates an Parameter with "integer" Lua type with the given default value and other optional properties.
number(id : ParameterId
, default : ParameterNumberDefault
, range : ParameterNumberRange
?
, name : ParameterName
?
, description : ParameterDescription
?
)
Creates an Parameter with "number" Lua type with the given default value and other optional properties.
enum(id : ParameterId
, default : ParameterEnumDefault
, values : string
[], name : ParameterName
?
, description : ParameterDescription
?
)
Creates an Parameter with a "string" Lua type with the given default value, set of valid values to choose from and other optional properties.
Aliases
ParameterBooleanDefault
Default boolean value.
ParameterDescription
Optional long description of the parameter describing what the parameter does.
ParameterEnumDefault
Default string value. Must be a valid string within the specified value set.
ParameterId
Unique id of the parameter. The id will be used in the
parameter
context table as key.
ParameterIntegerDefault
Default integer value. Must be in the specified value range.
ParameterIntegerRange
Optional value range. When undefined (0.0 - 1.0)
ParameterName
Optional name of the parameter as displayed to the user. When undefined, the id is used.
ParameterNumberDefault
Default number value. Must be in the specified value range.
ParameterNumberRange
Optional value range. When undefined (0 - 100)
pattern
- Global
- EventContext
- GateContext
- PatternOptions
- Properties
- unit :
"ms"
|"seconds"
|"bars"
|"beats"
|"1/1"
|"1/2"
|"1/4"
|"1/8"
|"1/16"
|"1/32"
|"1/64"
- resolution :
number
- offset :
number
- parameter :
Parameter
[] - pulse :
boolean
|number
|0
|1
|PulseValue
|nil
[] | (context :PulseContext
)->
boolean
|number
|0
|1
|PulseValue
|nil
| (context :PulseContext
)->
(context :PulseContext
)->
boolean
|number
|0
|1
|PulseValue
|nil
- repeats :
boolean
|integer
- gate : (context :
GateContext
)->
boolean
| (context :GateContext
)->
(context :GateContext
)->
boolean
- event :
Cycle
|Sequence
|Note
|NoteValue
|NoteValue
[] | (context :EventContext
)->
NoteValue
| (context :EventContext
)->
(context :EventContext
)->
NoteValue
- unit :
- Aliases
- Properties
- PulseContext
Global
Functions
pattern(options : PatternOptions
)
->
userdata
Create a new pattern with the given properties table:
examples:
--trigger notes in an euclidean triplet pattern return pattern { unit = "1/16", resolution = 2/3, pulse = pulse.euclidean(12, 16), event = { "c4", "g4", { "c5 v0.7", "g5 v0.4" }, "a#4" } }
-- trigger a chord sequence every 4 bars after 4 bars return pattern { unit = "bars", resolution = 4, offset = 1, event = { note("c4'm"), note("g3'm7"):transpose({ 0, 12, 0, 0 }) } }
--trigger notes in a seeded, random subdivision pattern local seed = 23498 return pattern { unit = "1/8", pulse = { 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, event = { "c4" } }
--trigger random notes in a seeded random pattern from a pentatonic scale local cmin = scale("c5", "pentatonic minor").notes return pattern { unit = "1/16", pulse = function(context) return (context.pulse_step % 4 == 1) or (math.random() > 0.8) end, event = function(context) return { key = cmin[math.random(#cmin)], volume = 0.7 } end }
--emit a tidal cycle every bar return pattern { unit = "bars", event = cycle("[c4 [f5 f4]*2]|[c4 [g5 g4]*3]") }
Aliases
NoteValue
string
| number
| Note
| NoteTable
| nil
PulseValue
boolean
| number
| boolean
| number
| 0
| 1
| PulseValue
| nil
[] | 0
| 1
| nil
-- Single pulse value or a nested subdivision of pulses within a rhythm's pulse. PulseValue: | 0 | 1
EventContext
Event related context passed to functions in 'emit'.
Properties
trigger : Note
?
Note that triggered the pattern, if any. Usually will ne a monophic note. To access the raw note number value use:
context.trigger.notes[1].key
parameter : table<string
, boolean
| string
| number
>
Current parameter values: parameter ids are keys, parameter values are values. To access a parameter with id
enabled
use:context.parameter.enabled
beats_per_min : number
Project's tempo in beats per minutes.
beats_per_bar : integer
Project's beats per bar settings - usually will be 4.
samples_per_sec : integer
Project's audio playback sample rate in samples per second.
pulse_step : integer
Continues pulse counter, incrementing with each new skipped or emitted pulse. Unlike
step
in event this includes all pulses, so it also counts pulses which do not emit events. Starts from 1 when the pattern starts running or after it got 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 pulse. 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 0 or 1, but it can be any number value.
playback : PlaybackState
Specifies how the pattern 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 event function already got called. Starts from 1 when the pattern starts running or is reset.
Aliases
PlaybackState
"running"
| "seeking"
-- - *seeking*: The pattern is auto-seeked to a target time. All events are discarded. Avoid -- unnecessary computations while seeking, and only maintain your generator's internal state. -- - *running*: The pattern is played back regularly. Events are emitted and audible. PlaybackState: | "seeking" | "running"
GateContext
Pulse value context passed to functions in
gate
andevent
.
Properties
trigger : Note
?
Note that triggered the pattern, if any. Usually will ne a monophic note. To access the raw note number value use:
context.trigger.notes[1].key
parameter : table<string
, boolean
| string
| number
>
Current parameter values: parameter ids are keys, parameter values are values. To access a parameter with id
enabled
use:context.parameter.enabled
beats_per_min : number
Project's tempo in beats per minutes.
beats_per_bar : integer
Project's beats per bar settings - usually will be 4.
samples_per_sec : integer
Project's audio playback sample rate in samples per second.
pulse_step : integer
Continues pulse counter, incrementing with each new skipped or emitted pulse. Unlike
step
in event this includes all pulses, so it also counts pulses which do not emit events. Starts from 1 when the pattern starts running or after it got 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 pulse. 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 0 or 1, but it can be any number value.
PatternOptions
Construction options for a new pattern.
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 pattern. Use
resolution
to apply an additional factor, in order to create other less common time bases.examples:
-- slightly off beat pulse unit = "beats", resolution = 1.01
-- triplet unit = "1/16", resolution = 2/3
resolution : number
Factor which is applied on
unit
to specify the final time resolution of the pattern.examples:
-- slightly off beat pulse unit = "beats", resolution = 1.01
-- triplet unit = "1/16", resolution = 2/3
offset : number
Optional offset in
unit * resolution
time units. By default 0. When set, the pattern'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
parameter : Parameter
[]
Define optional parameters for the pattern. Parameters can dynamically change a patterns behavior everywhere where
context
s are passed, e.g. inpulse
,gate
,event
orcycle
map generator functions.examples:
-- trigger a single note as specified by parameter 'note' -- when parameter 'enabled' is true, else triggers nothing. return pattern { parameter = { parameter.boolean("enabled", true), parameter.integer("note", 48, { 0, 127 }) }, event = function(context) if context.parameter.enabled then -- boolean value return note(context.parameter.note) -- integer value else return nil end end }
pulse : boolean
| number
| 0
| 1
| PulseValue
| nil
[] | (context : PulseContext
) ->
boolean
| number
| 0
| 1
| PulseValue
| nil
| (context : PulseContext
) ->
(context : PulseContext
) ->
boolean
| number
| 0
| 1
| PulseValue
| nil
Defines the rhythmical part of the pattern. With the default
gate
implementation, each pulse with a value of1
ortrue
will cause an event from theevent
property to be triggered in the pattern's time unit.0
,false
ornil
values do not trigger.Pulses may contain 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 pulse is defined, a constant pulse value of
1
is triggered.Just like the
event
property, pulses can either be a static array of values or a function or generators which produces pulse values dynamically.examples:
-- static pattern pulse = { 1, 0, 0, 1 }
-- "cram" pulses into a single pulse slot via sub-divisions pulse = { 1, { 1, 1, 1 } }
-- pulses created via the "pulse" lib pulse = pulse.from{ 1, 0 } * 3 + { 1, 1 } pulse = pulse.euclidean(7, 16, 2)
-- stateless pulse function pulse = function(context) return math.random(0, 1) end
-- stateful pulse function pulse = 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
pulse
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 pulse 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 which filters events between the pulse and event emitter. By default a threshold gate, which passes all pulse values greater than zero.
Functions return true when a pulse value should be passed, and false when the event 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
event : Cycle
| Sequence
| Note
| NoteValue
| NoteValue
[] | (context : EventContext
) ->
NoteValue
| (context : EventContext
) ->
(context : EventContext
) ->
NoteValue
Specify the event values of the pattern. For every pulse in the pulse pattern, an event is picked from the specified event sequence. When the end of the sequence is reached, it starts again from the beginning.
To generate events dynamically, you can pass a function or a function iterator, instead of a static array or sequence of notes.
Events can also be generated via a tidal cycle mini-notation. Cycles are repeated endlessly by default, and have the duration of a single step in the patterns. Pulses can be used to sequence cycles too.
examples:
-- a sequence of c4, g4 event = {"c4", "g4"}
-- a chord of c4, d#4, g4 event = {{"c4", "d#4", "g4"}} -- or {"c4'min"}
-- a sequence of c4, g4 with volume 0.5 event = sequence{"c4", "g4"}:volume(0.5)
-- stateless generator function event = function(context) return 48 + math.random(1, 4) * 5 end
-- stateful generator function event = 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") --[...] event = pulse.from(tritone:chord(1, 4)):euclidean(6) + pulse.from(tritone:chord(5, 4)):euclidean(6)
-- a tidal cycle event = cycle("<[a3 c4 e4 a4]*3 [d4 g3 g4 c4]>"),
Aliases
NoteValue
string
| number
| Note
| NoteTable
| nil
PulseValue
boolean
| number
| boolean
| number
| 0
| 1
| PulseValue
| nil
[] | 0
| 1
| nil
-- Single pulse value or a nested subdivision of pulses within a rhythm's pulse. PulseValue: | 0 | 1
PulseContext
Pulse timing context passed to functions in
pulse
andgate
.
Properties
trigger : Note
?
Note that triggered the pattern, if any. Usually will ne a monophic note. To access the raw note number value use:
context.trigger.notes[1].key
parameter : table<string
, boolean
| string
| number
>
Current parameter values: parameter ids are keys, parameter values are values. To access a parameter with id
enabled
use:context.parameter.enabled
beats_per_min : number
Project's tempo in beats per minutes.
beats_per_bar : integer
Project's beats per bar settings - usually will be 4.
samples_per_sec : integer
Project's audio playback sample rate in samples per second.
pulse_step : integer
Continues pulse counter, incrementing with each new skipped or emitted pulse. Unlike
step
in event this includes all pulses, so it also counts pulses which do not emit events. Starts from 1 when the pattern starts running or after it got 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
- Pulse
- Functions
- new(length :
integer``?
, value :PulseTableValue
| (index :integer
)->
PulseTableValue``?
) - from(...
PulseTableValue
|PulseTableValue
[]) - copy(self :
Pulse
) - distributed(steps :
integer
|table
, length :integer
, offset :integer``?
, empty_value :PulseTableValue``?
) - euclidean(steps :
integer
|table
, length :integer
, offset :integer``?
, empty_value :PulseTableValue``?
) - unpack(self :
Pulse
) - subrange(self :
Pulse
, i :integer
, j :integer``?
, empty_value :PulseTableValue``?
) - take(self :
Pulse
, length :integer
, empty_value :PulseTableValue``?
) - clear(self :
Pulse
) - init(self :
Pulse
, value :PulseTableValue
| (index :integer
)->
PulseTableValue
, length :integer``?
) - map(self :
Pulse
, fun : (index :integer
, value :PulseTableValue
)->
PulseTableValue
) - reverse(self :
Pulse
) - rotate(self :
Pulse
, amount :integer
) - push_back(self :
Pulse
, ...PulseTableValue
[] |PulseTableValue
) - pop_back(self :
Pulse
) - repeat_n(self :
Pulse
, count :integer
) - spread(self :
Pulse
, amount :number
, empty_value :PulseTableValue``?
) - tostring(self :
Pulse
)
- new(length :
- Aliases
- Functions
Pulse
Table with helper functions to ease creating rhythmic patterns.
examples:
-- using + and * operators to combine patterns pulse.from{ 0, 1 } * 3 + { 1, 0 }
-- repeating, spreading and subsets pulse.from{ 0, 1, { 1, 1 } }:repeat_n(4):spread(1.25):take(16)
-- euclidean patterns pulse.euclidean(12, 16) pulse.from{ 1, 0.5, 1, 1 }:euclidean(12)
-- generate/init from functions pulse.new(8):init(1) --> 1,1,1,1,1,1,1,1 pulse.new(12):init(function() return math.random(0.5, 1.0) end ) pulse.new(16):init(scale("c", "minor").notes_iter())
-- generate pulses with note values pulse.from{ "c4", "g4", "a4" } * 7 + { "a4", "g4", "c4" }
-- generate chords from degree values pulse.from{ 1, 5, 6, 4 }:map(function(index, degree) return scale("c", "minor"):chord(degree) end)
Functions
new(length : integer
?
, value : PulseTableValue
| (index : integer
) ->
PulseTableValue
?
)
->
Pulse
Create a new empty pulse table or a pulse table with the given length and value.
examples:
pulse.new(4) --> {0,0,0,0} pulse.new(4, 1) --> {1,1,1,1} pulse.new(4, function() return math.random() end)
from(...PulseTableValue
| PulseTableValue
[])
->
Pulse
Create a new pulse table from an existing set of values or tables. When passing tables, those will be flattened.
examples:
pulse.from(1,0,1,0) --> {1,0,1,0} pulse.from({1,0},{1,0}) --> {1,0,1,0}
copy(self : Pulse
)
->
Pulse
create a shallow-copy of the given pulse table (or self)
examples:
local p = pulse.from(1, 0) local p2 = p:copy() --> {1,0}
distributed(steps : integer
| table
, length : integer
, offset : integer
?
, empty_value : PulseTableValue
?
)
->
Pulse
Create an new pulse table or spread and existing pulse evenly within the given length. Similar, but not exactly like
euclidean
.Shortcut for
pulse.from{1,1,1}:spread(length / #self):rotate(offset)
examples:
pulse.distributed(3, 8) --> {1,0,0,1,0,1,0} pulse.from{1,1}:distributed(4, 1) --> {0,1,0,1}
euclidean(steps : integer
| table
, length : integer
, offset : integer
?
, empty_value : PulseTableValue
?
)
->
Pulse
Create a new euclidean rhythm pulse table with the given pulses or number of new pulses in the given length. Optionally rotate the contents too. Euclidean Rhythm
examples:
pulse.euclidean(3, 8) --> {1,0,0,1,0,0,1,0} pulse.from{"x", "x", "x"}:euclidean(8, 0, "-") --> {"x","-","-","x","-","-","x","-"}
unpack(self : Pulse
)
->
... : PulseTableValue
Shortcut for table.unpack(pulse): returns elements from this pulse as var args.
examples:
local p = pulse.from{1,2,3,4} local v1, v2, v3, v4 = p:unpack()
subrange(self : Pulse
, i : integer
, j : integer
?
, empty_value : PulseTableValue
?
)
->
Pulse
Fetch a sub-range from the pulse table as new pulse table. When the given length is past end of this pulse it is filled up with empty values.
examples:
local p = pulse.from{1,2,3,4} p = p:subrange(2,3) --> {2,3} p = p:subrange(1,4,"X") --> {2,3,"X","X"}
take(self : Pulse
, length : integer
, empty_value : PulseTableValue
?
)
->
Pulse
Get first n items from the pulse as new pulse table. When the given length is past end of this pulse its filled up with empty values.
examples:
local p = pulse.from{1,2,3,4} p = p:take(2) --> {1,2} p = p:take(4, "") --> {1,2,"",""}
clear(self : Pulse
)
->
Pulse
Clear a pulse table, remove all its contents.
examples:
local p = pulse.from{1,0} p:clear() --> {}
init(self : Pulse
, value : PulseTableValue
| (index : integer
) ->
PulseTableValue
, length : integer
?
)
->
Pulse
Fill pulse table with the given value or generator function in the given length.
examples:
local p = pulse.from{0,0} p:init(1) --> {1,1} p:init("X", 3) --> {"X","X", "X"} p:init(function(i) return math.random() end, 3)
map(self : Pulse
, fun : (index : integer
, value : PulseTableValue
) ->
PulseTableValue
)
->
Pulse
Apply the given function to every item in the pulse table.
examples:
local p = pulse.from{1,3,5} p:map(function(k, v) return scale("c", "minor"):degree(v) end) --> {48, 51, 55}
reverse(self : Pulse
)
->
Pulse
Invert the order of items in the pulse table.
examples:
local p = pulse.from{1,2,3} p:reverse() --> {3,2,1}
rotate(self : Pulse
, amount : integer
)
->
Pulse
Shift contents by the given amount to the left (negative amount) or right.
examples:
local p = pulse.from{1,0,0} p:rotate(1) --> {0,1,0} p:rotate(-2) --> {0,0,1}
push_back(self : Pulse
, ...PulseTableValue
[] | PulseTableValue
)
->
Pulse
Push a single or multiple items or other pulse contents to the end of the pulse. Note: When passing array alike tables or patterns, they will be unpacked.
examples:
local p = pulse.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 : Pulse
)
Remove an entry from the back of the pulse table. returns the removed item.
examples:
local p = pulse.from({1,2}) p:pop_back() --> {1} p:pop_back() --> {} p:pop_back() --> {}
repeat_n(self : Pulse
, count : integer
)
->
Pulse
Repeat contents of the pulse table n times.
examples:
local p = pulse.from{1,2,3} patterns:repeat_n(2) --> {1,2,3,1,2,3}
spread(self : Pulse
, amount : number
, empty_value : PulseTableValue
?
)
->
Pulse
Expand (with amount > 1) or shrink (amount < 1) the length of the pulse table by the given factor, spreading allowed content evenly and filling gaps with 0 or the given empty value.
examples:
local p = pulse.from{1,1} p:spread(2) --> {1,0,1,0} p:spread(1/2) --> {1,1}
tostring(self : Pulse
)
->
string
Serialze a pulse table for display/debugging purposes.
examples:
pulse.euclidean(3, 8):tostring() --> "{1, 0, 0, 1, 0, 0, 1, 0}"
Aliases
PulseTableValue
boolean
| string
| number
| table
Valid pulse value in a pulse table
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 -> {48, 50, 51, 53, 55, 56, 58}
-- 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"
| "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" | "I" | "II" | "III" | "IV" | "V" | "VI" | "VII"
NoteValue
string
| number
| Note
| 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" | "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 index value. 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" | "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 `pulse` library local notes = pulse.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"
| "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" | "I" | "II" | "III" | "IV" | "V" | "VI" | "VII"
NoteValue
string
| number
| Note
| NoteTable
| nil
sequence
Global
Functions
sequence(...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 event 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
| Note
| NoteTable
| nil
Sequence
Properties
notes : NoteTable
[][]
Functions
transpose(self, step : integer
| integer
[])
->
Sequence
Transpose all note's 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 note's 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 pattern { event = function(init_context) -- use a unique random sequence every time the pattern gets (re)triggered local rand = math.randomstate(12345) return function(context) if rand(1, 10) > 5 then return "c5" else return "g4" end end }
table
Functions
new()
->
table
| tablelib
Create a new empty table that uses the global 'table.XXX' functions as methods, just like strings in Lua do. See also
table.from
.examples:
t = table.new(); t:insert("a"); print(t[1]) -> "a";
from(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.from{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
Serialize 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.