Capability first · transport second

SpeechPlayback.js

Preserves exact nonblank stored segment text; removes repeated formatting marks from the outbound synthesis copy by default or forwards exact input with constructor/prepare textFormat:'plain'; eagerly submits complete parts to a capacity-advertising fetchTTS provider while keeping playback indexed; and retains serialized one-segment lookahead for other clients.

SDK 0.97.1Runtime 0.8.12Protocol arcane/1
On this page

Overview

Preserves exact nonblank stored segment text; removes repeated formatting marks from the outbound synthesis copy by default or forwards exact input with constructor/prepare textFormat:'plain'; eagerly submits complete parts to a capacity-advertising fetchTTS provider while keeping playback indexed; and retains serialized one-segment lookahead for other clients.

  • Artifact
    SpeechPlayback.js · esm
  • Classification
    public first party
  • Availability
    Browser + compatible AI/native bridge
  • Normalization
    Exact nonblank split/part input strings are preserved in mutable records. Each synthesis request copies that input, preserves it exactly for constructor/prepare textFormat:'plain' or applies default formatting cleanup, and carries SDK-internal preparation metadata to prevent a second cleanup pass. A capable provider receives complete segments immediately and owns bounded FIFO admission; URLs and playback stay in exact input order. Native/custom clients remain serialized. Canonical state dispatch precedes the optional synchronous onState callback. Cancellation, Replay recovery, Blob results, and lifecycle state are normalized; provider failures remain external.

Import and lifecycle

JavaScript
import * as module from '/node_modules/arcane-os/runtime/arcane/modules/SpeechPlayback.js';

Construction attaches ended/play/pause/error listeners to the supplied audio element and retains the optional onState callback. prepare() cancels prior work, retains indexed Blob URLs or native descriptors, and autoplays when allowed. A fetchTTS or prepareTTSPlayback client with positive providerRuntime TTS execution capacity receives every complete part immediately; its provider queue owns bounded FIFO admission while playback waits for exact input order. Clients without that advertised capacity retain serialized synthesis and one-segment lookahead. Pause and Resume retain the current audio element or native playback control. Replay preserves completed segments and active provider work while retrying only failed missing provider segments. cancel/stop abort every owned request and release URLs; failed native cancellation retains control until actual release. destroy() emits its final cancellation state, removes listeners, disposes the event source, and neutralizes the callback.

Application-facing behavior: prepareTTSPlayback or direct native provider.prepare may return silent native-speech descriptors. autoplay:false stays silent; actual completion advances ordered parts and failed cancellation retains controls until released. Current native finished:false without error reports ready with reason native-playback-stopped; natural final completion reports ended. Replay preserves completed segments and retries only missing failed generation. SpeechPlayback class/default, mutable SPEECHVOICEOPTIONS records, mutable SPEECHVOICEALIASES membership, SPEECHPLAYBACKSTATEEVENT, optional constructor onState(detail), splitSpeechText(), canonical playback lifecycle events, cancellation, and destroy APIs.

Protocol and host implementation

AI.prepareTTSPlayback or AI.fetchTTS plus providerRuntime TTS execution capacity, native provider.prepare, or compatible serialized Arcane.speech.synthesize; globalThis.arcaneEvents, Blob URLs/audio element, and native playback controls This detail does not widen the application-facing API or grant authority.

Exports, signatures, parameters, and results

