/** * frames.ts, the arithmetic between a device and a consumer. * * Everything here is pure and shared, because every one of these conversions is * a place where a mistake is SILENT rather than loud: * * - a recorder hands over byte chunks whose length has nothing to do with a * frame size (a pipe read is whatever the kernel had), so frames must be * re-cut rather than assumed, {@link AudioFrameSlicer}; * - the classifier was trained on int16 magnitudes, so audio scaled to -1..1 * scores near zero forever and looks exactly like a microphone that is not * picking anything up, {@link pcm16ToFloatSamples}; * - speech-to-text needs a container, and a WAV header with the wrong byte * order or a stale length field transcribes as silence rather than failing. */ /** Bytes per sample in the 16-bit PCM every recorder here is asked for. */ export declare const PCM16_BYTES_PER_SAMPLE = 2; /** The int16 full-scale magnitude, the scale detector frames carry. */ export declare const PCM16_FULL_SCALE = 32768; /** * Decode little-endian signed 16-bit PCM into the float MAGNITUDES the wake * front end expects (-32768..32767), not normalised -1..1 values. */ export declare function pcm16ToFloatSamples(bytes: Uint8Array): Float32Array; /** Encode float magnitudes back to little-endian signed 16-bit PCM, clamping. */ export declare function floatSamplesToPcm16(samples: Float32Array): Uint8Array; /** * Root-mean-square level of a frame, on the int16 magnitude scale. Used for * silence detection, which is why it is not normalised: the threshold constants * that read it are stated in the same units the frames carry. */ export declare function frameRms(samples: Float32Array): number; /** * Re-cuts an arbitrary stream of samples into fixed-size frames. * * A recorder's stdout arrives in whatever sizes the pipe produced, and a browser * worklet delivers 128-sample render quanta. Neither is the 1280 samples the * wake engine requires per call, and handing it a short frame does not fail, it * shifts the whole front end off the framing the classifier was trained at. So * the remainder is CARRIED, never dropped and never padded. */ export declare class AudioFrameSlicer { #private; constructor(frameSamples: number); /** Samples per emitted frame. */ get frameSamples(): number; /** Samples held back because they do not yet complete a frame. */ get pendingSamples(): number; /** Drop the carried remainder. Called when a stream restarts. */ reset(): void; /** * Add samples and return every whole frame they completed, in order. Each * returned frame is its own Float32Array, so a consumer may retain it (the * wake pre-roll buffer does) without the next chunk overwriting it. */ push(samples: Float32Array): Float32Array[]; } /** Concatenate frames into one buffer. */ export declare function concatSamples(chunks: readonly Float32Array[]): Float32Array; /** * Wrap float magnitudes in a 16-bit PCM WAV container. * * WAV rather than the browser's native webm/opus for one reason: this is the one * encoding both surfaces can produce from raw frames without a codec, and a * host-captured stream has no container of its own. Written by hand because the * header is 44 fixed bytes and pulling a dependency in for it would put a * codec's release cadence in front of the microphone path. */ export declare function encodeWavPcm16(samples: Float32Array, sampleRate: number, channels?: number): Uint8Array; /** * Base64-encode bytes without `Buffer` or `btoa`. * * Hand-written because this module is imported by a browser bundle AND by a * daemon child process: `Buffer` does not exist in one, and `btoa` needs a * binary string built first, which for a ten-second clip is a 320 kB * intermediate. Encoding straight from the bytes avoids both. */ /** * Decode base64 back to bytes, without `Buffer` or `atob`. * * The counterpart of {@link bytesToBase64}, and needed for the same reason: the * noise-suppression stage carries its WebAssembly module as base64 in a source * file, so it decodes in a daemon child process and in a browser tab through the * same code path. Ignores whitespace and stops at padding; a character outside * the alphabet is a corrupted blob, so it throws rather than decoding to * plausible-looking garbage. */ export declare function base64ToBytes(text: string): Uint8Array; export declare function bytesToBase64(bytes: Uint8Array): string; //# sourceMappingURL=frames.d.ts.map