jaseknighter / flora

L-systems sequencer and bandpass filtered sawtooth engine for monome norns

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Flora

An L-systems sequencer and bandpass-filtered sawtooth engine for monome norns

Demonstration video: https://vimeo.com/496481575
Follow the discussion on lines: https://llllllll.co/t/40261

Documentation

Overview

L-systems and their sequencing

L-system basics

An L-system is a parallel rewriting mechanism originally conceived by Aristid Lindenmayer in 1968 as a mathematical model of plant development.

The basic building blocks of most L-systems include:

  • Turtle graphics engine: First developed for the Logo programming language, a turtle creates a drawing from instructions that dictate when to move forward and draw and when to rotate to point in a different direction.
  • Alphabet: A set of characters, each representing an instruction for an L-system algorithm to interpret (e.g. rotate, move forward, turn around, draw a line, etc.).
  • Axiom: A sentence containing one or more characters that represents the starting point of an L-system algorithm.
  • Rulesets: Each ruleset of an L-system contains two strings. The first string, the predecessor, typically contains a single character. The second string, the successor, contains one or more character. Each time the algorithm runs, if the character contained in the first string of the ruleset is encountered, it will replace that character with the character(s) of the second string.
  • Angle: An angle used by the turtle to rotate clockwise or counterclockwise, giving it a new direction to move the next time it receives an instruction to draw a line.
  • Generations: A generation represents a completed execution of the L-system algorithm.

Simple rewriting example

Take the following:

  • Axiom: b
  • Ruleset 1: b->a
  • Ruleset 2: a->ab

The above axiom and rulesets will result in the following sentences when run six times, starting with the axiom b as Generation 0. Ruleset 1 states that each time the character b is encountered, it is replaced with a. Ruleset 2 states that each time the character a is encountered, it replaced with ab.

  • Generation 0: b
  • Generation 1: a
  • Generation 2: ab
  • Generation 3: aba
  • Generation 4: abaab
  • Generation 5: abaababa

Sequencing the L-system

The Flora alphabet
Character Turtle Behavior Sound Behavior
F Move the turtle forward and draw a line and a circle Play current note
G Move the turtle forward and draw a line Resting note (silence)
[ Save the current position Save the current note
] Restore the last saved position Restore the last saved note
+ Rotate the turtle counterclockwise by the current angle Increase the active note's pitch (see Changes in pitch below)
- Rotate the turtle clockwise by the current angle Decrease the active note's pitch (see Changes in pitch below)
| Rotate the turtle 180 degrees No sound behavior
r Randomly increase or decrease the angle each frame Randomly increase or decrease the active note's pitch (see Changes in pitch below)
! Randomly set an angle Randomly increase or decrease the active note's pitch (see Changes in pitch below)
other Other characters are ignored by the turtle No sound behavior
Changes in pitch

Flora leverages L-systems to algorithmically generate music, in particular, by taking the angles written into the L-system sentences as indicators of an increase or decrease in pitch. The amount of change in pitch is set by the angle measured in radians multiplied by the current pitch. The changes in pitch are quantized, so if an angle multiplied by the current pitch is not greater than a whole number, the pitch stays the same.

If a change in angle results in a pitch that is greater than the number of notes in the active scale, the active note becomes the root (lowest) note of the active scale. Conversely, if a change in angle results in a pitch that is less than the root note of the active scale, the active note becomes the last (highest) note in the active scale.

Bandsaw

If the output parameter is set in norns to include audio, notes will be played using the Bandsaw engine, built around a bandpass filtered sawtooth wave generator. This engine is based on the marimba instrument demonstrated by Eli Fieldsteel in video, SuperCollider Tutorial #15: Composing a Piece, Part I.

Unlike a 'typical' oscillator, where the frequency of the oscillator is perceived as the note being played, the notes typically heard when the Bandsaw engine is played are determined by the center frequency of its bandpass filter, not the frequency of its sawtooth oscillator.

The parameters of this instrument may be set in the PARAMETERS->EDIT menu or on the water page of the Flora program (see water below for more details)

SAFETY NOTES

Safety Note #1
The SuperCollider documentation for its BandPassFilter (BPF) contains the following warning:

WARNING: due to the nature of its implementation frequency values close to 0 may cause glitches and/or extremely loud audio artifacts!

For safety purposes, the minimum note frequency value is set to 0.2 to prevent loud noises. This safety measure is implemented in both the Bandsaw engine and the Lua code for norns.

Safety Note #2
The Bandsaw engine becomes loudly percussive as the values for rqmin and rqmax increase. Please take care not to hurt your ears, especially when using headphones.

