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)The library carries no instruments. The application registers the ones it uses —
register/2, or the :sfz key of the :tuning_fork environment in the same shape —
each with its source, its licence and who to credit; instruments/0 lists them.
load/2 takes a registered name, a github: source, a URL or a local path. The kit
loads a registered name itself the first time a pattern plays it, so s("fingerbass")
in Strudel needs nothing more than the registration.
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
Types
An instrument by name: where its SFZ file is, its licence, who to credit, what it is, and the keys to read.
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 the application has registered, by name: each a :source for
load/2, its :licence, who to :credit, :what it is and, when given, the :keys
read. The library registers none of its own.
Read an instrument into 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.
Register every instrument in instruments, a map of name to instrument as register/2 takes them.
Register an instrument under name, so load/2 and the kit know it by that name.
instrument must carry :source, :licence, :credit and :what; :keys is optional.
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
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.
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
Whether name is one of instruments/0.
@spec instruments() :: %{required(String.t()) => instrument()}
The instruments the application has registered, by name: each a :source for
load/2, its :licence, who to :credit, :what it is and, when given, the :keys
read. The library registers none of its own.
@spec load(String.t(), keyword()) :: {:ok, String.t(), non_neg_integer()} | {:error, term()}
Read an instrument into 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— asbank/2takes them; a registered instrument brings its own:keys:prefetch— start fetching every recording in the background, defaultfalse
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.
@spec register(%{required(String.t()) => instrument()}) :: :ok
Register every instrument in instruments, a map of name to instrument as register/2 takes them.
@spec register(String.t(), instrument()) :: :ok
Register an instrument under name, so load/2 and the kit know it by that name.
instrument must carry :source, :licence, :credit and :what; :keys is optional.
TuningFork.Sfz.register("fingerbass", %{
source: "github:freepats/electric-bass-YR/master/FingerBassYR 20190930.sfz",
licence: "CC0 1.0",
credit: "FreePats, Yamaha RBX bass",
what: "an electric bass, fingered"
})
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.