On this page
Overview
Preserves exact nonblank stored segment text; removes repeated formatting marks from only each outbound synthesis copy; 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 clones that part, removes repeated formatting marks from only the outbound input, 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
import * as module from '/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, creates indexed Blob URLs, and autoplays when allowed. A fetchTTS 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. Native and custom clients without that advertised capacity retain serialized synthesis and one-segment lookahead. Pause and Resume keep the same audio element. Replay preserves completed URLs and active provider work while retrying only failed missing provider segments. cancel/stop abort every owned request and release URLs; destroy() emits its final cancellation state, removes listeners, disposes the event source, and neutralizes the callback.
Application-facing behavior: 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.fetchTTS plus providerRuntime TTS execution capacity, or compatible serialized Arcane.speech.synthesize; globalThis.arcaneEvents, Blob URLs, audio element This detail does not widen the application-facing API or grant authority.
Exports, signatures, parameters, and results
| Binding | Form | Declaration or signature | Parameter syntax |
|---|---|---|---|
SPEECH_PLAYBACK_STATE_EVENT | variable · value | variable SPEECH_PLAYBACK_STATE_EVENT | — |
SPEECH_VOICE_ALIASES | variable · value | variable SPEECH_VOICE_ALIASES | — |
SPEECH_VOICE_OPTIONS | variable · value | variable SPEECH_VOICE_OPTIONS | — |
SpeechPlayback | class | class SpeechPlayback | — |
splitSpeechText | function | function splitSpeechText(value='') | value='' |
default | default · class | class SpeechPlayback | — |
| Callable | Kind | Exact public signature | Parameter syntax |
|---|---|---|---|
splitSpeechText | exported-function | function splitSpeechText(value='') | value='' |
| Member | Kind | Exact public declaration | Parameter syntax |
|---|---|---|---|
SpeechPlayback.constructor | constructor | constructor({
audio,
speech=globalThis.Arcane?.speech,
model=null,
voice=null,
responseFormat=null,
speed=1,
onState=function noop(){},
createObjectURL,
revokeObjectURL,
delay,
messages={}
}={}) | {
audio,
speech=globalThis.Arcane?.speech,
model=null,
voice=null,
responseFormat=null,
speed=1,
onState=function noop(){},
createObjectURL,
revokeObjectURL,
delay,
messages={}
}={} |
SpeechPlayback.synthesisInFlight | get | get synthesisInFlight() | — |
SpeechPlayback.message | method | message(name,details={}) | name,details={} |
SpeechPlayback.nextOperationId | method | nextOperationId() | — |
SpeechPlayback.emit | method | emit(state,message,key=this.key,{code=null,reason=null}={}) | state,message,key=this.key,{code=null,reason=null}={} |
SpeechPlayback.available | method | available() | — |
SpeechPlayback.hasAudio | method | hasAudio(key=this.key) | key=this.key |
SpeechPlayback.releaseURLs | method | releaseURLs() | — |
SpeechPlayback.releaseUrls | method | releaseUrls() | — |
SpeechPlayback.cancel | method | cancel(
message=this.message('idle'),
reason='speech-playback-cancelled'
) |
message=this.message('idle'),
reason='speech-playback-cancelled'
|
SpeechPlayback.requestSpeech | async method | async requestSpeech(part,signal) | part,signal |
SpeechPlayback.synthesizeSegment | async method | async synthesizeSegment(
index,
generation,
announce=false,
providerSynthesisBatch=null
) |
index,
generation,
announce=false,
providerSynthesisBatch=null
|
SpeechPlayback.startLookahead | method | startLookahead(index,generation=this.generation) | index,generation=this.generation |
SpeechPlayback.waitForSegment | async method | async waitForSegment(index,generation) | index,generation |
SpeechPlayback.waitForPause | async method | async waitForPause(duration,generation) | duration,generation |
SpeechPlayback.prepare | async method | async prepare({
key,
parts,
model=this.model,
voice=this.voice,
responseFormat=this.responseFormat,
speed=this.speed,
autoplay=true
}={}) | {
key,
parts,
model=this.model,
voice=this.voice,
responseFormat=this.responseFormat,
speed=this.speed,
autoplay=true
}={} |
SpeechPlayback.loadCurrent | method | loadCurrent() | — |
SpeechPlayback.play | async method | async play() | — |
SpeechPlayback.restart | async method | async restart() | — |
SpeechPlayback.replay | method | replay() | — |
SpeechPlayback.togglePause | async method | async togglePause() | — |
SpeechPlayback.stop | method | stop() | — |
SpeechPlayback.advance | async method | async advance() | — |
SpeechPlayback.handleEnded | async method | async handleEnded() | — |
SpeechPlayback.handlePlay | method | handlePlay() | — |
SpeechPlayback.handlePause | method | handlePause() | — |
SpeechPlayback.handleError | method | handleError() | — |
SpeechPlayback.fail | method | fail(error) | error |
SpeechPlayback.destroy | method | destroy() | — |
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. requestSpeech() clones that stored part, removes repeated same formatting marks from only the outbound input, 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?,speed=1,onState=()=>{},createObjectURL?,revokeObjectURL?,delay?,messages?}); prepare({key,parts,model?,voice?,responseFormat?,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.
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_MISMATCHARCANE_SPEECH_PLAYBACK_OPERATION_SEQUENCE_EXHAUSTEDARCANE_SPEECH_PLAYBACK_SYNTHESIZER_UNAVAILABLEARCANE_SPEECH_PLAYBACK_DESTROYEDARCANE_SPEECH_PLAYBACK_SYNTHESIZER_UNAVAILABLEARCANE_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 clones that part, removes repeated formatting marks from only the outbound input, 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
import SpeechPlayback,{SPEECH_VOICE_OPTIONS} from '/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
});
});