import type { AudioFrame } from '@livekit/rtc-node';
import type { ReadableStream } from 'node:stream/web';
import type { WordTokenizer } from '../../tokenize/index.js';
import { AudioOutput, type PlaybackFinishedEvent, TextOutput, type TimedString } from '../io.js';
interface TextSyncOptions {
    speed: number;
    hyphenateWord: (word: string) => string[];
    splitWords: (words: string) => [string, number, number][];
    wordTokenizer: WordTokenizer;
    enabled: boolean;
}
/**
 * Tracks speaking rate data from TTS timing annotations.
 * @internal Exported for testing purposes.
 */
export declare class SpeakingRateData {
    /** Timestamps of the speaking rate. */
    timestamps: number[];
    /** Speed at the timestamp. */
    speakingRate: number[];
    /** Accumulated speaking units up to the timestamp. */
    speakIntegrals: number[];
    /** Buffer for text without timing annotations yet. */
    private textBuffer;
    /**
     * Add by speaking rate estimation.
     */
    addByRate(timestamp: number, speakingRate: number): void;
    /**
     * Add annotation from TimedString with start_time/end_time.
     */
    addByAnnotation(text: string, startTime: number | undefined, endTime: number | undefined): void;
    /**
     * Get accumulated speaking units up to the given timestamp.
     */
    accumulateTo(timestamp: number): number;
    /** Get the last pushed timestamp. */
    get pushedDuration(): number;
}
declare class SegmentSynchronizerImpl {
    private readonly options;
    private readonly nextInChain;
    /**
     * When true, fall back to seeding `startWallTime` / resolving `startFuture`
     * from the first audio frame in `pushAudio` if `onPlaybackStarted` hasn't
     * fired yet. Used for impls created by mid-flow segment rotation, where the
     * wrapped `AudioOutput.firstFrameEmitted` stays true and won't propagate
     * another `playbackStarted` event. The first impl (constructed in the
     * `TranscriptionSynchronizer` constructor) leaves this `false` so it always
     * trusts the chain — required for `waitPlaybackStart=true` on
     * `DataStreamAudioOutput`.
     */
    private readonly seedFromPushAudio;
    private enabled;
    private textData;
    private audioData;
    private speed;
    private outputStream;
    private outputStreamWriter;
    private captureTask;
    private startWallTime?;
    private startFuture;
    private closedFuture;
    private playbackCompleted;
    private interrupted;
    private pausedWallTime?;
    /** Accumulated paused time in milliseconds; subtracted from wall-clock elapsed. */
    private pausedDuration;
    /** Gate held closed while paused; the main task awaits it between words. */
    private outputEnabledFuture;
    private logger;
    constructor(options: TextSyncOptions, nextInChain: TextOutput, 
    /**
     * When true, fall back to seeding `startWallTime` / resolving `startFuture`
     * from the first audio frame in `pushAudio` if `onPlaybackStarted` hasn't
     * fired yet. Used for impls created by mid-flow segment rotation, where the
     * wrapped `AudioOutput.firstFrameEmitted` stays true and won't propagate
     * another `playbackStarted` event. The first impl (constructed in the
     * `TranscriptionSynchronizer` constructor) leaves this `false` so it always
     * trusts the chain — required for `waitPlaybackStart=true` on
     * `DataStreamAudioOutput`.
     */
    seedFromPushAudio?: boolean);
    get closed(): boolean;
    get audioInputEnded(): boolean;
    get textInputEnded(): boolean;
    get hasPendingText(): boolean;
    get readable(): ReadableStream<string | TimedString>;
    onPlaybackStarted(startTime: number): void;
    pushAudio(frame: AudioFrame): void;
    endAudioInput(): void;
    pushText(text: string | TimedString): void;
    endTextInput(): void;
    pause(): void;
    resume(): void;
    markPlaybackFinished(_playbackPosition: number, interrupted: boolean): void;
    get synchronizedTranscript(): string;
    private captureTaskImpl;
    private mainTask;
    /** Elapsed audio-playback time in seconds — wall-clock minus paused time. */
    private synchronizedElapsedSeconds;
    private calcHyphens;
    private sleepIfNotClosed;
    close(): Promise<void>;
}
export interface TranscriptionSynchronizerOptions {
    speed: number;
    hyphenateWord: (word: string) => string[];
    splitWords: (words: string) => [string, number, number][];
    wordTokenizer: WordTokenizer;
    enabled: boolean;
}
export declare const defaultTextSyncOptions: TranscriptionSynchronizerOptions;
export declare class TranscriptionSynchronizer {
    readonly audioOutput: SyncedAudioOutput;
    readonly textOutput: SyncedTextOutput;
    private options;
    private rotateSegmentTask;
    private _outputsAttached;
    private closed;
    /** @internal */
    _impl: SegmentSynchronizerImpl;
    /** @internal */
    _audioAttached: boolean;
    /** @internal */
    _textAttached: boolean;
    /** @internal */
    _warnedAsymmetricDetach: boolean;
    /** Pause state tracked at the synchronizer level so it can be reapplied across segment rotations.
     * @internal */
    _paused: boolean;
    private logger;
    constructor(nextInChainAudio: AudioOutput, nextInChainText: TextOutput, options?: TranscriptionSynchronizerOptions);
    get outputsAttached(): boolean;
    get enabled(): boolean;
    set enabled(value: boolean);
    /** @internal */
    _onAttachmentChanged(args: {
        audioAttached?: boolean;
        textAttached?: boolean;
    }): void;
    rotateSegment(): void;
    close(): Promise<void>;
    barrier(): Promise<void>;
    private rotateSegmentTaskImpl;
}
declare class SyncedAudioOutput extends AudioOutput {
    synchronizer: TranscriptionSynchronizer;
    private nextInChainAudio;
    private pushedDuration;
    constructor(synchronizer: TranscriptionSynchronizer, nextInChainAudio: AudioOutput);
    pause(): void;
    resume(): void;
    captureFrame(frame: AudioFrame): Promise<void>;
    flush(): void;
    clearBuffer(): void;
    waitForPlayout(): Promise<PlaybackFinishedEvent>;
    onPlaybackStarted(createdAt: number): void;
    onPlaybackFinished(ev: PlaybackFinishedEvent): void;
    onAttached(): void;
    onDetached(): void;
}
declare class SyncedTextOutput extends TextOutput {
    private readonly synchronizer;
    readonly nextInChain: TextOutput;
    private capturing;
    private logger;
    constructor(synchronizer: TranscriptionSynchronizer, nextInChain: TextOutput);
    captureText(text: string | TimedString): Promise<void>;
    flush(): Promise<void>;
    onAttached(): void;
    onDetached(): void;
}
export {};
//# sourceMappingURL=synchronizer.d.ts.map