# grove.composer.audio

Native voice capture for **composer-only** Sprigs with the `microphone.record`
grant. Not available in settings, Roots, channel/split panes, web dashboards,
or subframes. Browser `getUserMedia` remains denied for every Grove document.
Feature-detect `grove.composer.audio`; older Mango versions do not support it.

```js
const audio = grove.composer.audio
const unsubscribe = audio.onUpdate(update => {
  // {state: 'recording', duration: seconds, amplitude: 0...1}
  // {state: 'stopped'}: duration limit or interruption; call stop to collect.
  // {state: 'cancelled'}: capture was discarded (e.g. app backgrounded).
})
await audio.start()
const clip = await audio.stop()
// clip: {data: Uint8Array, duration: seconds, mimeType: 'audio/mp4', filename: 'voice-message.m4a'}
const seconds = Math.floor(clip.duration)
const duration = `${String(Math.floor(seconds / 60)).padStart(2, '0')}:${String(seconds % 60).padStart(2, '0')}`
grove.composer.upload(clip.data, {filename: clip.filename, caption: `[Voice] (${duration}):`}) // also requires uploads.use
unsubscribe()
```

- Call `start()` only from an explicit Record button. Mango must be active and
  the composer visible. Apple prompts for microphone consent when needed.
- `start`, `stop`, and `cancel` return Promises; handle their rejections.
- Only one recording across Mango's windows is allowed. Concurrent operations
  are rejected rather than queued. `cancel()` discards an active recording.
- Capture is mono AAC, 24 kHz / 64 kbit/s, at most 180 seconds and 8 MiB.
- Closing, WebKit failure/reload, grant revocation, or app backgrounding cancels
  capture. Recordings are temporary and are not restored after a restart.
- `onUpdate` returns an unsubscribe function. Treat level events as lossy UI
  samples, not an audio stream; do not use their count to measure elapsed time.
- No filesystem paths or provider credentials enter the Sprig. Upload is a
  separate user action through `grove.composer.upload`; the normal draft/send
  confirmation flow is unchanged. The chosen host must support audio uploads.
- No background recording, camera access, or arbitrary audio-device selection.

See `Examples/grove-voice-composer` for a dependency-free 420 × 144pt example.

The example sends ordinary IRC text, such as
`[Voice] (00:12): https://files.example/voice-message.m4a`. This readable
fallback is a Mango presentation choice, not an IRCv3 standard. It uses no
Markdown link or proprietary message tag. Mango detects the URL and obtains
duration/waveform from the audio itself, not from the caption; other clients can
display the text and open the URL. Bare audio URLs continue to preview normally.
