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

Generated background music: `music: %{elevenlabs: "prompt"}` composes a
track with the [ElevenLabs Music API](https://elevenlabs.io/docs/api-reference/music/compose)
and the result then goes through the normal music pipeline (loop/trim,
fades, ducking; see `PhxDemo.Render`).

    use PhxDemo.Case,
      music: %{elevenlabs: "calm lo-fi piano, soft drums", length_ms: 30_000}

Or pick a preset (see `presets/0`):

    use PhxDemo.Case, music: %{elevenlabs: :lofi}

Keys (atom or string):
  * `:elevenlabs`: a prompt string, or a preset name such as `:lofi`
    (required). A string that equals a preset name is the preset, so
    presets survive the round trip through `timeline.json`.
  * `:length_ms`: track length, 3_000 to 600_000 (default 30_000). A track
    shorter than the video loops, so a short one is fine.
  * `:instrumental`: no vocals (default `true`)
  * `:model_id`: e.g. `"music_v1"` (default: the API's default)
  * `:output_format`: an MP3 format, default `"mp3_44100_128"`

The API key comes from the `ELEVENLABS_API_KEY` environment variable, or
`config :phx_demo, :elevenlabs_api_key, "..."`. It is never put in the
settings, so it never reaches `timeline.json`.

The track is cached under `<output_dir>/.tts_cache`, keyed on the prompt and
options, so re-recording an unchanged demo makes no API call.

# `cache_key`

Cache key for a normalized spec.

# `check`

Pre-flight for generated music. Returns `:ok` when `music` isn't generated,
otherwise validates the setting and checks that an API key is available.
`settings` is a map with atom or string keys. Option `:api_key` overrides
the lookup (for testing).

# `check!`

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

# `generate`

Generates the track into `cache_dir` (or reuses the cached one). Returns
`{:ok, path}` or `{:error, message}`. Options: `:api_key`, `:base_url`,
`:http` (a 3-arity fun like `PhxDemo.HTTP.post_json/3`, for tests).

# `generated?`

Whether a `music:` value asks for generated music.

    iex> PhxDemo.MusicGen.generated?(%{elevenlabs: "lo-fi"})
    true

    iex> PhxDemo.MusicGen.generated?(%{"elevenlabs" => "lo-fi"})
    true

    iex> PhxDemo.MusicGen.generated?("song.mp3")
    false

# `preset_prompt`

The full prompt for a preset, or nil when `name` (atom or string) isn't one.

    iex> PhxDemo.MusicGen.preset_prompt(:lofi) =~ "lo-fi hip hop"
    true

    iex> PhxDemo.MusicGen.preset_prompt("lofi") == PhxDemo.MusicGen.preset_prompt(:lofi)
    true

    iex> PhxDemo.MusicGen.preset_prompt("calm piano")
    nil

# `presets`

The built-in presets: name to prompt. Each prompt also gets a shared suffix
asking for steady, vocal-free background music (see `preset_prompt/1`).

# `request_body`

The JSON request body for a normalized spec.

# `spec`

Normalizes a generated `music:` value into a map with atom keys and
defaults filled in. Returns `{:ok, spec}` or `{:error, message}`.

---

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