import type { MutableRefObject } from "react"; export type MicrophoneBufferType = { input?: GainNode; output?: MediaStreamAudioSourceNode; rawOutput?: GainNode; analyser?: AnalyserNode; finalAnalyser?: AnalyserNode; mediaStream?: MediaStream; processedStream?: MediaStream; monitorTap?: GainNode; monitorAnalyser?: AnalyserNode; muteGain?: GainNode; /** The raw microphone, sent as is when nothing would change it. Mute switches it off (voice#53). */ directTrack?: MediaStreamTrack; volumeGain?: GainNode; noiseGate?: GainNode; noiseGateWorklet?: AudioWorkletNode; rnnoiseNode?: AudioWorkletNode; agcAnalyser?: AnalyserNode; agcGain?: GainNode; compressor?: DynamicsCompressorNode; /** Makeup gain after the compressor, so taming peaks does not just get * quieter. Absent when the compressor is off (GRYT-511). */ compressorMakeup?: GainNode; /** Last stage before the gate, always on: nothing leaves above OUTPUT_CEILING_DB (GRYT-1679). */ limiter?: DynamicsCompressorNode; /** Whether the voice goes out, as setSending last said. The own transport reads it (GRYT-1818). */ sending?: boolean; /** Told whenever setSending is, so the own transport's worklet mutes with the pipeline. */ onSending?: (on: boolean) => void; }; /** * Why the microphone could not be acquired, kept apart because the advice differs: "denied" * is fixed in the OS, "no-device" means nothing is plugged in, "failed" is everything else. */ export type MicrophoneUnavailableReason = "denied" | "no-device" | "failed"; export interface MicrophoneInterface { addHandle: (id: string) => void; removeHandle: (id: string) => void; /** * Set while there is no usable microphone, null once one is live. Joining still works — * listening without a microphone is useful — so this is what lets the UI say so. */ micUnavailable: MicrophoneUnavailableReason | null; /** * True while a `getUserMedia` is out and has not come back. A request in flight deserves * patience; no request at all deserves none, and one timeout for both is wrong either way. */ isAcquiring: boolean; microphoneBuffer: MicrophoneBufferType; isBrowserSupported: boolean | undefined; devices: InputDeviceInfo[]; audioContext?: AudioContext; isLoaded: boolean; getDevices: () => Promise; getVisualizerData: () => Uint8Array | null; /** Level the noise gate is deciding on, 0-100. Null if the gate worklet is unavailable. */ getGateLevel: () => number | null; /** True while audio is actually leaving this client. Null if the gate worklet is unavailable. */ isTransmitting: boolean | null; isPttActive: MutableRefObject; /** * Opens and closes the transmit gate in push-to-talk mode. The embedder owns the trigger * and calls this; a no-op in voice-activity mode. */ setPushToTalkActive: (active: boolean) => void; }