norns UI

Flora's interface consists of five screens (or "pages"). Navigation between screens occurs using Encoder 1 (E1). While the controls for each screen vary, basic instructions for each screen can always be accessed using the key combination: Key 1 (K1) + Key 2 (K2). The instructions may also be found in the lib/instructions.lua file.

For many parameters, fine-grained adjustments can be made by pressing K1 along with the encoder (see below for details.)

Screens

The first three screens of the Flora program (Plant, Modify, and Observe) display two L-system rulesets, used by the program to sequence notes. The fourth screen (Plow) displays two envelopes. The fifth screen (Water) displays controls for the Bandsaw engine and other outputs (i.e. Midi, Just Friends, and crow).

Plant

e1: next page  
k1 + e1: select active plant  
k1 + e2: replace active plant  
e3: increase/decrease angle  
k2/k3: previous/next generation  
k1 + k3: reset plants to original forms and restart their sequences

Modify

e1: next/previous page  
k1 + e1: select active plant  
e2: go to next/previous letter  
e3: change letter  
k2/k3: delete/add letter  
k1 + k3: reset plants to original forms and restart their sequences

Observe

e1: next/previous page  
k1 + e1: select active plant  
e2: move up/down  
e3: move left/right  
k2/k3: zoom out/in  
k1 + k3: reset plants to original forms and restart their sequences

Plow

e1: next/previous page 
k1 + e1: select active plant  
e2: select envelope control  
e3: change envelope control value  
k2/k3: delete/add envelope control point  

The Plow screen provides controls for two envelopes, one for each L-system ruleset sequence. An extension of Mark Eats' envgraph class, the envelopes controlled on this screen are applied to the Bandsaw engine when the envelopes' respective L-system ruleset sequence triggers a note to play.

Unlike typical envelopes (AR, AD, ADSR, etc.), the envelope class developed for this program allows for a variable number of control points or 'nodes.' The program allows for anywhere from 3-20 nodes per envelope.

There are 5 types of controls for each of the two envelopes:

env level: the maximum amplitude of the envelope
env length: the length of the envelope
node time: when the node is processed by the envelope
node level: the amplitude of the envelope at the node time
node angle: the shape of the ramp from the prior node time to the current node time

With a few exceptions, the last of the three control types (node time, node level, and node angle) are adjustable for each of envelopes nodes.

Fine grain controls: All of the envelope controls allow for fine grain control using K1+E3.

Plow modulation
e1: next/previous page 
k1+k3: show/hide plow modulation menu
k1+e1: select active plant  
k2: select control
k3: change control value

As of version 0.2.0-beta, pressing K1+K3 on the plow screen brings up the plow modulators menu, which can be navigated using E2 and E3. There are eight parameters for each of the two plants related to modulating envelopes that may be set:

mod prob: The probability that one of the other modulation parameters will be evaluated. If it is set to 0%, no envelope modulation will occur for the selected plant.
time prob: The probability that the time value for each of the envelope's nodes will be modulated.
time mod amt: The amount of modulation that will be applied to the time value of each of the envelope's nodes.
level prob: The probability that the level value for each of the envelope's nodes will be modulated.
level mod amt: The amount of modulation that will be applied to the level value of each of the envelope's nodes.
curve prob: The probability that the curve value for each of the envelope's nodes will be modulated.
curve mod amt: The amount of modulation that will be applied to the curve value of each of the envelope's nodes.
env mod nav: Selects which of the above seven parameters are selected on when plow modulation is visible (by pressing K1+K3) on the plow screen. This parameter is useful for controlling the plow ui via midi.

In addition, the show env mod params parameter makes the parameter modulation navigation visible (again, useful for controlling the ui via midi).

Water

e1: next/previous page  
e2: select control  
e3: change control value  

The water interface provides control for the output parameters:

  • (all output types) amp (fg): the overall amplitude of the outputs
  • (all output typess) p1 note dur: The length of each note for the first plant
  • (all output typess) p2 note dur: The length of each note for the second plant
  • (all output typess) note scalar (fg): This value is multiplied by the current angle of the plant, which is then added to the current note. This sets the next note to be played.
  • (Bandsaw only) cf scalars: 1-4 center frequency scalars are applied to the center frequency of the Bandsaw engine's bandpass filter to set the octave of the notes played by each plant. If more than one center frequency is activate, the active scalars are randomly selected by the Bandsaw engine each time a note is played.
  • (Bandsaw only) rqmin/rqmax (fg): These two parameters set the range of the reciprocal of the bandpass filter's Quality values.
  • (Bandsaw only) note frequencies (fg): this sets the frequency of the Bandsaw's oscillations. Values of less than ~20 will sound like individual tones. With larger values, the oscillations begin to blend into one another creating a single tone that is not related to the note set by the center frequency of the Bandsaw's bandpass filter. The third note frequency parameter of each selected note frequency allows for fine grain control.