BindingFormDeclaration or signatureParameter syntax
SPEECH_PLAYBACK_STATE_EVENTvariable · valuevariable SPEECH_PLAYBACK_STATE_EVENT—
SPEECH_VOICE_ALIASESvariable · valuevariable SPEECH_VOICE_ALIASES—
SPEECH_VOICE_OPTIONSvariable · valuevariable SPEECH_VOICE_OPTIONS—
SpeechPlaybackclassclass SpeechPlayback—
splitSpeechTextfunctionfunction splitSpeechText(value='')value=''
defaultdefault · classclass SpeechPlayback—
CallableKindExact public signatureParameter syntax
splitSpeechTextexported-functionfunction splitSpeechText(value='')value=''
MemberKindExact public declarationParameter syntax
SpeechPlayback.constructorconstructorconstructor({ audio, speech=globalThis.Arcane?.speech, model=null, voice=null, responseFormat=null, textFormat=null, speed=1, onState=function noop(){}, createObjectURL, revokeObjectURL, delay, messages={} }={}){ audio, speech=globalThis.Arcane?.speech, model=null, voice=null, responseFormat=null, textFormat=null, speed=1, onState=function noop(){}, createObjectURL, revokeObjectURL, delay, messages={} }={}
SpeechPlayback.synthesisInFlightgetget synthesisInFlight()—
SpeechPlayback.messagemethodmessage(name,details={})name,details={}
SpeechPlayback.nextOperationIdmethodnextOperationId()—
SpeechPlayback.emitmethodemit(state,message,key=this.key,{code=null,reason=null}={})state,message,key=this.key,{code=null,reason=null}={}
SpeechPlayback.availablemethodavailable()—
SpeechPlayback.hasAudiomethodhasAudio(key=this.key)key=this.key
SpeechPlayback.hasSegmentmethodhasSegment(index)index
SpeechPlayback.retainSegmentmethodretainSegment(index,outcome)index,outcome
SpeechPlayback.discardSegmentmethoddiscardSegment(outcome)outcome
SpeechPlayback.releaseURLsmethodreleaseURLs()—
SpeechPlayback.releaseUrlsmethodreleaseUrls()—
SpeechPlayback.cancelmethodcancel( message=this.message('idle'), reason='speech-playback-cancelled' ) message=this.message('idle'), reason='speech-playback-cancelled'
SpeechPlayback.requestSpeechasync methodasync requestSpeech(part,signal)part,signal
SpeechPlayback.synthesizeSegmentasync methodasync synthesizeSegment( index, generation, announce=false, providerSynthesisBatch=null ) index, generation, announce=false, providerSynthesisBatch=null
SpeechPlayback.startLookaheadmethodstartLookahead(index,generation=this.generation)index,generation=this.generation
SpeechPlayback.waitForSegmentasync methodasync waitForSegment(index,generation)index,generation
SpeechPlayback.waitForPauseasync methodasync waitForPause(duration,generation)duration,generation
SpeechPlayback.prepareasync methodasync prepare({ key, parts, model=this.model, voice=this.voice, responseFormat=this.responseFormat, textFormat=this.textFormat, speed=this.speed, autoplay=true }={}){ key, parts, model=this.model, voice=this.voice, responseFormat=this.responseFormat, textFormat=this.textFormat, speed=this.speed, autoplay=true }={}
SpeechPlayback.loadCurrentmethodloadCurrent()—
SpeechPlayback.playasync methodasync play()—
SpeechPlayback.stopNativePlaybackmethodstopNativePlayback()—
SpeechPlayback.playNativemethodplayNative()—
SpeechPlayback.restartasync methodasync restart()—
SpeechPlayback.replaymethodreplay()—
SpeechPlayback.togglePauseasync methodasync togglePause()—
SpeechPlayback.stopmethodstop()—
SpeechPlayback.advanceasync methodasync advance()—
SpeechPlayback.handleEndedasync methodasync handleEnded()—
SpeechPlayback.handlePlaymethodhandlePlay()—
SpeechPlayback.handlePausemethodhandlePause()—
SpeechPlayback.handleErrormethodhandleError()—
SpeechPlayback.failmethodfail(error)error
SpeechPlayback.destroymethoddestroy()—

Parameter meanings and results

SPEECH_VOICE_OPTIONS is an ordered mutable compatibility array of ten {value,label} records, and SPEECH_VOICE_ALIASES is the mutable Set of those values; SpeechPlayback does not select either catalog. splitSpeechText(value) returns [] for blank text or a mutable one-item array containing the exact input without trimming, splitting, or freezing it. prepare() preserves each nonblank part's exact input string in a new mutable record. Constructor/prepare textFormat:'plain' captures exact-input selection on every prepared part and forwards it unchanged; omitted prepare selection reuses the instance setting, and explicit null or other non-undefined values select default cleanup. requestSpeech() copies that stored input, removes repeated same formatting marks only under default cleanup, adds textFormat:'plain' only to explicitly plain payloads, and passes SDK-internal {speechInputPrepared:true} outside the payload so downstream SDK boundaries do not filter the copy twice; original part objects and every non-input payload field remain unchanged. new SpeechPlayback({audio,speech?,model?,voice?,responseFormat?,textFormat?,speed=1,onState=()=>{},createObjectURL?,revokeObjectURL?,delay?,messages?}); prepare({key,parts,model?,voice?,responseFormat?,textFormat?,speed?,autoplay=true}) resolves {ready,played}; available(), hasAudio(), play(), restart()/replay(), togglePause(), advance(), stop(), cancel(), releaseURLs(), destroy(). Browser/provider eager admission requires fetchTTS(payload,signal) plus providerRuntime.status('tts',{execution:true}).execution.maxConcurrentRequests as a positive safe integer; the provider, not SpeechPlayback, enforces that bound. SpeechPlayback supplies both fetchTTS and serialized synthesize clients a third SDK-internal preparation object, which existing two-argument clients may ignore. Otherwise admission remains serialized. Omitted model, voice, and response format remain caller/catalog-owned. Exports the two voice compatibility catalogs and playback-state event. Prefers prepareTTSPlayback returning Blob or an inert native-speech descriptor; direct native provider.prepare is also supported. Native descriptors stay separate from Blob URLs and autoplay false remains silent. Actual native completion advances indexed parts; native pause/resume/stop/restart/dispose retain resource ownership through released even when finished reports an earlier control error. Stale completion never advances replacement content. Current native finished:false without error keeps ready with reason native-playback-stopped; initial ready has no such reason, and natural final completion emits ended. An error does not prove native resources released.

