{"version":3,"sources":["../../../src/voice/room_io/_output.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { AgentSession as pb } from '@livekit/protocol';\nimport type { RemoteParticipant } from '@livekit/rtc-node';\nimport {\n  type AudioFrame,\n  AudioSource,\n  LocalAudioTrack,\n  type LocalTrackPublication,\n  type Participant,\n  type RemoteTrackPublication,\n  type Room,\n  RoomEvent,\n  type TextStreamWriter,\n  TrackPublishOptions,\n  TrackSource,\n} from '@livekit/rtc-node';\nimport {\n  ATTRIBUTE_TRANSCRIPTION_FINAL,\n  ATTRIBUTE_TRANSCRIPTION_SEGMENT_ID,\n  ATTRIBUTE_TRANSCRIPTION_TRACK_ID,\n  TOPIC_TRANSCRIPTION,\n} from '../../constants.js';\nimport { log } from '../../log.js';\nimport {\n  type ExpressiveTag,\n  TranscriptMarkupStripper,\n  expressionAttribute,\n  splitAllMarkup,\n  stripAllMarkup,\n} from '../../tts/provider_format.js';\nimport { Future, Task, shortuuid } from '../../utils.js';\nimport { AudioOutput, TextOutput, type TimedString, isTimedString } from '../io.js';\nimport { findMicrophoneTrackId } from '../transcription/index.js';\n\nexport interface TranscriptionOutputOptions {\n  /**\n   * Whether expressive markup may be present in the text reaching this sink.\n   *\n   * Evaluated per chunk, because the session latches it on the first turn that injects\n   * the markup guide — after this sink is constructed. Defaults to \"never\", so a session\n   * that doesn't use expressive mode publishes its transcript untouched: the strip works\n   * off the union of every provider's tag names, and an agent that legitimately writes\n   * `<break time=\"1s\"/>` should not have it silently deleted.\n   */\n  expressiveEnabled?: () => boolean;\n}\n\nabstract class BaseParticipantTranscriptionOutput extends TextOutput {\n  protected room: Room;\n  protected isDeltaStream: boolean;\n  protected participantIdentity: string | null = null;\n  protected trackId?: string;\n  protected capturing: boolean = false;\n  protected latestText: string = '';\n  protected currentId: string = this.generateCurrentId();\n  protected logger = log();\n  protected expressiveEnabled: () => boolean;\n\n  constructor(\n    room: Room,\n    isDeltaStream: boolean,\n    participant: Participant | string | null,\n    options: TranscriptionOutputOptions = {},\n  ) {\n    super();\n    this.room = room;\n    this.isDeltaStream = isDeltaStream;\n    this.expressiveEnabled = options.expressiveEnabled ?? (() => false);\n\n    this.room.on(RoomEvent.TrackPublished, this.onTrackPublished);\n    this.room.on(RoomEvent.LocalTrackPublished, this.onLocalTrackPublished);\n\n    this.setParticipant(participant);\n  }\n\n  setParticipant(participant: Participant | string | null) {\n    if (typeof participant === 'string' || participant === null) {\n      this.participantIdentity = participant;\n    } else {\n      this.participantIdentity = participant.identity;\n    }\n\n    if (!this.participantIdentity) {\n      return;\n    }\n\n    try {\n      this.trackId = findMicrophoneTrackId(this.room, this.participantIdentity);\n    } catch (error) {\n      // track id is optional for TextStream when audio is not published\n    }\n\n    this.flush();\n    this.resetState();\n  }\n\n  protected onTrackPublished = (track: RemoteTrackPublication, participant: RemoteParticipant) => {\n    if (\n      !this.participantIdentity ||\n      participant.identity !== this.participantIdentity ||\n      track.source !== TrackSource.SOURCE_MICROPHONE\n    ) {\n      return;\n    }\n\n    this.trackId = track.sid;\n  };\n\n  protected onLocalTrackPublished = (track: LocalTrackPublication | undefined) => {\n    if (!track) {\n      this.logger.warn('LocalTrackPublished event without publication payload');\n      return;\n    }\n\n    if (\n      !this.participantIdentity ||\n      this.participantIdentity !== this.room.localParticipant?.identity ||\n      track.source !== TrackSource.SOURCE_MICROPHONE\n    ) {\n      return;\n    }\n\n    this.trackId = track.sid;\n  };\n\n  protected generateCurrentId(): string {\n    return shortuuid('SG_');\n  }\n\n  protected resetState() {\n    this.currentId = this.generateCurrentId();\n    this.capturing = false;\n    this.latestText = '';\n  }\n\n  async captureText(text: string | TimedString) {\n    if (!this.participantIdentity) {\n      return;\n    }\n\n    const textStr = isTimedString(text) ? text.text : text;\n    this.latestText = textStr;\n    await this.handleCaptureText(textStr);\n  }\n\n  flush() {\n    if (!this.participantIdentity || !this.capturing) {\n      return;\n    }\n\n    this.capturing = false;\n    this.handleFlush();\n  }\n\n  protected abstract handleCaptureText(text: string): Promise<void>;\n  protected abstract handleFlush(): void;\n}\n\nexport interface ParticipantTranscriptionOutputOptions extends TranscriptionOutputOptions {\n  /** When true, each chunk sent on the `lk.transcription` datastream topic is serialized\n   *  as a JSON object with `text`, and `start_time`/`end_time`/`confidence`/\n   *  `start_time_offset` when the captured value is a TimedString. Each object is\n   *  suffixed with a newline so subscribers can parse the stream line-by-line. */\n  jsonFormat?: boolean;\n}\n\nexport class ParticipantTranscriptionOutput extends BaseParticipantTranscriptionOutput {\n  private writer: TextStreamWriter | null = null;\n  private flushTask: Task<void> | null = null;\n  private jsonFormat: boolean;\n  /**\n   * Per-segment markup stripping: delta streams strip incrementally (buffering a tag split\n   * across chunks); non-delta streams re-strip the full text each time and keep the latest\n   * tags in {@link segmentTags} for the expression attribute.\n   */\n  private stripper = new TranscriptMarkupStripper();\n  private segmentTags: ExpressiveTag[] = [];\n\n  constructor(\n    room: Room,\n    isDeltaStream: boolean,\n    participant: Participant | string | null,\n    options: ParticipantTranscriptionOutputOptions = {},\n  ) {\n    super(room, isDeltaStream, participant, options);\n    this.jsonFormat = options.jsonFormat ?? false;\n  }\n\n  override async captureText(text: string | TimedString) {\n    if (!this.participantIdentity) {\n      return;\n    }\n\n    if (this.flushTask && !this.flushTask.done) {\n      await this.flushTask.result;\n    }\n\n    if (!this.capturing) {\n      this.resetState();\n      this.capturing = true;\n    }\n\n    // the raw text (expressive markup intact) arrives here; publish only the visible text.\n    // Skip a chunk that strips to nothing (a partial tag still buffering, or a markup-only\n    // token) so the transcript cadence isn't disturbed. Without expressive there is no\n    // markup to remove, so the text is published exactly as it arrives.\n    const rawText = isTimedString(text) ? text.text : text;\n    let cleanText: string;\n    if (!this.expressiveEnabled()) {\n      cleanText = rawText;\n    } else if (this.isDeltaStream) {\n      cleanText = this.stripper.push(rawText);\n    } else {\n      [cleanText, this.segmentTags] = splitAllMarkup(rawText);\n      // a marker opening the segment leaves the space that followed it behind (see\n      // TranscriptMarkupStripper); this path re-strips the whole accumulation each time,\n      // so trimming the head is idempotent\n      cleanText = cleanText.replace(/^\\s+/, '');\n    }\n    if (!cleanText) {\n      return;\n    }\n\n    // latestText must hold the encoded payload so non-delta flush (FINAL=true) republishes the\n    // same newline-delimited JSON format as the interim chunks.\n    const payload = this.encode(cleanText, text);\n    this.latestText = payload;\n    await this.publish(payload);\n  }\n\n  private encode(cleanText: string, timingSrc?: string | TimedString): string {\n    if (!this.jsonFormat) {\n      return cleanText;\n    }\n    const isTimed = timingSrc !== undefined && isTimedString(timingSrc);\n    const message = new pb.TimedString({\n      text: cleanText,\n      startTime: isTimed ? timingSrc.startTime : undefined,\n      endTime: isTimed ? timingSrc.endTime : undefined,\n      confidence: isTimed ? timingSrc.confidence : undefined,\n      startTimeOffset: isTimed ? timingSrc.startTimeOffset : undefined,\n    });\n    return message.toJsonString({ useProtoFieldName: true }) + '\\n';\n  }\n\n  private async publish(payload: string): Promise<void> {\n    try {\n      if (this.room.isConnected) {\n        if (this.isDeltaStream) {\n          // reuse the existing writer\n          if (this.writer === null) {\n            // Whatever markup was stripped ahead of the first visible text goes on the\n            // opening header — a frontend can't colour the turn until the agent stops\n            // talking otherwise. The instructions ask for a leading expression marker, so\n            // this is normally already populated.\n            //\n            // If the model puts prose before its first expression marker, the tag arrives\n            // after this header and the segment carries no lk.expression: rtc-node's\n            // `TextStreamWriter.close()` takes no attributes, so unlike Python (which\n            // passes them to `aclose()`) there is no trailing header to fall back on. The\n            // same limitation is why the delta path can't send lk.transcription_final\n            // either. Audio and transcript text are unaffected — only the UI hint.\n            this.writer = await this.createTextWriter(\n              undefined,\n              expressionAttribute(this.stripper.tags),\n            );\n          }\n          await this.writer.write(payload);\n        } else {\n          const tmpWriter = await this.createTextWriter(\n            undefined,\n            expressionAttribute(this.segmentTags),\n          );\n          await tmpWriter.write(payload);\n          await tmpWriter.close();\n        }\n      }\n    } catch (error) {\n      this.logger.error(error, 'failed to publish transcription');\n    }\n  }\n\n  protected async handleCaptureText(_text: string): Promise<void> {\n    // captureText is overridden above; the base implementation is unused here.\n  }\n\n  protected handleFlush() {\n    const currWriter = this.writer;\n    this.writer = null;\n    const expressive = this.expressiveEnabled();\n    // visible text left in the strip buffer\n    const remaining = expressive && this.isDeltaStream ? this.stripper.flush() : '';\n    const tags = !expressive ? [] : this.isDeltaStream ? this.stripper.tags : this.segmentTags;\n    const pendingText = remaining ? this.encode(remaining) : '';\n    this.flushTask = Task.from((controller) =>\n      this.flushTaskImpl(currWriter, controller.signal, expressionAttribute(tags), pendingText),\n    );\n  }\n\n  protected override resetState() {\n    super.resetState();\n    this.stripper = new TranscriptMarkupStripper();\n    this.segmentTags = [];\n  }\n\n  private async createTextWriter(\n    attributes?: Record<string, string>,\n    extra?: Record<string, string>,\n  ): Promise<TextStreamWriter> {\n    if (!this.participantIdentity) {\n      throw new Error('participantIdentity not found');\n    }\n\n    if (!this.room.localParticipant) {\n      throw new Error('localParticipant not found');\n    }\n\n    if (!attributes) {\n      attributes = {\n        [ATTRIBUTE_TRANSCRIPTION_FINAL]: 'false',\n      };\n      if (this.trackId) {\n        attributes[ATTRIBUTE_TRANSCRIPTION_TRACK_ID] = this.trackId;\n      }\n    }\n    attributes[ATTRIBUTE_TRANSCRIPTION_SEGMENT_ID] = this.currentId;\n    // overlaid rather than replacing, so the caller can add a key without dropping the\n    // transcription attributes the protocol requires\n    if (extra) {\n      Object.assign(attributes, extra);\n    }\n\n    return await this.room.localParticipant.streamText({\n      topic: TOPIC_TRANSCRIPTION,\n      senderIdentity: this.participantIdentity,\n      attributes,\n    });\n  }\n\n  private async flushTaskImpl(\n    writer: TextStreamWriter | null,\n    signal: AbortSignal,\n    extraAttributes?: Record<string, string>,\n    pendingText = '',\n  ): Promise<void> {\n    const attributes: Record<string, string> = {\n      [ATTRIBUTE_TRANSCRIPTION_FINAL]: 'true',\n    };\n    if (this.trackId) {\n      attributes[ATTRIBUTE_TRANSCRIPTION_TRACK_ID] = this.trackId;\n    }\n    for (const [key, value] of Object.entries(extraAttributes ?? {})) {\n      attributes[key] ??= value;\n    }\n\n    const abortPromise = new Promise<void>((resolve) => {\n      signal.addEventListener('abort', () => resolve());\n    });\n\n    try {\n      if (this.room.isConnected) {\n        if (this.isDeltaStream) {\n          // a segment whose every chunk was held back by the stripper (a tag-shaped \"<\"\n          // never resolves) reaches flush with text but no writer — open one here rather\n          // than dropping the transcript\n          let deltaWriter: TextStreamWriter | null = writer;\n          if (!deltaWriter && pendingText) {\n            const opened = await Promise.race([this.createTextWriter(attributes), abortPromise]);\n            if (signal.aborted || !opened) {\n              return;\n            }\n            deltaWriter = opened;\n          }\n          if (deltaWriter) {\n            if (pendingText) {\n              await Promise.race([deltaWriter.write(pendingText), abortPromise]);\n            }\n            await Promise.race([deltaWriter.close(), abortPromise]);\n          }\n        } else {\n          const tmpWriter = await Promise.race([this.createTextWriter(attributes), abortPromise]);\n          if (signal.aborted || !tmpWriter) {\n            return;\n          }\n          await Promise.race([tmpWriter.write(this.latestText), abortPromise]);\n          if (signal.aborted) {\n            return;\n          }\n          await Promise.race([tmpWriter.close(), abortPromise]);\n        }\n      }\n    } catch (error) {\n      this.logger.error(error, 'failed to publish transcription');\n    }\n  }\n}\n\nexport class ParticipantLegacyTranscriptionOutput extends BaseParticipantTranscriptionOutput {\n  private pushedText: string = '';\n  private flushTask: Promise<void> | null = null;\n\n  protected async handleCaptureText(text: string): Promise<void> {\n    if (!this.trackId) {\n      return;\n    }\n\n    if (this.flushTask) {\n      await this.flushTask;\n    }\n\n    if (!this.capturing) {\n      this.resetState();\n      this.capturing = true;\n    }\n\n    if (this.isDeltaStream) {\n      this.pushedText += text;\n    } else {\n      this.pushedText = text;\n    }\n\n    // pushedText keeps the raw text (markup intact); publish the visible text only.\n    // Stripping the whole accumulation each time avoids partial-tag edge cases; the\n    // expression is dropped here — the deprecated rtc Transcription API has no attribute\n    // channel (the stream-based output carries lk.expression instead).\n    await this.publishTranscription(this.currentId, this.visibleText(), false);\n  }\n\n  /** The raw accumulation, with markup removed only when expressive could have written it. */\n  private visibleText(): string {\n    if (!this.expressiveEnabled()) return this.pushedText;\n    // trimStart: a marker opening the segment leaves the space that followed it behind\n    return stripAllMarkup(this.pushedText).replace(/^\\s+/, '');\n  }\n\n  protected handleFlush() {\n    if (!this.trackId) {\n      return;\n    }\n\n    this.flushTask = this.publishTranscription(this.currentId, this.visibleText(), true);\n    this.resetState();\n  }\n\n  async publishTranscription(id: string, text: string, final: boolean, signal?: AbortSignal) {\n    if (!this.participantIdentity || !this.trackId) {\n      return;\n    }\n\n    try {\n      if (this.room.isConnected) {\n        if (signal?.aborted) {\n          return;\n        }\n\n        await this.room.localParticipant?.publishTranscription({\n          participantIdentity: this.participantIdentity,\n          trackSid: this.trackId,\n          segments: [{ id, text, final, startTime: BigInt(0), endTime: BigInt(0), language: '' }],\n        });\n      }\n    } catch (error) {\n      this.logger.error(error, 'failed to publish transcription');\n    }\n  }\n\n  protected resetState() {\n    super.resetState();\n    this.pushedText = '';\n  }\n}\n\nexport class ParalellTextOutput extends TextOutput {\n  /** @internal */\n  _sinks: TextOutput[];\n\n  constructor(sinks: TextOutput[], nextInChain?: TextOutput) {\n    super(nextInChain);\n    this._sinks = sinks;\n  }\n\n  async captureText(text: string | TimedString) {\n    await Promise.all(this._sinks.map((sink) => sink.captureText(text)));\n  }\n\n  flush() {\n    for (const sink of this._sinks) {\n      sink.flush();\n    }\n  }\n}\n\nexport interface AudioOutputOptions {\n  sampleRate: number;\n  numChannels: number;\n  trackPublishOptions: TrackPublishOptions;\n  queueSizeMs?: number;\n}\nexport class ParticipantAudioOutput extends AudioOutput {\n  private room: Room;\n  private options: AudioOutputOptions;\n  private audioSource: AudioSource;\n  private publication?: LocalTrackPublication;\n  private flushTask?: Task<void>;\n\n  /** Duration of audio pushed to the source, in seconds */\n  private pushedDuration: number = 0;\n  private sourcePushedDuration: number = 0;\n  private sourceDiscardedDuration: number = 0;\n  private interruptionGeneration: number = 0;\n  private forwardingCount: number = 0;\n  /** Resolved only while no capture is held or being submitted to AudioSource. */\n  private forwardingIdleFuture: Future<void> = new Future();\n  private startedFuture: Future<void> = new Future();\n  private interruptedFuture: Future<void> = new Future();\n  // playbackStarted fires once per segment; a mid-segment pause/resume does not re-arm this.\n  private firstFrameEmitted: boolean = false;\n  /** Gate held closed while the output is paused; frame forwarding awaits it. */\n  private playbackEnabledFuture: Future<void> = new Future();\n  /**\n   * The playhead sits at `sourcePushedDuration` minus what the source still holds; `dryAt` is\n   * when the source runs out if nothing more is pushed, which dates a run that has ended.\n   */\n  private runOffset: number = 0;\n  private dryAt?: number;\n\n  constructor(room: Room, options: AudioOutputOptions) {\n    super(options.sampleRate, undefined, { pause: true });\n    this.room = room;\n    this.options = options;\n    this.audioSource = new AudioSource(\n      options.sampleRate,\n      options.numChannels,\n      options.queueSizeMs,\n    );\n    this.playbackEnabledFuture.resolve();\n    this.forwardingIdleFuture.resolve();\n  }\n\n  pause(): void {\n    if (this.playbackEnabledFuture.done) {\n      this.playbackEnabledFuture = new Future();\n    }\n    super.pause();\n  }\n\n  resume(): void {\n    if (!this.playbackEnabledFuture.done) {\n      this.playbackEnabledFuture.resolve();\n    }\n    super.resume();\n  }\n\n  get subscribed(): boolean {\n    return this.startedFuture.done;\n  }\n\n  /** Report the open run, and begin the next past any audio that never played. */\n  private reportRun(opts: { offset: number; endedAt: number; resumesAt?: number }): void {\n    const { offset, resumesAt } = opts;\n    // a run cannot outlast the audio the source held, however late the caller noticed\n    const endedAt = this.dryAt !== undefined ? Math.min(opts.endedAt, this.dryAt) : opts.endedAt;\n\n    const duration = offset - this.runOffset;\n    if (duration > 0) {\n      this.onPlaybackProgressed({\n        startedAt: endedAt - duration,\n        offset: this.runOffset,\n        duration,\n      });\n    }\n    this.runOffset = resumesAt ?? offset;\n    this.dryAt = undefined;\n  }\n\n  async start(signal: AbortSignal): Promise<void> {\n    await this.publishTrack(signal);\n  }\n\n  async captureFrame(frame: AudioFrame): Promise<void> {\n    const interruptionGeneration = this.interruptionGeneration;\n    if (!this.startedFuture.done) {\n      await this.startedFuture.await;\n      if (interruptionGeneration !== this.interruptionGeneration) {\n        return;\n      }\n    }\n\n    if (this.flushTask && !this.flushTask.done) {\n      this.logger.error('captureFrame called while flush is in progress');\n      await this.flushTask.result;\n    }\n    if (interruptionGeneration !== this.interruptionGeneration) {\n      return;\n    }\n\n    const segmentCapture = super.captureFrame(frame);\n    const frameDuration = frame.samplesPerChannel / frame.sampleRate;\n    this.pushedDuration += frameDuration;\n    const interruptionFuture = this.interruptedFuture;\n    this.forwardingCount++;\n    if (this.forwardingCount === 1 && this.forwardingIdleFuture.done) {\n      this.forwardingIdleFuture = new Future();\n    }\n\n    try {\n      await segmentCapture;\n      if (interruptionGeneration !== this.interruptionGeneration || interruptionFuture.done) {\n        return;\n      }\n      while (!this.playbackEnabledFuture.done) {\n        const queuedDuration = this.audioSource.queuedDuration;\n        this.sourceDiscardedDuration += queuedDuration / 1000;\n        // the dropped queue never plays, so the next run resumes past it\n        this.reportRun({\n          offset: this.sourcePushedDuration * 1000 - queuedDuration,\n          endedAt: Date.now(),\n          resumesAt: this.sourcePushedDuration * 1000,\n        });\n        this.audioSource.clearQueue();\n        await Promise.race([this.playbackEnabledFuture.await, interruptionFuture.await]);\n        if (interruptionGeneration !== this.interruptionGeneration || interruptionFuture.done) {\n          return;\n        }\n      }\n\n      if (!this.firstFrameEmitted) {\n        this.firstFrameEmitted = true;\n        this.onPlaybackStarted(Date.now());\n      }\n\n      if (this.dryAt !== undefined && Date.now() >= this.dryAt) {\n        // the source ran out before this frame arrived\n        this.reportRun({ offset: this.sourcePushedDuration * 1000, endedAt: this.dryAt });\n      }\n\n      this.sourcePushedDuration += frameDuration;\n      await this.audioSource.captureFrame(frame);\n      this.dryAt = Date.now() + this.audioSource.queuedDuration;\n    } finally {\n      this.forwardingCount--;\n      if (this.forwardingCount === 0) {\n        this.forwardingIdleFuture.resolve();\n      }\n    }\n  }\n\n  private async waitForPlayoutTask(): Promise<void> {\n    const interruptionFuture = this.interruptedFuture;\n\n    const waitForForwardingAndPlayout = async () => {\n      await this.forwardingIdleFuture.await;\n      await this.audioSource.waitForPlayout();\n    };\n\n    await Promise.race([waitForForwardingAndPlayout(), interruptionFuture.await]);\n    const interrupted = interruptionFuture.done;\n    let pushedDuration = Math.max(this.sourcePushedDuration - this.sourceDiscardedDuration, 0);\n\n    if (interrupted) {\n      const queuedDuration = this.audioSource.queuedDuration;\n      pushedDuration = Math.max(pushedDuration - queuedDuration / 1000, 0);\n      // the playhead stops where the cleared queue begins\n      this.reportRun({\n        offset: this.sourcePushedDuration * 1000 - queuedDuration,\n        endedAt: Date.now(),\n      });\n      this.audioSource.clearQueue();\n    } else {\n      // the source drained, so everything pushed has played\n      this.reportRun({ offset: this.sourcePushedDuration * 1000, endedAt: Date.now() });\n    }\n\n    this.runOffset = 0;\n    this.pushedDuration = 0;\n    this.sourcePushedDuration = 0;\n    this.sourceDiscardedDuration = 0;\n    this.firstFrameEmitted = false;\n    if (this.interruptedFuture === interruptionFuture) {\n      this.interruptedFuture = new Future();\n    }\n\n    this.onPlaybackFinished({ playbackPosition: pushedDuration, interrupted });\n  }\n\n  /**\n   * Flush any buffered audio, marking the current playback/segment as complete\n   */\n  flush(): void {\n    super.flush();\n\n    if (!this.pushedDuration) {\n      return;\n    }\n\n    if (this.flushTask && !this.flushTask.done) {\n      return;\n    }\n\n    const flushTask = Task.from(() => this.waitForPlayoutTask());\n    this.flushTask = flushTask;\n    void flushTask.result.catch(() => {});\n  }\n\n  clearBuffer(): void {\n    this.interruptionGeneration++;\n    if (\n      this.interruptedFuture.done ||\n      (this.pushedDuration === 0 && this.pendingPlayoutSegments === 0)\n    ) {\n      return;\n    }\n    if (\n      (this.pushedDuration > 0 || this.pendingPlayoutSegments > 0) &&\n      (!this.flushTask || this.flushTask.done)\n    ) {\n      this.flush();\n    }\n    if (!this.interruptedFuture.done) {\n      this.interruptedFuture.resolve();\n    }\n  }\n\n  private async publishTrack(signal: AbortSignal) {\n    const track = LocalAudioTrack.createAudioTrack('roomio_audio', this.audioSource);\n    this.publication = await this.room.localParticipant?.publishTrack(\n      track,\n      this.options.trackPublishOptions ??\n        new TrackPublishOptions({ source: TrackSource.SOURCE_MICROPHONE }),\n    );\n\n    if (signal.aborted) {\n      return;\n    }\n\n    await this.publication?.waitForSubscription();\n\n    if (!this.startedFuture.done) {\n      this.startedFuture.resolve();\n    }\n  }\n\n  async close() {\n    await this.audioSource.close();\n  }\n}\n"],"mappings":"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","names":["pb"]}