Fine grain controls: All of the controls in the above list with the characters '(fg)' attached to the control names allow for fine grain control using K1+E3.

Note: Tempo scalar offset is a parameter that provides macro control over all active note frequencies. It is not yet available from the Water UI screen but can be adjusted from PARAMETERS->EDIT. The Tempo Scalar Offset’s default value of 1.5 can also be changed by updating the variable tempo_scalar_offset_default in the lib/globals.lua file.

Tinta and Tinta Envelope

e1: previous page  
enter commands with external keyboard

As of flora v2.0, the tinta interface provides control for the melody accompanying the plant melodies via external keyboard or the Maiden REPL and was inspired by Aarvo Pärt's method of Tintinnabuli).

Tinta makes extensive use of sequins.

Options have been added to the PARAMETERS menu controls to output tinta notes to midi, crow, jf, and w/.

as of flora v2.1, three options have been added to set the envelope used by tinta, using the new tinta env type parameter in the tinta section of flora's parameters menu:

  • ad: a kind of attack-decay envelope, using the value of tinta's vel parameter for envelope level and the active plant for the envelope's other values (time and curve)
  • plant: uses the active plant's envelope (set on the plow screen)
  • morphing: the shape of tinta's envelope is morphed between the two plant envelopes (set on the plow screen).

for the morphing envelope type, the duration and number of steps, and "shape" of the morph may be set with two new tinta commands: edu and est.

there are three "styles" of envelope morphing, set with the new tinta env morph style parameter:

  • shuttle: once a morph is completed, a new morph begins going in the opposition direction
  • loop: once a morph is completed, a new morph begins, starting with plant 1's envelope shape
  • 1-shot: once a morph is completed, morphing stops (it can be started again by setting the tinta env morph parameter to true)

Keyboard commands (using an external keyboard)

Command Description
tin set the melody
format: tin=s{1,3,5,s{4,2}}
oct shift the octave
recommended range: +/- 2
format: oct=s{0,1}
vel set the velocity of each note
recommended range: 0 - 10
format: vel=s{0,5}
rhy set the rhythm of each note
recommended range: 0.1 - 2
format: rhy=s{1,0.25}
stop stop the melody
format: stop
play play the melody
format: play
offdance don't adjust melody relative to plant melody
format: offdance
ondance adjust melody relative to plant melody
format: ondance
estart start envelope morphing
format: estart
estop stop envelope morphing
format: estop
edu set the recommendation morphing duration (in beats)
recommended range: 0.125 - 50
format: edu=s{1,10}
est set the number of steps to complete the morph (each step generates an envelope)
recommended range: 1 - 50
format: est=s{3,20}

Important note: when setting the rhythm (rhy), nested sequins will throw an error (e.g. rhy=s{1,s{0.5,0.25}}).

REPL commands (using the Maiden REPL)

Using the maiden REPL to control the tinta interface, more complex sequins functionality can be utilized such as flow-modifiers.

Tinta Command Description
tin set the melody
format: tt.tin=s{1,3,5,s{4,2}}
oct shift the octave
recommended range: +/- 2
format: tt.oct=s{0,1}
vel set the velocity of each note
recommended range: 0 - 10
format: tt.vel=s{0,5}
rhy set the rhythm of each note
recommended range: 0.1 - 2
format: tt.set_rhythm({1,0.25}). (note that when setting the rhythm with the maiden repl, a table is passed to the method tt.set_rhythm instead of setting the rhythm directly with sequins.)
edu set the envelope morphing duration (in beats)
recommended range: 0.25 - 50
format: tt.edu=s{1,10}
est set the number of steps to complete the morph (each step generates an envelope)
recommended range: 1 - 25
format: tt.est=s{3,20}

Tinta params menu

Paramter Description
tinta enabled turns the tinta melody generator on and off
dancing notes if set to on the tinta melody is set relative to the notes played by the plant forms
tinta target sets which plant to determine the tinta melody when the dancing notes parameter is set to "on"
tinta method sets how the tinta note is selected: cycle, closest, and furthest
tinta env type selects the type of envelope used by tinta: ad,plant,morph
tinta env morph starts and stops morphing when the tinta env type parameter is set to morph
tinta env morph style determins how morphing occurs: shuttle, loop,1-shot

