Media — Audio record, playback, mic gate, formats (privacy + widgets)

mandatory

Contrato cross-surface para gravação e reprodução de áudio em apps Koder (não-voice). Toggles em Settings (`media.audio.mic`, `media.audio.playback_in_background`); defaults seguros; codecs canônicos (Opus/AAC/MP3 in; Opus out via kodec); widgets `KoderAudioPlayer` + `KoderAudioRecorder` em `engines/sdk/koder_kit`. Recording indicator obrigatório. Upload size cap 100 MB. `<APP>-AUDIO-*` error map. **Relação com voice:** `specs/voice/wake-word.kmd` cobre wake-word + Talk Mode (ouvir o usuário ativamente, sub-domínio). Este spec cobre áudio genérico (tocar/gravar como mídia). Mic permission é compartilhada; voice toggles continuam separados em Settings.

Media — Audio Spec — v0.1

Normative cross-surface spec para audio I/O em apps Koder, *xcluindo voicewake-word*(coberto em `specsvoice/wake-word.kmd). Implementação obrigatória via widgets KoderAudio* em enginessdkkoder_kit (Flutter) — nunca rolar audioplayers upstream ou audio` raw local.


Scope

Aplica-se a *odo app Koder que grave OU reproduza áudio fora do domínio voice/wake-word* audio memos (recorded notes), podcast playback, voicemail, audio attachments em chat, ringtones, sound effects de UI, music streaming.

*ora de scope (cobertos por voice/wake-word.kmd):*wake-word detection (ring buffer préwake), Talk Mode (alwayslistening), barge-in TTS cancellation.

Surfaces cobertas: Flutter mobile (Android + iOS), Flutter desktop (Linux + macOS + Windows), Flutter web ou templ+HTMX, TV (raro; playback de ringtones em smart-TV apps). CLI/TUI delegam ao desktop player externo.


1 — MUST: expose "Áudio" toggle group in Settings

Sub-seção "Áudio" do agrupamento "Mídia"; *eve*conter, na ordem:

  1. *icrofone*(media.audio.mic) — gate à gravação de áudio
  2. *eproduzir em background*(media.audio.playback_in_background)

    — checkbox; quando ON, audio continua tocando ao sair do app (iOS exige entitlement audio no Info.plist)

  3. *ualidade de gravação*(media.audio.record_quality) — dropdown

    voice (32 kbps Opus) / music (96 kbps Opus) / lossless (FLAC)

  4. *cho cancellation*(media.audio.aec) — toggle (relevante em

    gravação ambiental); default ON

media.audio.mic é *eparado*de voice.enabled da §1 do voice/wake-word.kmd. Voice toggles controlam wake-word + Talk Mode; este toggle controla gravação manual (pushtotalk, memos).


2 — MUST: defaults seguros em fresh install

Chave Default Notas
media.audio.mic *FF* Privacybydefault; primeiro uso dispara permission prompt do SO
media.audio.playback_in_background *FF* iOS exige entitlement; user opt-in via toggle
media.audio.record_quality voice 32 kbps Opus equilibra inteligibilidade e tamanho de upload
media.audio.aec *N* Reduz echo de loudspeaker em gravações; OFF útil só pra music/instrument capture

3 — MUST: privacidade + recording indicator

  • *UNCA*auto-upload de gravação (user confirma antes do POST)
  • *UNCA*keep mic open em background além de 5 s pós-leave (a menos

    que playback_in_background ativo E o app esteja em sessão de gravação explícita — caso raríssimo, exige consent toast)

  • *ecording indicator*obrigatório quando capture ativo: ícone +

    timer no app; em mobile, system status bar (mic dot) é OS-managed

  • *ic permission*é compartilhada com voice.enabled no SO; este

    toggle controla *so* não permissão de SO


4 — MUST: widget surface no koder_kit

Widget Função
KoderAudioPlayer Playback de KoderAudioRef ou URL; controls (playpauseseekspeedAirPlay-like)
KoderAudioRecorder Gravação com VU meter + startstoppause; gates media.audio.mic; emite chunks streaming
KoderAudioPicker Sheet "Gravar áudio" / "Escolher arquivo"; retorna KoderAudioRef
KoderAudioWaveform Render de waveform pré-computado (para preview de memo, attachment); lazy
KoderRingtonePicker Helper específico para tom de notificação; restringe a duração ≤ 30 s

KoderAudioRef: {uri, mime, durationMs, sampleRate, channels, codec, sizeBytes, koderUserId?, workspaceId?}.


5 — MUST: format support

*ecode (via engines/kodec):*Opus, AAC, MP3, FLAC, OGG Vorbis, WAV.

*ncode (default):*Opus em OGG container. FLAC quando record_quality = lossless.

*ample rate:*16 kHz mono para voice, 48 kHz stereo para music e lossless.

*pload size cap:*100 MB. Exceder → erro <APP>-AUDIO-SIZE-001.


6 — MUST: error surface

Cenário ID Texto pt-BR
Permissão de mic negada <APP>-AUDIO-MIC-001 "Permita o acesso ao microfone para gravar áudio."
Codec de decode não suportado <APP>-AUDIO-CDC-001 "Este áudio usa codec não suportado ($codec)."
Tamanho excede 100 MB <APP>-AUDIO-SIZE-001 "Áudio muito grande. Reduza qualidade ou duração."
Upload falhou <APP>-AUDIO-NET-001 "Falha ao enviar o áudio. Tente novamente."
Hardware indisponível (mic ocupado outro app) <APP>-AUDIO-HW-001 "Microfone em uso por outro app. Feche-o e tente novamente."
Background playback sem entitlement (iOS) <APP>-AUDIO-BG-001 "Ative 'Reproduzir em background' em Ajustes para continuar."

7 — Observability

  • Counters: media.audio.recorded, media.audio.uploaded,

    media.audio.playback_started, media.audio.upload_error

  • Latency histograms: media.audio.encode_ms,

    media.audio.upload_ms, media.audio.first_sample_ms

  • *ão*emitir métricas que vazem conteúdo (transcript, file path,

    hash do audio) — apenas counters + latência


8 — Adoption checklist (per app)

  • [ ] Importa KoderMediaSettingsTile (sub-seção Áudio visível)
  • [ ] Usa KoderAudioPlayer/KoderAudioRecorder (nunca audioplayers upstream raw)
  • [ ] Defaults da §2 respeitados
  • [ ] Recording indicator implementado (§3)
  • [ ] Codec layer via engines/kodec
  • [ ] Error map da §6 implementado
  • [ ] Upload path respeita multi-tenancy/contract.kmd
  • [ ] Se o app também tem voice/wake-word: separar UX (Settings "Voz"

    e "Áudio" coexistem; cross-link aceito mas não obrigatório)


Relação com voice/wake-word.kmd

Voice ≠ Audio:

  • *oice*(specs/voice/wake-word.kmd): capability "ouvir o usuário

    ativamente" — wakeword detection (ring buffer préwake), Talk Mode (alwayslistening), STT pipeline, bargein. Settings group "Voz".

  • *udio*(este spec): capability "tocar/gravar áudio como mídia" —

    memos, attachments, podcast, music, ringtones. Settings group "Áudio".

Mic permission do SO é *ompartilhada* cada toggle em Settings é *so*(não permissão). Se um app usa ambos, manter os 2 sub-grupos em Settings é a UX preferida (clareza de intent: o usuário pode desligar voice mas manter mic pra audio memos).


Non-normative — referências

  • Sibling specs: specs/media/image.kmd, specs/media/video.kmd,

    specs/media/document.kmd

  • Voice (subdomain): `specsvoicewakeword.kmd` (mic gate

    precedent + ring buffer privacy)

  • Codec engine: engines/kodec/ (canônica)
  • Implementation surface: engines/sdk/koder_kit/lib/src/media/

Source: ../home/koder/dev/koder/meta/docs/stack/specs/media/audio.kmd