# `PhxDemo.Midi`
[🔗](https://github.com/thenatespack/phoenix_demo/blob/main/lib/phx_demo/midi.ex#L1)

MIDI background music: `music: "song.mid"` is rendered to WAV with
[fluidsynth](https://www.fluidsynth.org/) and the WAV then goes through the
normal music pipeline (loop/trim, fades, ducking; see `PhxDemo.Render`).

The WAV is cached under `<output_dir>/.tts_cache`, next to narration clips,
keyed on the MIDI bytes, the soundfont (path, size and mtime) and the sample
rate. Re-recording an unchanged demo doesn't run fluidsynth again.

## Soundfont

Resolved in this order:

  1. the `:soundfont` setting (`use PhxDemo.Case, soundfont: "..."` or
     `config :phx_demo, :soundfont, "..."`)
  2. the soundfont downloaded by `mix phx_demo.soundfont.setup`
  3. common system locations (`/usr/share/sounds/sf2/FluidR3_GM.sf2`,
     `/opt/homebrew/share/soundfonts/...`, ...)

# `cache_key`

Cache key for a rendered MIDI: a hash of the MIDI bytes, the soundfont's
path, size and mtime, and the sample rate.

# `check`

Pre-flight for MIDI music. Returns `:ok` when `music` isn't MIDI, otherwise
checks that fluidsynth is on PATH and a soundfont resolves. `settings` is a
map with atom or string keys (`PhxDemo.settings/1`, or a timeline's).

# `check!`

Like `check/2`, raising `PhxDemo.Env.Error`.

# `downloaded_soundfont`

Where `mix phx_demo.soundfont.setup` puts the default soundfont.

# `fluidsynth_args`

The fluidsynth argument list for rendering `midi` to `out`.

# `midi?`

Whether a `music:` value is a MIDI file, judged by extension.

    iex> PhxDemo.Midi.midi?("a/song.MID")
    true

    iex> PhxDemo.Midi.midi?("song.midi")
    true

    iex> PhxDemo.Midi.midi?("song.mp3")
    false

    iex> PhxDemo.Midi.midi?(false)
    false

# `render_wav`

Renders `midi` to a WAV in `cache_dir` (or reuses the cached one). Returns
`{:ok, wav_path}` or `{:error, message}`. Options: `:soundfont` (the
setting), `:rate`.

# `resolve_soundfont`

Finds a soundfont. `setting` is the `:soundfont` setting (a path, or
nil/false). Options (for testing): `:downloaded` and `:system`, to override
those locations.

Returns `{:ok, path}`, or `{:error, message}`. An explicit setting that
points at a missing file is an error; it doesn't fall through.

# `soundfont_dir`

Directory for the soundfont downloaded by `mix phx_demo.soundfont.setup`.

# `system_soundfonts`

Common system soundfont locations, in search order.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