PSET Sequencer

As of version v0.2.0-beta, a PSET sequencer has been built into Flora. This feature allows PSETS saved in the PARAMETERS->PSET menu to be sequenced. The sequencer's parameters (accessed from the PARAMETERS->EDIT menu) include:

  • pset seq enabled: Turns the sequencer on and off.
  • pset seq mode: There are three sequence modes:
    • loop: Load PSETs in order from first to last. After the last PSET has been loaded, the sequence restarts from the first saved PSET.
    • up/down: Load PSETs in order from first to last. After the last PSET has been loaded, the loading order is reversed.
    • random: PSETs are loaded in random order.
  • load pset: Manually load a preset.
  • pset seq beats: Set the number of cycles that run before a PSET is loaded. A cycle is measured by dividing pset seq beats by pset beats per bar and multiplying that number by the value of tempo which is set in PARAMETERS->CLOCK. This parameter ranges from 1-16.
  • pset beats per bar: See pset seq beats above. This parameter ranges from 1-4.
  • pset exclusions: This parameter group contains a list parameter sets that can be excluded when presets are saved. By default, there are five PSET exclusion sets:
    • plant psets: This set includes the plant instruction and plant angle parameters associated with the plant, modify, and observe screens.
    • plow psets: This set includes the main parameters associated with the plow screen.
    • plow mod psets: This set includes the envelope modulation parameters associated with the plow screen that are accessed by pressing K1+K3 from the plow screen.
    • water psets: This set includes the parameters associated with the water screen.
    • nav psets: This set includes two of the parameters associated with general navigation (page turner and active plant switcher).

Parameters that are part of an enabled exclusion set will be excluded when a PSET is saved. Accordingly, parameters in an enabled exclusion set won't be overwritten when the sequencer loads a PSET. Please note that for this feature to work, exclusion sets need to be enabled (in the PARAMETERS menu) before saving the PSETS. Enabling a PSET exclusion set while the PSET sequencer is playing will have no immediate effect.

Custom exclusion sets can be created by adding, deleting, and modifying the tables defined in the init function of the flora.lua file.

Generating new L-system axioms and rulesets

L-system instructions are found in the files lib/gardens/garden_default.lua and lib/gardens/garden_community.lua. There are eight required variables/tables for each L-system instruction set:

