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

Audio input for demos: `PhxDemo.Steps.upload_audio/4`,
`use_microphone/3`, `stop_microphone/1` and `use_camera(path, audio: true)`.

Uploads go through the ordinary `upload/4` (or `drop_files/4`); this module
only adds the `:audio_input` timeline event. The simulated microphone is part
of the JS stand-in in `PhxDemo.Camera`: `getUserMedia({audio: ...})` returns a
`MediaStream` from WebAudio (the fixture is decoded with `decodeAudioData` and
played into a `MediaStreamAudioDestinationNode`). A request for
`{video, audio}` gets both fake tracks.

Playback starts when the app requests the microphone, not when the step runs:
the page's "record" button is what starts the voice, as with a real person.
A non-looping fixture plays once, and a later request plays it again.

## Formats

Anything ffmpeg decodes. WAV, MP3, Ogg, Opus and FLAC are served as they are
(Chromium's `decodeAudioData` reads them); other formats (m4a, aac, wma, a
video's soundtrack, ...) are converted to 16-bit WAV with ffmpeg and cached
under the system temp dir, keyed by path, size and mtime.

## Timeline

`:audio_input` events carry `mode` (`"upload"`, `"microphone"` or `"camera"`),
`action`, `file` (absolute path) and `audible`:

  * `upload_audio`: `action: "upload"`, `start` is the event's `at_ms`
    (the moment the file was attached), `duration_ms` from ffprobe
  * `use_microphone`: `action: "use"` when the step runs, then
    `action: "play"` (with `duration_ms` and `loop`) each time the app starts
    listening, and `action: "stop"` for `stop_microphone`

`PhxDemo.Render` mixes the events with `audible: true` into the MP4.

# `classify`

`:browser` if the file is served as it is, `:convert` if ffmpeg converts it to WAV.

# `duration_ms`

Duration of an audio file in ms from ffprobe, or nil if it can't be read.

# `prepare!`

The path the browser can decode for a fixture: the file itself, or a cached
WAV conversion. Raises `ArgumentError` if the file is missing or needs
ffmpeg and it isn't installed.

# `soundtrack!`

The audio of a video as a cached WAV. Raises `ArgumentError` if the video has
no audio stream.

---

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