speechstate 2.0.1 → 2.0.2

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/dist/tts.js ADDED
@@ -0,0 +1,409 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ttsMachine = void 0;
4
+ const xstate_1 = require("xstate");
5
+ const getToken_1 = require("./getToken");
6
+ const TextToSpeech_1 = require("web-speech-cognitive-services/lib/SpeechServices/TextToSpeech");
7
+ const REGION = "northeurope";
8
+ const UTTERANCE_CHUNK_REGEX = /(^.*([!?]+|([.,]+\s)))/;
9
+ exports.ttsMachine = (0, xstate_1.setup)({
10
+ types: {},
11
+ actions: {
12
+ ttsStop: ({ context }) => {
13
+ context.wsaTTS.cancel();
14
+ },
15
+ addFiller: (0, xstate_1.assign)(({ context }) => {
16
+ const spaceIndex = context.buffer.lastIndexOf(" ");
17
+ return {
18
+ buffer: context.buffer.substring(0, spaceIndex) +
19
+ " um," +
20
+ context.buffer.substring(spaceIndex),
21
+ };
22
+ }),
23
+ },
24
+ actors: {
25
+ getToken: getToken_1.getToken,
26
+ createEventsFromStream: (0, xstate_1.fromCallback)(({ sendBack, input }) => {
27
+ const eventSource = new EventSource(input.stream);
28
+ eventSource.addEventListener("STREAMING_DONE", (_event) => {
29
+ console.log("received streaming done - closing event stream");
30
+ sendBack({ type: "STREAMING_DONE" });
31
+ eventSource.close();
32
+ });
33
+ eventSource.addEventListener("STREAMING_RESET", (_event) => {
34
+ console.log("received streaming reset");
35
+ });
36
+ eventSource.addEventListener("STREAMING_CHUNK", (event) => {
37
+ console.log("received streaming chunk:", event);
38
+ sendBack({ type: "STREAMING_CHUNK", value: event.data });
39
+ });
40
+ }),
41
+ ponyfill: (0, xstate_1.fromCallback)(({ sendBack, input }) => {
42
+ const ponyfill = (0, TextToSpeech_1.default)({
43
+ audioContext: input.audioContext,
44
+ credentials: {
45
+ region: REGION, // TODO
46
+ authorizationToken: input.azureAuthorizationToken,
47
+ },
48
+ });
49
+ const { speechSynthesis, SpeechSynthesisUtterance } = ponyfill;
50
+ const tts = speechSynthesis;
51
+ const ttsUtterance = SpeechSynthesisUtterance;
52
+ tts.addEventListener("voiceschanged", () => {
53
+ const voices = tts.getVoices();
54
+ if (voices.length > 0) {
55
+ console.debug("[TTS] READY");
56
+ sendBack({
57
+ type: "READY",
58
+ value: { wsaTTS: tts, wsaUtt: ttsUtterance },
59
+ });
60
+ }
61
+ else {
62
+ console.error("[TTS] No voices available");
63
+ sendBack({ type: "ERROR" });
64
+ }
65
+ });
66
+ }),
67
+ start: (0, xstate_1.fromCallback)(({ sendBack, input }) => {
68
+ if (["", " "].includes(input.utterance)) {
69
+ console.debug("[TTS] SPEAK: ", input.utterance);
70
+ input.wsaTTS.speak("");
71
+ }
72
+ else {
73
+ console.debug("[TTS] SPEAK: ", input.utterance);
74
+ const content = wrapSSML(input.utterance, input.voice, input.ttsLexicon, 1); // todo speech rate;
75
+ const utterance = new input.wsaUtt(content);
76
+ utterance.addEventListener("start", () => {
77
+ sendBack({ type: "TTS_STARTED" });
78
+ console.debug("[TTS] TTS_STARTED");
79
+ });
80
+ utterance.addEventListener("end", () => {
81
+ sendBack({ type: "SPEAK_COMPLETE" });
82
+ console.debug("[TTS] SPEAK_COMPLETE");
83
+ });
84
+ input.wsaTTS.speak(utterance);
85
+ }
86
+ }),
87
+ },
88
+ guards: {
89
+ bufferContainsUtterancePartReadyToBeSpoken: ({ context }) => {
90
+ const m = context.buffer.match(UTTERANCE_CHUNK_REGEX);
91
+ return !!m;
92
+ },
93
+ bufferIsNonEmpty: ({ context }) => {
94
+ return !!context.buffer;
95
+ },
96
+ },
97
+ delays: {
98
+ FILLER_DELAY: ({ context }) => {
99
+ return context.agenda.fillerDelay;
100
+ },
101
+ },
102
+ }).createMachine({
103
+ id: "tts",
104
+ context: ({ input }) => ({
105
+ ttsDefaultVoice: input.ttsDefaultVoice || "en-US-DavisNeural",
106
+ audioContext: input.audioContext,
107
+ azureCredentials: input.azureCredentials,
108
+ buffer: "",
109
+ }),
110
+ initial: "GetToken",
111
+ on: {
112
+ READY: {
113
+ target: ".Ready",
114
+ actions: [
115
+ (0, xstate_1.assign)({
116
+ wsaTTS: ({ event }) => event.value.wsaTTS,
117
+ wsaUtt: ({ event }) => event.value.wsaUtt,
118
+ }),
119
+ (0, xstate_1.sendParent)({ type: "TTS_READY" }),
120
+ ],
121
+ },
122
+ ERROR: { actions: (0, xstate_1.sendParent)({ type: "TTS_ERROR" }) },
123
+ },
124
+ states: {
125
+ Ready: {
126
+ initial: "Idle",
127
+ states: {
128
+ Idle: {
129
+ on: {
130
+ SPEAK: [
131
+ {
132
+ target: "BufferedSpeaker",
133
+ guard: ({ event }) => !!event.value.stream,
134
+ actions: (0, xstate_1.assign)({
135
+ agenda: ({ event }) => event.value.fillerDelay
136
+ ? event.value
137
+ : Object.assign(Object.assign({}, event.value), { fillerDelay: 500 }),
138
+ }),
139
+ },
140
+ {
141
+ target: "Speaking",
142
+ actions: (0, xstate_1.assign)({ agenda: ({ event }) => event.value }),
143
+ },
144
+ ],
145
+ },
146
+ },
147
+ BufferedSpeaker: {
148
+ type: "parallel",
149
+ invoke: {
150
+ id: "createEventsFromStream",
151
+ src: "createEventsFromStream",
152
+ input: ({ context }) => context.agenda,
153
+ },
154
+ on: {
155
+ STOP: {
156
+ target: "Idle",
157
+ },
158
+ SPEAK_COMPLETE: [
159
+ {
160
+ guard: (0, xstate_1.stateIn)("#BufferingDone"),
161
+ target: "Idle",
162
+ actions: [(0, xstate_1.sendParent)({ type: "SPEAK_COMPLETE" })],
163
+ },
164
+ ],
165
+ },
166
+ states: {
167
+ Buffer: {
168
+ initial: "BufferIdle",
169
+ states: {
170
+ BufferIdle: {
171
+ id: "BufferIdle",
172
+ entry: [
173
+ ({ event }) => console.debug("=== Entry BufferIdle", event),
174
+ ],
175
+ on: {
176
+ STREAMING_CHUNK: {
177
+ actions: [
178
+ ({ event }) => console.debug("=================STREAMING_CHUNK: BufferIdle => Buffering", event),
179
+ ],
180
+ target: "Buffering",
181
+ },
182
+ },
183
+ },
184
+ Buffering: {
185
+ id: "Buffering",
186
+ on: {
187
+ STREAMING_CHUNK: [
188
+ {
189
+ actions: [
190
+ ({ event }) => console.debug("=================STREAMING_CHUNK: Buffering => Buffering", event),
191
+ ],
192
+ target: "Buffering",
193
+ reenter: true,
194
+ },
195
+ ],
196
+ STREAMING_DONE: [
197
+ {
198
+ target: "BufferingDone",
199
+ actions: [
200
+ ({ event }) => console.debug("=================STREAMING_DONE: Buffering => BufferingDone", event),
201
+ ],
202
+ },
203
+ ],
204
+ },
205
+ entry: [
206
+ ({ event }) => console.debug("=== Entry Buffering", event),
207
+ (0, xstate_1.assign)({
208
+ buffer: ({ context, event }) => context.buffer + event.value,
209
+ }),
210
+ ],
211
+ },
212
+ BufferingDone: {
213
+ entry: [
214
+ ({ event }) => console.debug("=== Entry BufferingDone", event),
215
+ ],
216
+ id: "BufferingDone",
217
+ },
218
+ },
219
+ },
220
+ Speaker: {
221
+ initial: "SpeakingIdle",
222
+ states: {
223
+ SpeakingIdle: {
224
+ entry: [
225
+ ({ event }) => console.debug("=== Entry SpeakingIdle", event),
226
+ ],
227
+ always: [
228
+ {
229
+ target: "Speak",
230
+ guard: (0, xstate_1.stateIn)("#BufferingDone"),
231
+ actions: [
232
+ ({ event }) => console.debug("========== in BufferingDone: SpeakingIdle => Speak", event),
233
+ (0, xstate_1.assign)({
234
+ utteranceFromStream: ({ context }) => context.buffer,
235
+ }),
236
+ (0, xstate_1.assign)({
237
+ buffer: "",
238
+ }),
239
+ ],
240
+ },
241
+ {
242
+ target: "PrepareSpeech",
243
+ guard: "bufferContainsUtterancePartReadyToBeSpoken",
244
+ },
245
+ ],
246
+ after: {
247
+ FILLER_DELAY: {
248
+ target: "SpeakingIdle",
249
+ reenter: true,
250
+ actions: "addFiller",
251
+ guard: ({ context }) => context.buffer.includes(" "),
252
+ },
253
+ },
254
+ },
255
+ PrepareSpeech: {
256
+ entry: [
257
+ ({ event }) => console.debug("=== Entry PrepareSpeech", event),
258
+ (0, xstate_1.assign)(({ context }) => {
259
+ let utterancePart;
260
+ let restOfBuffer;
261
+ const match = context.buffer.match(UTTERANCE_CHUNK_REGEX);
262
+ utterancePart = match[0];
263
+ restOfBuffer = context.buffer.substring(utterancePart.length);
264
+ return {
265
+ buffer: restOfBuffer,
266
+ utteranceFromStream: utterancePart,
267
+ };
268
+ }),
269
+ ],
270
+ always: [
271
+ {
272
+ target: "Speak",
273
+ },
274
+ ],
275
+ },
276
+ Speak: {
277
+ entry: [
278
+ ({ event }) => console.debug("=== Entry Speak", event),
279
+ ],
280
+ initial: "Go",
281
+ on: {
282
+ TTS_STARTED: {
283
+ actions: (0, xstate_1.sendParent)({ type: "TTS_STARTED" }),
284
+ },
285
+ SPEAK_COMPLETE: [
286
+ {
287
+ guard: (0, xstate_1.stateIn)("#Buffering"),
288
+ target: "SpeakingIdle",
289
+ },
290
+ {
291
+ guard: "bufferIsNonEmpty",
292
+ target: "SpeakingIdle",
293
+ },
294
+ ],
295
+ },
296
+ states: {
297
+ Go: {
298
+ invoke: {
299
+ src: "start",
300
+ input: ({ context }) => ({
301
+ wsaTTS: context.wsaTTS,
302
+ wsaUtt: context.wsaUtt,
303
+ ttsLexicon: context.ttsLexicon,
304
+ voice: context.agenda.voice || context.ttsDefaultVoice,
305
+ utterance: context.utteranceFromStream,
306
+ }),
307
+ },
308
+ on: {
309
+ CONTROL: "Paused",
310
+ },
311
+ exit: "ttsStop",
312
+ },
313
+ Paused: {
314
+ on: {
315
+ CONTROL: "Go",
316
+ },
317
+ },
318
+ },
319
+ },
320
+ },
321
+ },
322
+ },
323
+ },
324
+ Speaking: {
325
+ initial: "Go",
326
+ on: {
327
+ STOP: {
328
+ target: "Idle",
329
+ },
330
+ TTS_STARTED: {
331
+ actions: (0, xstate_1.sendParent)({ type: "TTS_STARTED" }),
332
+ },
333
+ SPEAK_COMPLETE: {
334
+ target: "Idle",
335
+ },
336
+ },
337
+ exit: (0, xstate_1.sendParent)({ type: "SPEAK_COMPLETE" }),
338
+ states: {
339
+ Go: {
340
+ invoke: {
341
+ src: "start",
342
+ input: ({ context }) => ({
343
+ wsaTTS: context.wsaTTS,
344
+ wsaUtt: context.wsaUtt,
345
+ ttsLexicon: context.ttsLexicon,
346
+ voice: context.agenda.voice || context.ttsDefaultVoice,
347
+ // streamURL: context.agenda.streamURL,
348
+ utterance: context.agenda.utterance,
349
+ }),
350
+ },
351
+ on: {
352
+ CONTROL: "Paused",
353
+ },
354
+ exit: "ttsStop",
355
+ },
356
+ Paused: {
357
+ on: {
358
+ CONTROL: "Go",
359
+ },
360
+ },
361
+ },
362
+ },
363
+ },
364
+ },
365
+ Fail: {},
366
+ GetToken: {
367
+ invoke: {
368
+ id: "getAuthorizationToken",
369
+ input: ({ context }) => ({
370
+ credentials: context.azureCredentials,
371
+ }),
372
+ src: "getToken",
373
+ onDone: {
374
+ target: "Ponyfill",
375
+ actions: [
376
+ (0, xstate_1.assign)(({ event }) => {
377
+ return { azureAuthorizationToken: event.output };
378
+ }),
379
+ ],
380
+ },
381
+ onError: {
382
+ actions: ({ event }) => console.error("[TTS] getToken error", event),
383
+ target: "Fail",
384
+ },
385
+ },
386
+ },
387
+ Ponyfill: {
388
+ invoke: {
389
+ id: "ponyTTS",
390
+ src: "ponyfill",
391
+ input: ({ context }) => ({
392
+ audioContext: context.audioContext,
393
+ azureAuthorizationToken: context.azureAuthorizationToken,
394
+ }),
395
+ },
396
+ },
397
+ },
398
+ });
399
+ const wrapSSML = (text, voice, lexicon, speechRate) => {
400
+ let content = `<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="http://www.w3.org/2001/mstts" xml:lang="en-US"><voice name="${voice}">`;
401
+ if (lexicon) {
402
+ content = content + `<lexicon uri="${lexicon}"/>`;
403
+ }
404
+ content =
405
+ content +
406
+ `<prosody rate="${speechRate}">` +
407
+ `${text}</prosody></voice></speak>`;
408
+ return content;
409
+ };
@@ -0,0 +1,42 @@
1
+ export interface AzureSpeechCredentials {
2
+ endpoint: string;
3
+ key: string;
4
+ }
5
+ /**
6
+ * @deprecated use `AzureSpeechCredentials` instead
7
+ */
8
+ export interface AzureCredentials extends AzureSpeechCredentials {
9
+ }
10
+ export interface AzureLanguageCredentials {
11
+ endpoint: string;
12
+ key: string;
13
+ projectName: string;
14
+ deploymentName: string;
15
+ }
16
+ export interface Settings {
17
+ locale?: string;
18
+ azureCredentials: string | AzureCredentials | AzureSpeechCredentials;
19
+ azureLanguageCredentials?: AzureLanguageCredentials;
20
+ asrDefaultCompleteTimeout?: number;
21
+ asrDefaultNoInputTimeout?: number;
22
+ ttsDefaultVoice?: string;
23
+ speechRecognitionEndpointId?: string;
24
+ }
25
+ export interface Agenda {
26
+ utterance: string;
27
+ voice?: string;
28
+ stream?: string;
29
+ fillerDelay?: number;
30
+ }
31
+ export interface Hypothesis {
32
+ utterance: string;
33
+ confidence: number;
34
+ }
35
+ export interface RecogniseParameters {
36
+ noInputTimeout?: number;
37
+ completeTimeout?: number;
38
+ locale?: string;
39
+ hints?: string[];
40
+ nlu?: boolean | AzureLanguageCredentials;
41
+ }
42
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,sBAAsB;CAAG;AAEnE,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,GAAG,gBAAgB,GAAG,sBAAsB,CAAC;IACrE,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;IACpD,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2BAA2B,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,MAAM,WAAW,MAAM;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,GAAG,wBAAwB,CAAC;CAC1C"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "speechstate",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "license": "GPL-3.0",
5
5
  "homepage": "http://localhost/speechstate",
6
6
  "main": "./dist/index.js",