Variable Description
start_from the starting x/y screen coordinate (format: vector:new(<x>,<y>)
ruleset[] the l-system ruleset(s) (format: rule:new('<character>',"<character(s)")
axiom the starting sentence (format: "<character(s)>"
max_generations the maximum number of generations
length the starting length (in pixels) of the segments drawn by the turtle
angle the default turtle rotation angle (in degrees)
initial_turtle_rotation initial turtle rotation angle (in degrees) applied to the turtle prior to evaluating the ruleset
starting_generation the initial generation to display

Example instruction set :

instruction.start_from = vector:new(screen_size.x/2-10, screen_size.y - 10)
instruction.ruleset = {}
instruction.ruleset[1] = rule:new('F',"F++F++F|F-F++F")
instruction.axiom = "F++F++F++F++F++F"
instruction.max_generations = 2
instruction.length = screen_size.y/8
instruction.angle = 36
instruction.initial_turtle_rotation = 0
instruction.starting_generation = 1

Advanced sequencing

Multiple rulesets Multiple rulesets can easily be added to the instruction.ruleset table.

Example instruction set with multiple rulesets:

instruction.start_from = vector:new(screen_size.x/2, screen_size.y )
instruction.ruleset = {}
instruction.ruleset[1] = rule:new('F',"G[+F]G[-F]+F")
instruction.ruleset[2] = rule:new('G',"GG");
instruction.axiom = "F"
instruction.max_generations = 3
instruction.length = 7
instruction.angle = 30
instruction.starting_generation = 2
instruction.initial_turtle_rotation = 90

source: http://algorithmicbotany.org/papers/abop/abop-ch1.pdf (Figure 1.24(d))

Community gardening

As of Flora v0.4.0, a new community gardening feature has been enabled, which leverages norns.online to allow custom plant shapes (i.e. sequences) to be shared through the norns' UI.

To take advantage of the new community gardening feature, install Flora v0.4.0 or later as well as the norns.online script.

Once Flora v0.4.0 and norns.online have been installed, the community gardening features may be accessed from the Gardening section at the bottom of the PARAMATERS>EDIT menu as follows:

Steps to share a plant shape with the community

  1. Save plant to nursery: locally saves the active plant shape to the norns' filesystem (in data/flora/nursery/)
  2. Enter the Community gardening submenu
  3. Select refresh directory
  4. Select upload from nursery and choose a plant
  5. Choose the plant you saved to your nursery in step 1 above

Steps to obtain a plant shape from the community garden

  1. Enter the Community gardening submenu
  2. Select refresh directory
  3. Select download to nursery and choose a plant
  4. Exit the Community gardening submenu
  5. Select Add plant to garden and choose the plant you downloaded from the community gardens

** Note, selecting a plant to be added to your local 'garden' will append it to the end of the list of selectable plants found on the plant screen. On this screen, use K1+E2 to select the plant. By default there are 11 plants, so the first plant added to the garden will be shown as plant i12, the second plant will be shown as plant i13, and so on.

Additional gardening features

  • Remove plant from nursery: locally removes a selected plant shape previously saved to the norns' filesystem. ** Note, this cannot be undone as the plant definition is removed from the local filesystem.
  • Remove plant from garden: removes a plant from the garden ** Note, removing a plant from the garden does not remove it from the nursery (that is, the plant definition remains on the local filesystem).

W Integration

Overview

As of v0.4.0, Flora provides i2c integration with Whimsical Raps' W/2 eurorack module via crow. All three modes, W/Tape, W/Syn, and W/Del, are supported. See the W/2 documentation on the lines forum about how each mode functions.

Flora's integration with W/2 is accessed via the PARAMETERS>EDIT menu. Prior to accessing the parameters for a particular mode, W/2 must first be put into the proper mode.

W Syn sequencing

W/Syn can be sequenced with Flora via i2c by setting the wsyn output parameter in the parameters>edit>w/syn menu.

Karplus-Strong sequencing

W/Del supports Karplus-Strong style string synthesis, which can be sequenced with Flora via i2c by setting the Karplus-Strong parameter in the parameters>edit>w/del menu.

Integration with other norns scripts

Flora's code to integrate with W/2 may be relatively easily dropped into another norns script:

  1. Install Flora v0.4.0 or later
  2. At the start of the norns script add the following two lines of code:
cs = require 'controlspec'
w_slash = include("flora/lib/w_slash")
  1. At the end of the script's init function add the following six lines of code:
  params:add_group("w/del",15)
  w_slash.wdel_add_params()
  params:add_group("w/syn",14)
  w_slash.wsyn_add_params()
  params:add_group("w/tape",17)
  w_slash.wtape_add_params()

Note, enabling sequencing with w/Syn and W/Del in another script requires additional code (see Flora's plant_sounds_externals.lua file for details).

Requirements

  • norns (required)
  • norns.online (required for community gardening)
  • crow (optional)
  • W/2 (optional)
  • Just Friends (optional)
  • Midi (optional)
  • Computer to create/update rulesets using Maiden (optional)

Preliminary Roadmap

  • (Done) Save modified l-system algorithms.
  • (Done) Community gardening: investigate using norns.online to share plant forms.
  • (Done) Improve outputs selection in params menu.
  • (Done) Fix intermittent plant freezing bug.
  • Support sending sysex messages to other midi controllers in addition to the 16n faderbank (currently supported)
  • Improve the quality and portability of the code.
  • Improve the documentation.
  • Utilize crow inputs.
  • (Done) Add option for crow outputs to send triggers and gates in addition to envelopes.
  • (Done) Add support for w/syn, w/tape, and w/del.
  • Create a detailed video walkthrough for the script.
  • Add microtonal scales.
  • Increase and decrease the brightness of the circles that appear when each note plays according to the level of the note's graph/envelope.

Credits

  • Flora's L-system code is based on the code in Chapter 8.6 of Daniel Shiffman's The Nature of Code.
  • Many of the specific L-system algorithms are based on code from Paul Bourke's L-System User Notes.
  • Bandsaw, the bandpass-filtered sawtooth engine is based on SuperCollider code for a marimba presented by Eli Fieldsteel in his SuperCollider Tutorial #15: Composing a Piece, Part I.
  • @schollz and @linusschrab for their kindness and assistance setting up and testing norns.online.
  • The code for this project was also deeply inspired by the following members of the lines community: Brian Crabtree (@tehn), Dan Derks (@dan_derks), Mark Wheeler (@markwheeler), Tom Armitage (@infovore), and Tyler Etters (@tyleretters).

References

About

L-systems sequencer and bandpass filtered sawtooth engine for monome norns

License:GNU General Public License v3.0


Languages

Language:Lua 97.1%Language:SuperCollider 2.9%