All notable changes to this project are documented here. Format follows Keep a Changelog; this project follows Semantic Versioning.
LocatorOptions.href: the resource a locate points into, taken from href-qualified textrefs (chapter.xhtml#css(...)). It’s kept on every locate, offsets entry and word boundary, so a sentence reconstructed across resources (e.g. fixed-layout pages stitched in reading order) maps each piece back to its own resource.createLocator() uses href from its options when present, instead of the current document’s.@readium/guided-navigation is now ^1.1.0.AutoPauseScope and ExtractionFormat types.verbosityPresets, languageModes, extractionFormats, autoPauseScopes, segmentationModes, rateRangeConfig, pitchRangeConfig, volumeRangeConfig, pauseDurationRangeConfig (RangeConfig), plus extractionPreferenceKeys. All are frozen.@readium/guided-navigation. makeGnd, parseMarkup, decodeTextref, the #css(...)/#domrange(...) encoders and decoders, and the GndRole, GndMediaType, GndGenerationOptions, TextrefOptions and DomRangeJSON types are no longer exported from @readium/speech; import them from @readium/guided-navigation.extractUtterances(), loadGndContent() and contextualization.params take @readium/shared’s GuidedNavigationObject instead of GndObject. GndObject, GndText and GndDocument are removed.build/; they’re imported from dependencies, with @readium/shared, @readium/decorator and @readium/helpers now as caret ranges (^2.7.0, ^1.2.5, ^1.2.0) instead of exact versions, and @readium/guided-navigation as ^1.0.1, so an app depending on them too shares one copy with this package.extractUtterances()’s options argument is now optional; omitting it uses the defaults.IEnumPreference.supportedValues is now readonly T[] and IRangePreference.supportedRange is now readonly [T, T]; they return the same frozen values the library validates against.build/index.cjs). The package is ESM-only; require("@readium/speech") still works on Node versions that support require() of ES modules.css-selector-generator dependency.TServerVoiceControls.boundary — voices now report boundary-event support (controls.boundary in JSON voice data, matching voices.schema.json), alongside the existing pitch/speed/ssml.TVoiceControls — the pitch/boundary subset of TServerVoiceControls applicable to json/browser voices.FallbackEngineProvider’s mid-playback voice matching, and WebSpeechEngine’s per-content-language voice switching (setSpeakInContentLanguage), now also prefer a voice with the same boundary-event support as the one being replaced, falling back to the wider match when none exists.ReadiumSpeechJSONVoice.pitchControl (boolean) replaced by controls.pitch, matching voices.schema.json; all bundled JSON voice data converted accordingly.ReadiumSpeechVoice.pitchControl — use controls.pitch instead.@readium/decorator@1.2.3.ExtractUtterancesOptions.segmentation — split utterances at real sentence boundaries instead of one per structural unit, reconstructing a sentence across sibling nodes when it genuinely spans them. mode: "sentence" (default "structure"), plus suppressions (per-language abbreviations to not treat as sentence endings) and segmenter (swap in a custom sentence segmenter for the built-in Intl.Segmenter-based one). See UtteranceExtraction.md.ReadiumSpeechUtterance.offsets/UtteranceOffset — the ranges of plain/ssml backed by real source text, each with its own locate; an utterance with none is what synthetic used to flag.ReadiumSpeechNavigator’s "boundary" event, previously only "word", now also fires "sentence"/"structure" (matching the active segmentation mode) whenever the current utterance changes, with detail.locate resolved to a LocatorOptions[]. resolveUtteranceLocate() (sentence/structure) and resolveBoundaryLocate() (word) are the two helpers behind this — the same ones to call yourself when driving playback without ReadiumSpeechNavigator. See Playback.md and Highlighting.md.ReadiumSpeechNavigatorConfiguration.segmentationOverrides and ISpeechPreferences.segmentation/ISpeechDefaults.segmentation — the live segmentation.mode preference, plus construction-time suppressions/segmenter.ExtractUtterancesOptions.substitutions — rewrite ASCII imitations of Unicode symbols (e.g. "1/2", "(c)") so the engine speaks the symbol, merged on top of a built-in table.makeGnd()/parseMarkup()’s textrefs.roles accepts "leaf-text", matching a roleless block that owns its own text directly (e.g. a <div> standing in for <p>). See GuidedNavigation.md.WebSpeechEngine no longer resolves a stray/delayed boundary event from an already-cancelled utterance against whatever utterance is current by the time it arrives.WebSpeechEngine no longer emits a duplicate start/pause/resume event for the same playback action — previously fired once eagerly and again from the native SpeechSynthesisUtterance event, which also doubled ReadiumSpeechNavigator’s synthesized boundary event on start.WebSpeechEngine/SpeechServerEngine now has angle brackets neutralized so plain text containing </> can’t be misread as markup.makeGnd()/parseMarkup()) no longer use attribute selectors, which could latch onto a JS-mutated attribute (e.g. an inline style set by a layout script) and silently stop matching once it changed.ReadiumSpeechUtterance.synthetic — an empty/absent offsets is now what marks an utterance as synthesized rather than sourced.endnotes section no longer skips its own location — previously it was suppressed there unconditionally (since a noteref already speaks it inline), leaving the section’s list silent.noteref no longer merges its Start of the footnote./End of the footnote. contextualization into the same utterance as its content — the note’s real content is now its own separate utterance, consistent with every other block role, instead of being incorrectly flagged synthetic. With inlineContextualization: true, the note is still spoken inline as one merged utterance.biblioref and glossary added to skippableRoles and to skippedAtVerbosity’s none/few/some presets — both are now skippable and, under a preset, extracted only at most.ExtractUtterancesOptions.contextualizationLocale — pick which shipped catalog locale to use.ExtractUtterancesOptions.contextualization — for a custom or overridden catalog: override wording, shape, and params per call.ReadiumSpeechNavigatorConfiguration.contextualizationOverrides — the same overrides, set once for a navigator.ReadiumSpeechUtterance.locate — options for createLocator() to build a locator for the utterance’s source.ReadiumSpeechUtterance.synthetic — true when the utterance’s text isn’t copied from the source, so word-level highlighting can be skipped (element-level highlighting via locate still works).makeGnd()/parseMarkup() can generate textrefs from a live document. See GuidedNavigation.md.cell/rowheader/row contextualizations carry positional context (column header, row number).extractUtterances(), extractUtterancesWithSources(), loadGndContent(), and submitPreferences() are now async.LocatorOptions restructured around text/cssSelector/domRange/fragment.skippableAtVerbosity renamed to skippedAtVerbosity.setRate()/setPitch()/setVolume() now restart the current utterance so changes apply immediately, not on the next one.<th> resolves to columnheader/rowheader instead of always cell.setRate()/setPitch()/setVolume() and playback-rate clamping no longer propagate NaN/±Infinity.defaultAnnouncements, Announcement, AnnouncementKey, AnnouncementPair, Announcements, RoleAnnouncement, isAnnouncementPair — superseded by the JSON contextualization catalog (defaultContextualizations, Contextualizations, ContextualizationEntry).ExtractUtterancesOptions.announcements — superseded by contextualization.contextualizations.FallbackSpeechEngine no longer picks a fallback voice that requires network access unless navigator.onLine is confirmed true, defaulting to offline-only when connectivity is unknown — previously it could pick an online voice while swapping away from a recoverable failure, risking the same failure again.FallbackEngineProvider / FallbackSpeechEngine, wrapping a primary and a fallback ReadiumSpeechEngineProvider as one. onFailure (default "fallback") controls the behavior: "fallback" swaps to the fallback on a recoverable playback failure (mid-playback included) and stays there; "fallbackAndRecover" additionally polls the primary and swaps back once it’s reachable again; "error" never swaps, surfacing failures as-is. No registry involved. See FallbackEngine.md."enginefallback" / "enginerecovered" events on ReadiumSpeechPlaybackEvent, emitted by FallbackSpeechEngine when it swaps engines in either direction.ReadiumSpeechPlaybackEngine.loadUtterances() takes an optional startIndex hint, so an engine that pre-buffers ahead of playback (e.g. SpeechServerEngine) warms the utterances playback will actually resume at instead of always index 0.ReadiumSpeechEngineProvider.getVoices() takes an optional forceRefresh parameter to bypass a provider’s own voice cache — used by FallbackSpeechEngine’s health check so a stale cache can’t report the primary reachable when it isn’t.SpeechServerEngineOptions.timeoutMs, a grace period tolerated after the playback buffer is projected to run dry before a still-pending /synthesize chunk is declared stalled (SpeechServerStallError, 408) and aborted. Default undefined: a stalled request waits forever, same as before this option existed. See Stall detection.SpeechServerNetworkError, thrown when a request to the server never reaches the network at all — distinguishes an actual connectivity failure from an unrelated TypeError thrown later while reading a response that did arrive.ErrorEventDetail, the { message, recoverable, ... } shape an engine’s "error" event detail is expected to follow.SpeechServerAudioDecodeError, thrown when a /synthesize response arrives and parses fine but its audio payload fails to decode.recoverable on every SpeechServerEngine error event’s detail: true when the server never responded at all (network failure or a stall), false when it responded but rejected the request or the audio payload failed to decode. Used by FallbackSpeechEngine to decide whether swapping engines could help.SpeechServerEngine’s "error" event detail always has at least { message, recoverable } now; { status, type, title, instance } is only present for a Problem Details response from the server.SpeechServerEngine catches exceptions thrown by its own event listeners instead of letting one throw out of emitEvent() and abort the others.WebSpeechEngine.loadUtterances() called back-to-back while already "ready" now emits "ready" each time — it previously went through the same diff-based state setter as everything else, which swallowed a same-state transition.SpeechServerEngine/SpeechServerEngineProvider no longer double-apply speed on voices whose provider already applies it server-side (e.g. ElevenLabs). speech-server stopped sending controls per voice on GET /voices, moving it to GET /service’s providers[] only — voice mapping now merges each voice’s controls from there instead of a field the server no longer sends.autoPause preference ("none" | "utterance" | "block", default "none") — fully pauses playback between utterances or blocks (fires pause, navigator.getState() becomes "paused") instead of continuing on its own; playback resumes only when play() is called. See Preferences.md.SpeechDefaults.language default is now "block-level", was "always".ReadiumSpeechUtterance drops startsNewBlock. Block-boundary info is now internal to extraction — autoPause: "block" only has effect on content loaded via loadGndContent(); content loaded via loadContent() never triggers it.pauseDuration always delays the next automatic continuation now that pauseScope is gone — it’s no longer scoped to a subset of transitions.SpeechPreferences.pauseScope / SpeechDefaults.pauseScope, added in 0.6.0. It was meant to implement automatic pausing between utterances/paragraphs but only ever delayed an automatic continuation rather than actually pausing — replaced by autoPause.ReadiumSpeechNavigator (submitPreferences(), settings, preferencesEditor), covering verbosity presets and prosody (pauseDuration, pauseScope, rate, pitch, volume). See Preferences.md.ReadiumSpeechNavigator.loadGndContent(), retaining a Guided Navigation tree so preferences can re-run extraction on it.ReadiumSpeechUtterance.startsNewBlock, marking utterances that begin a new block-level element — used by pauseScope: "block".ExtractUtterancesOptions.contextualize is now GndRole[] (which roles announce), replacing the previous on/off boolean; default is now nothing contextualized.ExtractUtterancesOptions.interruptSentence renamed to inlineContextualization.ReadiumSpeechNavigatorContract drops setRate/getRate/setPitch/getPitch/setVolume/getVolume — use the Preferences API instead.utterances.json now pairs one result with every option-set that produces it (options is an array), instead of repeating identical results per option-set — see fixtures/README.md.SpeechServerEngine / SpeechServerEngineProvider, a second ReadiumSpeechPlaybackEngine implementation backed by a Readium Speech Server HTTP service — with utterance prefetching, chunk-streaming for long/over-limit utterances, gapless Web Audio API scheduling, format/bitrate selection, and RFC 9457 error handling. See SpeechServerEngine.md.ReadiumSpeechProviderRegistry, for registering multiple ReadiumSpeechEngineProviders (e.g. WebSpeech and speech-server) side by side and querying voices/creating engines across all of them. See ProviderRegistry.md.ReadiumSpeechVoice.identifier and ReadiumSpeechVoice.controls (which playback controls a server-sourced voice actually honors), and "server" added to TSource.ReadiumSpeechNavigator (renamed from WebSpeechReadAloudNavigator) is now engine-agnostic: its constructor requires an explicit ReadiumSpeechPlaybackEngine (e.g. new ReadiumSpeechNavigator(new WebSpeechEngine())), instead of defaulting to WebSpeech.WebSpeechEngineProvider.getVoices() no longer requires an engine to have been created first.setSpeakInContentLanguage(enabled) / getSpeakInContentLanguage() on ReadiumSpeechNavigator and ReadiumSpeechPlaybackEngine: when enabled, each utterance is spoken in the best available voice for its own language, falling back to the selected/default voice when none matches (never racing ahead of voice loading and picking the wrong-language voice)."languagefallback" playback event, fired when no voice matches an utterance’s content language (detail: { language, reason: "no-matching-voice" }).WebSpeechVoiceManager.initialize({ languages }) can now be called again on an already-initialized singleton to broaden it to additional languages, instead of being silently ignored.makeGnd, extracting a Guided Navigation (GND) JSON tree from HTML/XHTML.extractUtterances, walking a GND tree into a flat, ordered list of ReadiumSpeechUtterances.@readium/decorator for highlighting, via a new wrapper API that simplifies common usage (including Locator handling).ReadiumSpeechVoice is now fully typed, dropping the any fallback (#48).textContent coercion in demos; README quickstart example.Initial release.