Floe Library Format

Overview

Floe sample libraries are plain, open folders of audio files (FLAC or WAV). They are accompanied by a floe.lua file that describes how the samples are mapped and configured.

It is a new format, along the same lines as SFZ or DecentSampler but focusing on ease-of-use and extensibility, and bringing the power of a full programming language to ease developing complicated library configurations.

📂FrozenPlain - Slow/
├── 📄slow.floe.lua
├── 📄Licence.html
├── 📄About Slow.html
├── 📁Samples/
│   ├── 📄synth_sustain_c4.flac
│   └── 📄synth_sustain_d4.flac
└── 📁Images/
    ├── 📄background.png
    └── 📄icon.png

Developer documentation

Sample libraries are configured using a file written in the Lua programming language1 (version 5.4).

Let’s start with a simple example of a floe.lua file:

local library = floe.new_library({
    name = "Iron Vibrations",
    tagline = "Organic sounds from resonating metal objects",
    url = "https://example.com/iron-vibrations",
    description = "A collection of resonating metal objects sampled using a handheld stereo recorder.",
    author = "Found-sound Labs",
    minor_version = 1,
    background_image_path = "images/background.jpg",
    icon_image_path = "images/icon.png",
})

local instrument = floe.new_instrument(library, {
    name = "Metal Fence Strike",
    folders = "Fences/Steel",
    description = "Tonal pluck metallic pluck made from striking a steel fence.",
    tags = { "Pluck", "Metallic", "Organic" },
    waveform_audio_path = "samples/file1.flac",
})

floe.add_region(instrument, {
    file = {
        path = "One-shots/Resonating String.flac",
        root_key = 60,
        loop = { 24, 6600, 100, false },
    },
    trigger_criteria = {
        trigger_event = "note-on",
        key_range = { 60, 64 },
        velocity_range = { 0, 100 },
        round_robin_index = 0,
    },
    options = {
        timbre_crossfade_region = { 0, 50 },
        auto_map_key_range_group = "group1",
        feather_overlapping_velocity_regions = false,
    },
})

floe.add_ir(library, {
    name = "Cathedral",
    path = "irs/cathedral.flac",
})

return library

Floe automatically scans for Lua files in its sample library folders (these are configurable in the settings). It looks for files called floe.lua, or files ending with .floe.lua; for example, woodwind-textures.floe.lua. Floe automatically detects when files are added, removed or changed, and will immediately apply the changes.

Floe provides an API (a set of functions) to floe.lua files. This API is available under a table called floe, for example, floe.new_instrument(...). It features functions for creating a library, creating instruments, adding regions and impulse responses. At the end of your floe.lua file, you return the library object created with floe.new_library(...).

A floe.lua file is only concerned with mapping and configuring audio-files. Sound-shaping parameters such as envelopes, filters and effects are offered by-default on Floe’s GUI.

In other sample library formats such as SFZ or Kontakt, regions are arranged into ‘groups’. Floe does not have this concept. Instead, the features of the Lua programming language (such as functions or loops) can be used to apply similar configuration to a set of regions. Additionally, Floe offers a function called floe.extend_table(base_table, table), which allows new tables to be created that are based off an existing table.

Additionally, a floe.lua file has access to some of Lua’s standard libraries: math, string, table, utf8. The other standard libraries are not accessible to Lua - including the require function. This is to minimise security risks.

Core Functions

floe.new_library

Creates a new library. It takes one parameter: a table of configuration and returns a new library object. You should only call this once.

local library = floe.new_library({
    -- The name of the library. [required]
    name = "Iron Vibrations",

    -- A few words to describe the library. [required]
    tagline = "Organic sounds from resonating metal objects",

    -- The URL associated with the library.
    -- [optional, default: no url]
    url = "https://example.com/iron-vibrations",

    -- A description of the library.
    -- [optional, default: no description]
    description = "A collection of resonating metal objects sampled using a handheld stereo recorder.",

    -- The name of the creator of this library. [required]
    author = "Found-sound Labs",

    -- The minor version of this library - backwards-compatible changes are 
    -- allowed on a library; this field represents that. Non-backwards-compatibile 
    -- changes are not allowed: you'd need to create a new library such as: 
    -- "Strings 2".
    -- [optional, default: 1]
    minor_version = 1,

    -- Path relative to this script for the background image. It should be a jpg 
    -- or png.
    -- [optional, default: ]
    background_image_path = "images/background.jpg",

    -- Path relative to this script for the icon image. It should be a square jpg 
    -- or png.
    -- [optional, default: ]
    icon_image_path = "images/icon.png",
})

The library is the top-level object. It contains all the instruments, regions, and impulse responses.

floe.new_instrument

Creates a new instrument on the library. It takes 2 parameters: the library object and a table of configuration. It returns a new instrument object. You can call this function multiple times to create multiple instruments.

