Skip to content

Commit

Permalink
Doc updates (#43)
Browse files Browse the repository at this point in the history
* always return rhythms in examples for consistency

* fix typos and cleaned up some misleading wording in the docs

* prefix unused function context args with _

* document expected return values of dynamic generators

* refer to parameters as input parameters
  • Loading branch information
emuell authored Dec 13, 2024
1 parent 85c60ec commit a676ace
Show file tree
Hide file tree
Showing 23 changed files with 122 additions and 122 deletions.
Binary file modified README.md
Binary file not shown.
10 changes: 5 additions & 5 deletions docs/src/API/cycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,12 @@
>
> #### examples:
> ```lua
> --Using a fixed mapping table
> --Using a static map table
> cycle("bd [bd, sn]"):map({
> bd = "c4",
> sn = "e4 #1 v0.2"
> })
> --Using a fixed mapping table with targets
> --Using a static map table with targets
> cycle("bd:1 <bd:5, bd:7>"):map({
> bd = { key = "c4", volume = 0.5 }, -- instrument #1,5,7 will be set as specified
> })
Expand All @@ -94,7 +94,7 @@
> return math.random(0, 11) + value * 12
> end)
> --Using a dynamic map function generator
> cycle("4 5 4 <4 [5|7]>"):map(function(context)
> 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
Expand All @@ -104,9 +104,9 @@
> end
> end)
> --Using a dynamic map function to map values to chord degrees
> cycle("1 5 1 [6|7]"):map(function(context)
> cycle("1 5 1 [6|7]"):map(function(_init_context)
> local cmin = scale("c", "minor")
> return function(context, value)
> return function(_context, value)
> return note(cmin:chord(tonumber(value)))
> end
> end)
Expand Down
4 changes: 2 additions & 2 deletions docs/src/API/modules/math.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@
>
> ```lua
> return rhythm {
> emit = function(context)
> emit = function(_init_context)
> -- use a unique random sequence every time the rhythm gets (re)triggered
> local rand = math.randomstate(12345)
> return function(context)
> return function(_context)
> if rand(1, 10) > 5 then
> return "c5"
> else
Expand Down
3 changes: 2 additions & 1 deletion docs/src/API/modules/table.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
`->`key_or_nil : [`any`](../../API/builtins/any.md)
> Find first match of *value* in the given table, starting from element
> number *start_index*.<br>
> number *start_index*.
>
> Returns the first *key* that matches the value or nil
>
> #### examples:
Expand Down
30 changes: 14 additions & 16 deletions docs/src/API/rhythm.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@
> pattern = function(context)
> return (context.pulse_step % 4 == 1) or (math.random() > 0.8)
> end,
> emit = function(context)
> emit = function(_init_context)
> local cmin = scale("c5", "pentatonic minor").notes
> return function(context)
> return function(_context)
> return { key = cmin[math.random(#cmin)], volume = 0.7 }
> end
> end
Expand Down Expand Up @@ -148,29 +148,28 @@
>
> When no pattern is defined, a constant pulse of `1` is triggered by the rhythm.
>
> Just like the `emitter` property, patterns can either be a fixed array of values or a
> function or iterator which produces values dynamically.
> Just like the `emitter` property, patterns can either be a static array of values
> or a function or generators which produces values dynamically.
>
> #### examples:
> ```lua
> -- a fixed pattern
> -- static pattern
> pattern = { 1, 0, 0, 1 }
> -- "cram" pulses into a single pulse slot via subdivisions
> pattern = { 1, { 1, 1, 1 } }
>
> -- fixed patterns created via the "patterns" lib
> -- patterns created via the "patterns" lib
> pattern = pattern.from{ 1, 0 } * 3 + { 1, 1 }
> pattern = pattern.euclidean(7, 16, 2)
>
> -- stateless generator function
> pattern = function(context)
> -- stateless pattern function
> pattern = function(_context)
> return math.random(0, 1)
> end
>
> -- stateful generator function
> pattern = function(context)
> pattern = function(_init_context)
> local triggers = table.create({0, 6, 10})
> ---@param context PatternContext
> return function(context)
> return triggers:find((context.step - 1) % 16) ~= nil
> end
Expand Down Expand Up @@ -215,10 +214,10 @@
### emit : [`Cycle`](../API/cycle.md#Cycle) | [`Sequence`](../API/sequence.md#Sequence) | [`Note`](../API/note.md#Note) | [`NoteValue`](#NoteValue) | [`Note`](../API/note.md#Note) | [`NoteValue`](#NoteValue)[] | (context : [`EmitterContext`](../API/rhythm.md#EmitterContext)) `->` [`NoteValue`](#NoteValue) | (context : [`EmitterContext`](../API/rhythm.md#EmitterContext)) `->` (context : [`EmitterContext`](../API/rhythm.md#EmitterContext)) `->` [`NoteValue`](#NoteValue) {#emit}
> 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.<br>
> the beginning.
>
> To generate notes dynamically, you can pass a function or a function iterator, instead of a
> fixed array or sequence of notes.<br>
> 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
Expand All @@ -234,15 +233,14 @@
> emit = sequence{"c4", "g4"}:volume(0.5)
>
> -- stateless generator function
> emit = function(context)
> emit = function(_context)
> return 48 + math.random(1, 4) * 5
> end
>
> -- stateful generator function
> emit = function(initial_context)
> emit = function(_init_context)
> local count, step, notes = 1, 2, scale("c5", "minor").notes
> ---@param context EmitterContext
> return function(context)
> return function(_context)
> local key = notes[count]
> count = (count + step - 1) % #notes + 1
> return { key = key, volume = 0.5 }
Expand Down
2 changes: 1 addition & 1 deletion docs/src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

***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](https://github.com/pjagielski/awesome-live-coding-music).
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](https://github.com/pjagielski/awesome-live-coding-music).

In addition to its imperative event generator approach, it also supports the creation of musical events using [tidalcycle](https://tidalcycles.org/)'s mini-notation.

Expand Down
8 changes: 4 additions & 4 deletions docs/src/extras/generators.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[Patterns](../guide/pattern.md), [Gates](../guide/gate.md) and [Emitters](../guide/emitter.md) can use Lua functions to dynamically generate or evaluate content.

Annonymous Lua functions, as used in rhythms, are actually [closures](https://www.lua.org/pil/6.1.html). They keep a record of their environment, so all (up)values which are declared outside of the annonymous function are accessible from within the function itself.
Anonymous Lua functions, as used in rhythms, are actually [closures](https://www.lua.org/pil/6.1.html). 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.

Expand All @@ -22,7 +22,7 @@ In the following example, an emitter function keeps track of its state by refere
```lua
local counter = 0
return rhythm {
emit = function(context)
emit = function(_context)
local midi_note = counter
counter = (counter + 1) % 128
return note(midi_note)
Expand All @@ -40,7 +40,7 @@ A `context` passed to *pattern* functions only contains the global playback stat

See [pattern context API](../API/rhythm.md#PatternContext), [gate context API](../API/rhythm.md#GateContext), [emitter context API](../API/rhythm.md#EmitterContext) for details.

Contexts also may contain user controlled input variables. See [parameters](../guide/parameters.md) for more info about this.
Contexts also may contain user controlled input variables. See [input parameters](../guide/parameters.md) for more info about this.

By making use of the context we can now rewrite the example above to:

Expand All @@ -65,7 +65,7 @@ Let's use our counter example again with such a *generator*:

```lua
return rhythm {
emit = function(_initial_context)
emit = function(_init_context)
local counter = 0 -- local state!
return function(_context)
local midi_note = counter
Expand Down
8 changes: 4 additions & 4 deletions docs/src/extras/randomization.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ local random_notes = pattern.new(10, function()
return cmin.notes[math.random(#cmin.notes)]
end)

-- play notes
return rhythm {
emit = random_notes
}
Expand All @@ -32,7 +31,8 @@ You can use `math.randomseed()` to seed the global random number generator.
```lua
-- create a scale to pick notes from
local cmin = scale("c", "minor")
-- pick **the same** random 10 notes from the scale every time

-- 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)]
Expand All @@ -54,9 +54,9 @@ To create multiple separate local random states, use the non standard [`math.ran
```lua
local cmin = scale("c", "minor")
return rhythm {
emit = function(context)
emit = function(_init_context)
local rand = math.randomstate(1234) -- a local random number generator
return function(context)
return function(_context)
return note(cmin.notes[rand(#cmin.notes)])
end
end
Expand Down
26 changes: 13 additions & 13 deletions docs/src/guide/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The Lua API uses configuration tables to define the rhythm and their sub-compone

- [TimeBase](./timebase.md) defines the time unit of a rhythm.
- [Pattern](./pattern.md) -> [Gate](./gate.md) -> [Emitter](./emitter.md) do perform the basic event generation in 3 stages.
- [Parameters](./parameters.md) change behaviour of all components during runtime.
- [Parameters](./parameters.md) change behaviour of components during runtime.

```md
*Inputs*
Expand Down Expand Up @@ -40,11 +40,11 @@ The Lua API uses configuration tables to define the rhythm and their sub-compone
*Event Stream*
```

By separating the **rhythmic** from the **tonal** or parameter value part of a musical sequence, each part of the sequence can be freely modified, composed and (re)combined. We're basically treating music in two dimensions here: the *rhythmic* part as one dimension, and the *tonal* part as another.
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 fixed -> e.g. a Lua table of events, or dynamic -> a Lua function that [generates](../extras/generators.md) events on the fly.
All content in rhythms can be either static, a Lua table of events, or dynamic, a Lua function that [generates](../extras/generators.md) events on the fly.

All dynamic functions or generators can also be controlled, automated via [parameters](./parameters.md) 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.
Dynamic functions or generators can also be controlled, automated via [input parameters](./parameters.md) 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
Expand All @@ -53,7 +53,7 @@ A simple rhythm with a static pattern and emitter, using the default gate implem

```lua
-- sequence of 1/4th c4 and two 1/8 c5 notes.
rhythm {
return rhythm {
unit = "1/4",
pattern = { 1, { 1, 1 } },
emit = { "c4", "c5", "c5" }
Expand All @@ -64,7 +64,7 @@ A rhythm with default pattern and gate, emitting a Tidal Cycle.

```lua
-- emit a tidal cycle every bar
rhythm {
return rhythm {
unit = "1/1",
emit = cycle("a4 e4@2 <c4 c5>")
}
Expand All @@ -74,10 +74,10 @@ A rhythm, using a Lua function as dynamic pattern generator.

```lua
-- maybe trigger a c4 on every 2nd 1/4.
rhythm {
return rhythm {
unit = "1/4",
pattern = function (context)
if (context.pulse_step % 2 == 1) then
pattern = function(context)
if context.pulse_step % 2 == 1 then
return math.random() > 0.5 and 1 or 0
else
return 1
Expand All @@ -99,19 +99,19 @@ return rhythm {

pattern = { 1, { 1, 0.1, 0.5 }, 1, { 1, 0, 0, 0.5 } },

gate = function (context)
gate = function(_init_context)
-- create a local random number generator for the probability
local rand = math.randomstate(seed)
return function (context)
return function(context)
-- use pulse value as trigger probability
return context.pulse_value >= rand()
end
end,

emit = function (context)
emit = function(_init_context)
-- create a local random number generator for the humanizing delay
local rand = math.randomstate(seed)
return function (context)
return function(context)
local volume, delay = 1.0, 0.0
if context.pulse_time < 1 then
-- lower volume and add a delay for events in sub patterns
Expand Down
16 changes: 8 additions & 8 deletions docs/src/guide/cycles.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ There's no exact specification for how tidal cycles work, and it's constantly ev
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*.

```lua
-- emits an entire cycle every *bar*
rhythm {
-- emits an entire cycle every bar
return rhythm {
unit = "bars",
emit = cycle("c d e f")
}
Expand All @@ -41,8 +41,8 @@ rhythm {
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.

```lua
-- emit an entire cycle's every *bar*, then pause for a bar, then repeat
rhythm {
-- 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")
Expand All @@ -53,7 +53,7 @@ You can also use the mini notation to emit single notes only, making use of tida

```lua
-- emit a single note from a cycle in an euclidean pattern
rhythm {
return rhythm {
unit = "beats",
pattern = pattern.euclidean(5, 8),
emit = cycle("<c d e f g|b>")
Expand All @@ -69,12 +69,12 @@ afseq's general random number generator is also used in cycles. So when you seed

Notes and chords in cycles are expressed as [note strings](./notes&scales.md#note-strings) in afseq. But you can also dynamically evaluate and map cycle identifiers using the cycle [`map`](../API/cycle.md#map) function.

This allows you, for example, to inject [parameters](./parameters.md) into cycles or to use custom identifiers.
This allows you, for example, to inject [input parameters](./parameters.md) into cycles or to use custom identifiers.

Using custom identifiers with a static map (a Lua table):

```lua
rhythm {
return rhythm {
unit = "bars",
emit = cycle("[bd*4], [_ sn]*2"):map({
["bd"] = note("c4 #0"),
Expand All @@ -86,7 +86,7 @@ rhythm {
Using custom identifiers with a dynamic map function (a Lua function):

```lua
rhythm {
return rhythm {
unit = "bars",
emit = cycle("[bd*4], [_ sn]*2"):map(function(context, value)
if value == "bd" then
Expand Down
Loading

0 comments on commit a676ace

Please sign in to comment.