TuningFork.Sfz (TuningFork v0.1.7)

Copy Markdown View Source

SFZ instruments: a text map of recordings by key, velocity and round robin, read into a pitched TuningFork.Sample.Bank the kit plays by name, with the recordings fetched one by one as notes ask for them.

{:ok, "fingerbass", 39} = TuningFork.Sfz.load("fingerbass")
TuningFork.Kit.voice(%{s: "fingerbass", note: "e2"}, 0.5)

instruments/0 is the library's own list — free instruments on GitHub, each with its licence and who to credit — plus whatever the application adds under the :sfz key of the :tuning_fork environment in the same shape; load/2 takes one of those names, a github: source, a URL or a local path. The kit loads a listed name itself the first time a pattern plays it, so s("fingerbass") in Strudel needs nothing more.

config :tuning_fork, sfz: %{"ours" => %{source: "priv/sfz/ours.sfz", licence: "CC0 1.0", credit: "us", what: "a bell"}}

What is read of the format: <control>, <global>, <master>, <group> and <region> headers, opcodes on any line in any number, values with spaces, // comments, #define and #include, default_path, sample, key, lokey, hikey, pitch_keycenter, lovel, hivel, tune, transpose, volume, loop_mode, loop_start, loop_end, trigger, sw_default, sw_last and locc. A bank takes one velocity layer (the regions whose range holds :velocity, default 100), every round robin of a note as one of its files, and leaves out release triggers, regions that need a controller raised (a pedal, a keyswitch away from the default) and, with :keys, regions outside a range. Envelopes, filters, modulation and controllers are not read.

Summary

Functions

The bank a parsed file maps: each recorded pitch, as a MIDI number that may be fractional, to its files in order of round robin, each {file, opts} with :loop and :gain as TuningFork.Sample.Bank.put/3 takes them. Files are resolved against :base, the file's own path or URL, and default_path.

Whether name is one of instruments/0.

The instruments known by name, the library's and the application's: each a :source for load/2, its :licence, who to :credit and :what it is.

Register an instrument with the bank: one of instruments/0 by name, or a source as url/1 takes it, or a local path. The file and its includes are read now; the recordings are fetched as they are played, or all at once with :prefetch.

The file as headers merged down to regions: :control is the <control> opcodes, and each of :regions carries its own opcodes over its group's, master's and the global ones, with #define names replaced. :include is a function of an #include path returning {:ok, text} or :error; without it, or when it says :error, the include is left out.

The URL of a source: github:user/repo/branch/path/to/file.sfz becomes the raw file on GitHub, a URL is kept, and either has its spaces and other reserved characters escaped.

Types

bank()

@type bank() :: %{required(float()) => [entry()]}

entry()

@type entry() :: {String.t(), keyword()}

region()

@type region() :: %{required(String.t()) => String.t()}

t()

@type t() :: %{control: %{required(String.t()) => String.t()}, regions: [region()]}

Functions

bank(map, opts)

@spec bank(t(), keyword()) :: bank()

The bank a parsed file maps: each recorded pitch, as a MIDI number that may be fractional, to its files in order of round robin, each {file, opts} with :loop and :gain as TuningFork.Sample.Bank.put/3 takes them. Files are resolved against :base, the file's own path or URL, and default_path.

Options

  • :base — required; the path or URL the file was read from
  • :velocity — the layer to take, default 100
  • :keys — a range of MIDI numbers; regions whose key centre lies outside it are left out

instrument?(name)

@spec instrument?(String.t()) :: boolean()

Whether name is one of instruments/0.

instruments()

@spec instruments() :: %{
  required(String.t()) => %{
    source: String.t(),
    licence: String.t(),
    credit: String.t(),
    what: String.t()
  }
}

The instruments known by name, the library's and the application's: each a :source for load/2, its :licence, who to :credit and :what it is.

load(source, opts \\ [])

@spec load(String.t(), keyword()) ::
  {:ok, String.t(), non_neg_integer()} | {:error, term()}

Register an instrument with the bank: one of instruments/0 by name, or a source as url/1 takes it, or a local path. The file and its includes are read now; the recordings are fetched as they are played, or all at once with :prefetch.

Returns {:ok, name, notes} with how many notes the bank holds, or {:error, reason}.

Options

  • :name — the bank name, default the instrument's name or the file's basename
  • :velocity, :keys — as bank/2 takes them; a listed instrument brings its own :keys
  • :prefetch — start fetching every recording in the background, default false

parse(text, opts \\ [])

@spec parse(String.t(), keyword()) :: t()

The file as headers merged down to regions: :control is the <control> opcodes, and each of :regions carries its own opcodes over its group's, master's and the global ones, with #define names replaced. :include is a function of an #include path returning {:ok, text} or :error; without it, or when it says :error, the include is left out.

url(url)

@spec url(String.t()) :: String.t()

The URL of a source: github:user/repo/branch/path/to/file.sfz becomes the raw file on GitHub, a URL is kept, and either has its spaces and other reserved characters escaped.