Events, side effects, and errors

Source-literal CustomEvent dispatches

No source-literal CustomEvent dispatch is part of this artifact.

Lifecycle and event flow

  • SPEECH_PLAYBACK_STATE_EVENT publishes mutable details with idle, synthesizing, ready, playing, paused, pausing, buffering, ended, and error states, then optional onState(detail) runs synchronously with the same public field values. Object identity between the canonical occurrence detail and callback detail is not promised.
  • Consumes audio ended/play/pause/error events.

Direct coded failures

  • ARCANE_SPEECH_PLAYBACK_SYNTHESIZED_AUDIO_CONTRACT_MISMATCH
  • ARCANE_SPEECH_PLAYBACK_OPERATION_SEQUENCE_EXHAUSTED
  • ARCANE_SPEECH_PLAYBACK_SYNTHESIZER_UNAVAILABLE
  • ARCANE_SPEECH_PLAYBACK_DESTROYED
  • ARCANE_SPEECH_PLAYBACK_SYNTHESIZER_UNAVAILABLE
  • ARCANE_SPEECH_PLAYBACK_AUDIO_PLAYBACK_REJECTED

Exported Error subclasses

This artifact exports no Error subclass.

Documented failure behavior

  • TypeError or RangeError for missing audio or invalid/blank speech input, voice, speed, or pause. An onState callback failure is reported through globalThis.reportError or console.error without replacing playback settlement.
  • Every first-segment provider rejection, including a falsy rejection value, propagates from prepare() while state becomes error; later indexed failure stops ordered playback without skipping and can be retried by Replay. Autoplay rejection becomes played:false.

Availability and capabilities

Browser + compatible AI/native bridge. Exact nonblank split/part input strings are preserved in mutable records. Each synthesis request copies that input, preserves it exactly for constructor/prepare textFormat:'plain' or applies default formatting cleanup, and carries SDK-internal preparation metadata to prevent a second cleanup pass. A capable provider receives complete segments immediately and owns bounded FIFO admission; URLs and playback stay in exact input order. Native/custom clients remain serialized. Canonical state dispatch precedes the optional synchronous onState callback. Cancellation, Replay recovery, Blob results, and lifecycle state are normalized; provider failures remain external.

Arcane.speech.synthesize uses ai.inference and remains serialized unless the supplied client explicitly advertises compatible fetchTTS provider execution capacity. Blob/audio playback still follows browser media and user-gesture policy.

Contract example

JavaScript
import SpeechPlayback,{SPEECH_VOICE_OPTIONS} from '/node_modules/arcane-os/runtime/arcane/modules/SpeechPlayback.js';

const audio = document.body.appendChild(document.createElement('audio'));
audio.controls = true;
console.log(SPEECH_VOICE_OPTIONS[0]);
const speech = new SpeechPlayback({
    audio,
    speech:globalThis.ai,
    onState(detail){console.log(detail.state);}
});
const button = document.body.appendChild(document.createElement('button'));
button.textContent = 'Speak';
button.addEventListener('click', async function speakCompleteSegments() {
    await globalThis.ai.setSpeechMuted(false);
    await speech.prepare({
        parts:['First complete segment.','Second complete segment.'],
        autoplay:true
    });
});