local instrument = floe.new_instrument(library, {
    -- The name of the instrument. Must be unique. [required]
    name = "Metal Fence Strike",

    -- Words separated by slashes used to hierarchically categorise the 
    -- instrument.
    -- [optional, default: no folders]
    folders = "Fences/Steel",

    -- A description of the instrument.
    -- [optional, default: no description]
    description = "Tonal pluck metallic pluck made from striking a steel fence.",

    -- An array of strings to denote properties of the instrument.
    -- [optional, default: no tags]
    tags = { "Pluck", "Metallic", "Organic" },

    -- Path to an audio file relative to this script that should be used as the 
    -- waveform on Floe's GUI.
    -- [optional, default: first region path]
    waveform_audio_path = "samples/file1.flac",
})

An instrument is like a musical instrument. It is a sound-producing entity that consists of one or more samples (samples are specified in regions). Each library can have multiple instruments.

floe.add_region

Adds a region to an instrument. It takes 2 parameters: the instrument object and a table of configuration. You can call this function multiple times to create multiple regions. Doesn’t return anything.

floe.add_region(instrument, {
    -- The file for this region. [required]
    file = {
        -- A path to an audio file, relative to this current lua file. [required]
        path = "One-shots/Resonating String.flac",

        -- The pitch of the audio file as a number from 0 to 127 (a MIDI note 
        -- number). On a range from 0 to 127. [required]
        root_key = 60,

        -- The region of the file that can be looped. It should be an array: 3 
        -- integers and 1 boolean: { start, end, crossfade, is_ping_pong boolean 
        -- }. Note that the end number is not inclusive. The start and end numbers 
        -- can be negative meaning they index the file from the end rather than 
        -- the start. For example, -1 == number_frames_in_file, -2 == 
        -- (number_frames_in_file - 1), etc.
        -- [optional, default: no loop]
        loop = { 24, 6600, 100, false },
    },

    -- How this region should be triggered.
    -- [optional, default: defaults]
    trigger_criteria = {
        -- What event triggers this region. Must be one of: "note-on" or 
        -- "note-off".
        -- [optional, default: note-on]
        trigger_event = "note-on",

        -- The pitch range of the keyboard that this region is mapped to. These 
        -- should be MIDI note numbers, from 0 to 127. Note that the end number is 
        -- not inclusive.
        -- [optional, default: { 60, 64 }]
        key_range = { 60, 64 },

        -- The velocity range of the keyboard that this region is mapped to. This 
        -- should be an array of 2 numbers ranging from 0 to 100. The first number 
        -- represents the start of the velocity range and the second number 
        -- represents 1-past the end of the range.
        -- [optional, default: { 0, 100 }]
        velocity_range = { 0, 100 },

        -- Trigger this region only on this round-robin index. For example, if 
        -- this index is 0 and there are 2 other groups with round-robin indices 
        -- of 1 and 2, then this region will trigger on every third press of a key 
        -- only.
        -- [optional, default: no round-robin]
        round_robin_index = 0,
    },

    -- Additional options for this region.
    -- [optional, default: defaults]
    options = {
        -- The start and end point, from 0 to 100, of the Timbre knob on Floe's 
        -- GUI that this region should be heard. You should overlay this range 
        -- with other timbre_crossfade_regions. Floe will create an even crossfade 
        -- of all overlapping sounds. Note that the end number is not inclusive.
        -- [optional, default: no timbre-crossfade]
        timbre_crossfade_region = { 0, 50 },

        -- For every region that matches this group, automatically set the start 
        -- and end values for each region's key range based on its root key. Only 
        -- works if all region's velocity range are the same.
        -- [optional, default: no auto-map]
        auto_map_key_range_group = "group1",

        -- If another region is triggered at the same time as this one and is 
        -- overlapping this, then both regions will play crossfaded together. This 
        -- smooths the transitions between velocity layers.
        -- [optional, default: false]
        feather_overlapping_velocity_regions = false,
    },
})

A region is a part of an instrument. It defines an audio file and the conditions under which it will be played. For example, you might have a region that plays the audio file Piano_C3.flac when the note C3 is played. Each instrument must have one or more regions.

floe.add_ir

Adds an reverb impulse response to the library. It takes 2 parameters: the library object and a table of configuration. You can call this function multiple times to create multiple impulse responses. Doesn’t return anything.

floe.add_ir(library, {
    -- The name of the IR. Must be unique. [required]
    name = "Cathedral",

    -- File path to the impulse response file, relative to this script. [required]
    path = "irs/cathedral.flac",
})

Support Functions

floe.extend_table

Extends a table with another table, including all sub-tables. It takes 2 parameters: the base table and the table to extend it with. The base table is not modified. The extension table is modified and returned. It has all the keys of the base table plus all the keys of the extended table. If a key exists in both tables, the value from the extension table is used.


local group1 = {
    trigger_criteria = {
        trigger_event = "note-on",
        velocity_range = { 0, 100 },
    },
    options = {
        auto_map_key_range_group = "group1",
        feather_overlapping_velocity_regions = false,
    },
}

floe.add_region(instrument, floe.extend_table(group1, {
    file = {
        path = "One-shots/Resonating String 2.flac",
        root_key = 65,
    },
}))

floe.add_region(instrument, floe.extend_table(group1, {
    file = {
        path = "One-shots/Resonating String 3.flac",
        root_key = 68,
    },
}))
1

Floe also supports libraries in the MDATA format, but this is only for backwards compatibility with Mirage.