@readium/speech 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -11
- package/build/WebSpeech/webSpeechEngine.d.ts +1 -1
- package/build/gnd/a11y.d.ts +18 -0
- package/build/gnd/converter.d.ts +52 -0
- package/build/gnd/dom.d.ts +5 -0
- package/build/gnd/index.d.ts +4 -0
- package/build/gnd/makeGnd.d.ts +8 -0
- package/build/gnd/object.d.ts +24 -0
- package/build/gnd/roles.d.ts +8 -0
- package/build/gnd/text.d.ts +18 -0
- package/build/gnd/types.d.ts +21 -0
- package/build/index.cjs +35 -35
- package/build/index.d.ts +2 -0
- package/build/index.js +2530 -1420
- package/build/utils/text.d.ts +4 -0
- package/build/utterance.d.ts +2 -2
- package/build/utterances/announcements.d.ts +2 -0
- package/build/utterances/extractUtterances.d.ts +12 -0
- package/build/utterances/index.d.ts +4 -0
- package/build/utterances/language.d.ts +1 -0
- package/build/utterances/roles.d.ts +2 -0
- package/build/utterances/text.d.ts +54 -0
- package/build/utterances/types.d.ts +18 -0
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -36,20 +36,28 @@ We are now focused on the fourth phase: extracting [Guided Navigation objects](h
|
|
|
36
36
|
|
|
37
37
|
## Demos
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
1. [Voice selection with playback demo](https://readium.org/speech/demo)
|
|
42
|
-
2. [In-context demo](https://readium.org/speech/demo/article)
|
|
43
|
-
|
|
44
|
-
The first demo showcases the following features:
|
|
39
|
+
### [Voice selection with playback demo](https://readium.org/speech/demo)
|
|
45
40
|
|
|
46
41
|
- fetching a list of all available languages, translating them to the user's locale and sorting them based on these translations
|
|
47
42
|
- returning a list of voices for a given language, grouped by region and sorted based on quality
|
|
48
43
|
- filtering languages and voices based on gender and offline availability
|
|
49
44
|
- using embedded test utterances to demo voices
|
|
50
45
|
- using the current Navigator for playback control
|
|
46
|
+
- highlighting: as playback progresses, the current word/sentence is highlighted
|
|
47
|
+
|
|
48
|
+
### [In-context demo](https://readium.org/speech/demo/article)
|
|
49
|
+
|
|
50
|
+
In-context reading with seamless voice selection (grouped by region and sorted based on quality), and playback control, providing an optional read-along experience that integrates naturally with the content. Also showcases highlighting, as above.
|
|
51
|
+
|
|
52
|
+
### [Extraction playground](https://readium.org/speech/demo/playground)
|
|
51
53
|
|
|
52
|
-
|
|
54
|
+
Pick a sample piece of markup and watch it move through the whole pipeline:
|
|
55
|
+
|
|
56
|
+
- the [Guided Navigation](docs/GuidedNavigation.md) document it produces
|
|
57
|
+
- the [utterances](docs/UtteranceExtraction.md) extracted from that, with every extraction option (format, language handling, skipped roles, sentence interruption, contextualization) adjustable live
|
|
58
|
+
- playback of the resulting utterances through the WebSpeech navigator, with word-boundary highlighting
|
|
59
|
+
|
|
60
|
+
The sample markup is drawn from this project's own conformance test suite ([`fixtures/`](fixtures/README.md)), so each pane also shows a pass/fail badge against that suite's expected output — a side effect of reusing real test content, not the point of the demo.
|
|
53
61
|
|
|
54
62
|
## Installation
|
|
55
63
|
|
|
@@ -127,6 +135,8 @@ Documentation provides guides for:
|
|
|
127
135
|
- [Voice Management](docs/VoiceManagement.md)
|
|
128
136
|
- [Playback API](docs/Playback.md)
|
|
129
137
|
- [Highlighting](docs/Highlighting.md)
|
|
138
|
+
- [Guided Navigation](docs/GuidedNavigation.md) — extracting [Guided Navigation objects](https://readium.org/guided-navigation) from HTML/XHTML content
|
|
139
|
+
- [Utterance Extraction](docs/UtteranceExtraction.md) — extracting utterances from Guided Navigation objects
|
|
130
140
|
|
|
131
141
|
## Development
|
|
132
142
|
|
|
@@ -148,7 +158,7 @@ This will compile the TypeScript code and generate the following outputs in the
|
|
|
148
158
|
|
|
149
159
|
### Running Demos Locally
|
|
150
160
|
|
|
151
|
-
The project includes
|
|
161
|
+
The project includes demo applications that can be served locally:
|
|
152
162
|
|
|
153
163
|
1. Start the local development server:
|
|
154
164
|
```bash
|
|
@@ -158,6 +168,7 @@ The project includes two demo applications that can be served locally:
|
|
|
158
168
|
2. Open your browser to:
|
|
159
169
|
- [Voice selection demo](http://localhost:8080/demo)
|
|
160
170
|
- [In-context reading demo](http://localhost:8080/demo/article)
|
|
171
|
+
- [Extraction playground](http://localhost:8080/demo/playground)
|
|
161
172
|
|
|
162
173
|
### ChromeOS Debugging
|
|
163
174
|
|
|
@@ -167,12 +178,19 @@ For ChromeOS development, the project includes a debug mode that mocks the Web S
|
|
|
167
178
|
|
|
168
179
|
2. The debug page loads mock voices from a json file which contains a snapshot of ChromeOS voices.
|
|
169
180
|
|
|
170
|
-
###
|
|
181
|
+
### Testing
|
|
182
|
+
|
|
183
|
+
`npm test` builds the library and runs the full test suite (`ava`) across `test/**/*.test.ts`. Narrower scripts are available for working on one area at a time:
|
|
171
184
|
|
|
172
|
-
To run the test suite for `WebSpeechVoiceManager`:
|
|
173
185
|
```bash
|
|
174
|
-
npm test
|
|
186
|
+
npm test # build + full suite
|
|
187
|
+
npm run test:voices # WebSpeechVoiceManager only
|
|
188
|
+
npm run test:gnd # HTML/XHTML -> Guided Navigation conversion
|
|
189
|
+
npm run test:utterances # Guided Navigation -> utterance extraction
|
|
175
190
|
```
|
|
191
|
+
|
|
192
|
+
`test:gnd` and `test:utterances` are both driven by [`fixtures/`](fixtures/README.md), a language-agnostic conformance suite of paired input/expected-output files (`input.html`/`input.xhtml`, `gnd.json`, `utterances.json`) covering the [Guided Navigation](docs/GuidedNavigation.md) and [utterance extraction](docs/UtteranceExtraction.md) stages one role/encoding/option at a time. Each fixture is a plain-file test case any platform implementation can consume, not just this TypeScript one — see [fixtures/README.md](fixtures/README.md) for the format, how to add a fixture, and how a fixture "passes".
|
|
193
|
+
|
|
176
194
|
## Acknowledgments
|
|
177
195
|
|
|
178
196
|
This project is based on the work done initially by [Hadrien Gardeur](https://github.com/hadriengardeur) in the [web-speech-recommended-voices](https://github.com/HadrienGardeur/web-speech-recommended-voices) repository.
|
|
@@ -38,7 +38,7 @@ export declare class WebSpeechEngine implements ReadiumSpeechPlaybackEngine {
|
|
|
38
38
|
private validateText;
|
|
39
39
|
private getCurrentVoiceForUtterance;
|
|
40
40
|
getCurrentVoice(): ReadiumSpeechVoice | null;
|
|
41
|
-
private
|
|
41
|
+
private toPlainText;
|
|
42
42
|
loadUtterances(contents: ReadiumSpeechUtterance[]): void;
|
|
43
43
|
setVoice(voice: ReadiumSpeechVoice | string): Promise<void>;
|
|
44
44
|
getAvailableVoices(): Promise<ReadiumSpeechVoice[]>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { GndText } from './types.js';
|
|
2
|
+
/** Normalized (whitespace-coalesced and trimmed) text content of a node's subtree. */
|
|
3
|
+
export declare function normalizedNodeText(el: Node): string;
|
|
4
|
+
/**
|
|
5
|
+
* Computes the text that becomes a node's `GndObject.description`, and
|
|
6
|
+
* whether the node is visible in the first place. Follows the AccName
|
|
7
|
+
* precedence order (https://www.w3.org/TR/accname/#terminology, 2.A-2.D)
|
|
8
|
+
* for its accessible-name sources, with a non-AccName `aria-describedby`
|
|
9
|
+
* fallback spliced in between 2.C and 2.D — see that branch below.
|
|
10
|
+
*/
|
|
11
|
+
export declare function extractNodeAria(el: Element): [GndText | null, boolean];
|
|
12
|
+
/**
|
|
13
|
+
* Maps an HTML element to the SSML tag its text should be wrapped in.
|
|
14
|
+
* https://www.w3.org/TR/speech-synthesis11/#S3.2.2
|
|
15
|
+
*/
|
|
16
|
+
export declare function convertElementToSSMLTag(tagName: string): [string, Record<string, string>?];
|
|
17
|
+
/** Elements whose entire subtree carries no user-facing content. */
|
|
18
|
+
export declare const skippedElements: Set<string>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { GndObject } from './types.js';
|
|
2
|
+
import { GndMediaType } from './dom.js';
|
|
3
|
+
/** Walks a DOM subtree, building the Guided Navigation object tree. */
|
|
4
|
+
export declare class Converter {
|
|
5
|
+
xmlParsed: boolean;
|
|
6
|
+
ids: Map<string, Element>;
|
|
7
|
+
suppressed: Set<Element>;
|
|
8
|
+
idAlloc: {
|
|
9
|
+
claimed: Set<string>;
|
|
10
|
+
counters: Map<string, number>;
|
|
11
|
+
};
|
|
12
|
+
noterefDepth: number;
|
|
13
|
+
allowNode: Element | null;
|
|
14
|
+
private root;
|
|
15
|
+
private current;
|
|
16
|
+
private segments;
|
|
17
|
+
private textAcc;
|
|
18
|
+
private currentCtx;
|
|
19
|
+
private flowEndsWithSpace;
|
|
20
|
+
private pendingChildren;
|
|
21
|
+
constructor(xmlParsed: boolean);
|
|
22
|
+
private allocateId;
|
|
23
|
+
private claimId;
|
|
24
|
+
prescan(root: Element): void;
|
|
25
|
+
convert(root: Element): void;
|
|
26
|
+
convertChildren(root: Element): void;
|
|
27
|
+
result(): GndObject[];
|
|
28
|
+
private descend;
|
|
29
|
+
private appendChild;
|
|
30
|
+
private walk;
|
|
31
|
+
private head;
|
|
32
|
+
private tail;
|
|
33
|
+
private text;
|
|
34
|
+
private textContext;
|
|
35
|
+
private updateFlowSpace;
|
|
36
|
+
private closeSegment;
|
|
37
|
+
private resetFlow;
|
|
38
|
+
private placeholder;
|
|
39
|
+
private pagebreak;
|
|
40
|
+
private noteref;
|
|
41
|
+
private link;
|
|
42
|
+
private flushText;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Converts an HTML or XHTML fragment or document into Guided Navigation
|
|
46
|
+
* objects, reflecting exactly the input it's given: a real, author-written
|
|
47
|
+
* <body> becomes its own role: ["body"] node like any other element; a
|
|
48
|
+
* <body> synthesized only by text/html parsing around a bodyless fragment
|
|
49
|
+
* is not content and is skipped through; a bodyless XHTML fragment's root
|
|
50
|
+
* element is itself the content.
|
|
51
|
+
*/
|
|
52
|
+
export declare function parseMarkup(input: string, mediaType?: GndMediaType): GndObject[];
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function nodeLanguage(el: Element | null): string;
|
|
2
|
+
export declare function hasElementChild(el: Element): boolean;
|
|
3
|
+
export declare function isAncestorOf(anc: Element, n: Element): boolean;
|
|
4
|
+
export type GndMediaType = "text/html" | "application/xhtml+xml";
|
|
5
|
+
export declare function sniffMediaType(input: string): GndMediaType;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { GndDocument } from './types.js';
|
|
2
|
+
import { GndMediaType } from './dom.js';
|
|
3
|
+
export type { GndMediaType };
|
|
4
|
+
/**
|
|
5
|
+
* Builds a Guided Navigation document from an HTML or XHTML fragment or
|
|
6
|
+
* document, following https://github.com/readium/guided-navigation.
|
|
7
|
+
*/
|
|
8
|
+
export declare function makeGnd(input: string, mediaType?: GndMediaType): GndDocument;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { GndObject, GndRole } from './types.js';
|
|
2
|
+
import { TextBuilder } from './text.js';
|
|
3
|
+
export interface ObjBuilder {
|
|
4
|
+
id?: string;
|
|
5
|
+
audioref?: string;
|
|
6
|
+
imgref?: string;
|
|
7
|
+
textref?: string;
|
|
8
|
+
videoref?: string;
|
|
9
|
+
text?: TextBuilder;
|
|
10
|
+
role?: GndRole[];
|
|
11
|
+
children?: ObjBuilder[];
|
|
12
|
+
description?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function isEmptyObj(o: ObjBuilder): boolean;
|
|
15
|
+
/** A node being built up during the tree walk, before its final shape is known. */
|
|
16
|
+
export declare class NavObject {
|
|
17
|
+
el?: Element;
|
|
18
|
+
object: ObjBuilder;
|
|
19
|
+
children: NavObject[];
|
|
20
|
+
noText: boolean;
|
|
21
|
+
finalize(): ObjBuilder;
|
|
22
|
+
}
|
|
23
|
+
export declare function finalizeToGndObject(o: ObjBuilder): GndObject;
|
|
24
|
+
export declare function gndObjectToObjBuilder(n: GndObject): ObjBuilder;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { GndRole } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Determines the Guided Navigation roles of an element, combining the roles
|
|
4
|
+
* derived from the element type itself with the ones from its ARIA `role` and
|
|
5
|
+
* `epub:type` attributes, e.g. `<section epub:type="chapter">` -> `[section, chapter]`.
|
|
6
|
+
* An ARIA role of "presentation"/"none" strips the element of its native semantics.
|
|
7
|
+
*/
|
|
8
|
+
export declare function extractNodeRoles(el: Element): GndRole[];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { GndText } from './types.js';
|
|
2
|
+
export interface TextBuilder {
|
|
3
|
+
plain: string;
|
|
4
|
+
ssml: string;
|
|
5
|
+
language: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function textIsEmpty(t: TextBuilder): boolean;
|
|
8
|
+
export declare function finalizeText(t?: TextBuilder): string | GndText | undefined;
|
|
9
|
+
export interface SSMLContext {
|
|
10
|
+
lang: string;
|
|
11
|
+
tag: string;
|
|
12
|
+
attrs?: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
export declare function ctxEqual(a: SSMLContext, b: SSMLContext): boolean;
|
|
15
|
+
export declare const ssmlTextEscape: (s: string) => string;
|
|
16
|
+
export declare const ssmlAttrEscape: (s: string) => string;
|
|
17
|
+
export declare function isNoBreakSpace(ch: string): boolean;
|
|
18
|
+
export declare function normalizeWhitespace(text: string, stripLeading: boolean): string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type GndRole = string;
|
|
2
|
+
export interface GndText {
|
|
3
|
+
language: string;
|
|
4
|
+
plain?: string;
|
|
5
|
+
ssml?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface GndObject {
|
|
8
|
+
role?: GndRole[];
|
|
9
|
+
text?: string | GndText;
|
|
10
|
+
description?: string;
|
|
11
|
+
imgref?: string;
|
|
12
|
+
audioref?: string;
|
|
13
|
+
videoref?: string;
|
|
14
|
+
textref?: string;
|
|
15
|
+
id?: string;
|
|
16
|
+
children?: GndObject[];
|
|
17
|
+
}
|
|
18
|
+
export interface GndDocument {
|
|
19
|
+
links?: unknown[];
|
|
20
|
+
guided: GndObject[];
|
|
21
|
+
}
|