Audio Capture
Audio Capture
Section titled “Audio Capture”@cosella/audio-capture handles acquiring audio streams from the user’s microphone and device/system audio. It abstracts the platform differences between Electron (silent loopback) and web browsers (screen share picker) behind a unified AudioCaptureManager interface.
Installation
Section titled “Installation”pnpm add @cosella/audio-captureQuick Start
Section titled “Quick Start”import { createCaptureManager, useMicCapture, useDeviceCapture } from '@cosella/audio-capture';
const manager = createCaptureManager(); // Auto-detects Electron vs web
function CallSetup() { const mic = useMicCapture(manager); const device = useDeviceCapture(manager);
const handleStart = async () => { await mic.acquire(); // getUserMedia — works everywhere await device.acquire(); // Electron: silent loopback; Web: screen share picker };
return ( <div> <p>Mic: {mic.state}</p> <p>Device: {device.state}</p> <button onClick={handleStart}>Start Capture</button> </div> );}Platform Behavior
Section titled “Platform Behavior”| Capability | Desktop (Electron) | Web (Chrome) |
|---|---|---|
| Mic capture | getUserMedia() | getUserMedia() |
| Device audio | Silent loopback (auto-approved) | Screen share picker (user must check “Share audio”) |
| System audio | All system audio (WASAPI/CoreAudio) | Windows: system audio; macOS/Linux: tab audio only |
| User interaction | None (auto-approved by main process) | Required (screen share picker) |
Platform Detection
Section titled “Platform Detection”createCaptureManager() checks for window.cosella (the Electron preload bridge). If present, it returns an ElectronCaptureManager; otherwise, a WebCaptureManager.
API Reference
Section titled “API Reference”AudioCaptureManager
Section titled “AudioCaptureManager”interface AudioCaptureManager { acquireMic(deviceId?: string): Promise<MediaStream>; acquireDeviceAudio(): Promise<MediaStream>; getDevices(): Promise<AudioDeviceInfo[]>; onStateChange(cb: (event: CaptureStateEvent) => void): () => void; getState(channel: 'mic' | 'loopback'): CaptureState; release(channel: 'mic' | 'loopback'): void; dispose(): void;}CaptureState
Section titled “CaptureState”type CaptureState = 'idle' | 'requesting' | 'active' | 'ended' | 'error' | 'denied';CaptureError
Section titled “CaptureError”Extends Error with typed error codes:
| Code | Recoverable | Meaning |
|---|---|---|
PERMISSION_DENIED | Yes | User denied mic/screen permission |
DEVICE_NOT_FOUND | Yes | Selected device unavailable |
DEVICE_IN_USE | Yes | Device locked by another app |
AUDIO_NOT_SHARED | Yes | User didn’t check “Share audio” in picker |
NOT_SUPPORTED | No | Platform can’t capture this channel |
React Hooks
Section titled “React Hooks”useMicCapture
Section titled “useMicCapture”const { stream, state, error, acquire, release } = useMicCapture(manager);useDeviceCapture
Section titled “useDeviceCapture”const { stream, state, error, acquire, release } = useDeviceCapture(manager);useAudioDevices
Section titled “useAudioDevices”const { devices, refresh } = useAudioDevices(manager);Note: enumerateDevices() returns blank labels before mic permission is granted. Call acquire() on the mic first, then enumerate.
Important Notes
Section titled “Important Notes”acquireDeviceAudio()must be called from a user gesture context (click/tap handler)- The 16kHz sample rate constraint is a hint — browsers typically deliver 48kHz. The
audio-enginepackage handles resampling. - v1 assumes headphones. No echo cancellation for the loopback stream.