@ng-web-apis/speech
Version:
A library for using Web Speech API with Angular
193 lines (176 loc) • 8.53 kB
JavaScript
import * as i0 from '@angular/core';
import { inject, EventEmitter, Directive, Input, Output, Pipe, InjectionToken, LOCALE_ID, NgZone, Injectable } from '@angular/core';
import { SPEECH_SYNTHESIS, WA_SPEECH_RECOGNITION, SPEECH_RECOGNITION } from '@ng-web-apis/common';
import { filter, scan, map, pipe, skipWhile, takeWhile, Observable, fromEvent, startWith } from 'rxjs';
class WaTextToSpeech {
speechSynthesisRef = inject(SPEECH_SYNTHESIS);
paused = false;
onerror = new EventEmitter();
onend = new EventEmitter();
onmark = new EventEmitter();
onboundary = new EventEmitter();
set waTextToSpeech(utterance) {
this.speechSynthesisRef.cancel();
this.speechSynthesisRef.pause();
utterance.onerror = (e) => this.onerror.emit(e);
utterance.onend = (e) => this.onend.emit(e);
utterance.onmark = (e) => this.onmark.emit(e);
utterance.onboundary = (e) => this.onboundary.emit(e);
this.speechSynthesisRef.speak(utterance);
}
ngOnChanges() {
if (this.paused) {
this.speechSynthesisRef.pause();
}
else {
this.speechSynthesisRef.resume();
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WaTextToSpeech, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: WaTextToSpeech, isStandalone: true, selector: "[waTextToSpeech]", inputs: { paused: ["waTextToSpeechPaused", "paused"], waTextToSpeech: "waTextToSpeech" }, outputs: { onerror: "waTextToSpeechError", onend: "waTextToSpeechEnd", onmark: "waTextToSpeechMark", onboundary: "waTextToSpeechBoundary" }, usesOnChanges: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WaTextToSpeech, decorators: [{
type: Directive,
args: [{
standalone: true,
selector: '[waTextToSpeech]',
}]
}], propDecorators: { paused: [{
type: Input,
args: ['waTextToSpeechPaused']
}], onerror: [{
type: Output,
args: ['waTextToSpeechError']
}], onend: [{
type: Output,
args: ['waTextToSpeechEnd']
}], onmark: [{
type: Output,
args: ['waTextToSpeechMark']
}], onboundary: [{
type: Output,
args: ['waTextToSpeechBoundary']
}], waTextToSpeech: [{
type: Input
}] } });
/**
* @deprecated: use {@link WaTextToSpeech}
*/
const TextToSpeechDirective = WaTextToSpeech;
class UtterancePipe {
transform(text, { lang = '', pitch = 1, rate = 1, volume = 1, voice = null, } = {}) {
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = lang;
utterance.pitch = pitch;
utterance.rate = rate;
utterance.volume = volume;
utterance.voice = voice; // Strange TS issue will not allow null here
return utterance;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: UtterancePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "16.2.12", ngImport: i0, type: UtterancePipe, isStandalone: true, name: "waUtterance" });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: UtterancePipe, decorators: [{
type: Pipe,
args: [{
standalone: true,
name: 'waUtterance',
}]
}] });
function confidenceAbove(threshold) {
return filter(({ confidence }) => confidence > threshold);
}
function continuous() {
return scan((result, current) => [
...result.filter(({ isFinal }) => isFinal),
...current,
], []);
}
function final() {
return map((results) => results.filter(({ isFinal }) => isFinal));
}
function firstAlternative() {
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
return map((result) => result[0]?.[0]);
}
function isSaid(phrase) {
const lowercased = phrase.toLowerCase().trim();
return (results) => !!results.find((result) => result.isFinal &&
result[0]?.transcript.toLowerCase().trim() === lowercased);
}
function skipUntilSaid(text) {
const predicate = isSaid(text);
return pipe(skipWhile((results) => !predicate(results)), map((value, index) => (index ? value : [])));
}
function takeUntilSaid(text) {
const predicate = isSaid(text);
return takeWhile((results) => !predicate(results));
}
const WA_SPEECH_RECOGNITION_MAX_ALTERNATIVES = new InjectionToken('[WA_SPEECH_RECOGNITION_MAX_ALTERNATIVES]', {
factory: () => 1,
});
/**
* @deprecated: drop in v5.0, use {@link WA_SPEECH_RECOGNITION_MAX_ALTERNATIVES}
*/
const SPEECH_RECOGNITION_MAX_ALTERNATIVES = WA_SPEECH_RECOGNITION_MAX_ALTERNATIVES;
class SpeechRecognitionService extends Observable {
classRef = inject(WA_SPEECH_RECOGNITION);
lang = inject(LOCALE_ID, { optional: true });
maxAlternatives = inject(WA_SPEECH_RECOGNITION_MAX_ALTERNATIVES);
ngZone = inject(NgZone);
constructor() {
super((subscriber) => {
if (!this.classRef) {
subscriber.error(new Error('SpeechRecognition is not supported'));
return () => { };
}
// eslint-disable-next-line new-cap
const speechRecognition = new this.classRef();
speechRecognition.maxAlternatives = this.maxAlternatives;
speechRecognition.lang = this.lang ?? '';
speechRecognition.interimResults = true;
speechRecognition.onerror = (error) => subscriber.error(error);
speechRecognition.onend = () => subscriber.complete();
speechRecognition.onresult = ({ results }) => this.ngZone.run(() => subscriber.next(Array.from({ length: results.length }, (_, i) => results[i])));
speechRecognition.start();
return () => speechRecognition.abort();
});
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SpeechRecognitionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SpeechRecognitionService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SpeechRecognitionService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: function () { return []; } });
const WA_SPEECH_RECOGNITION_SUPPORT = new InjectionToken('[WA_SPEECH_RECOGNITION_SUPPORT]', {
factory: () => !!inject(SPEECH_RECOGNITION),
});
/**
* @deprecated: drop in v5.0, use {@link WA_SPEECH_RECOGNITION_SUPPORT}
*/
const SPEECH_RECOGNITION_SUPPORT = WA_SPEECH_RECOGNITION_SUPPORT;
const WA_SPEECH_SYNTHESIS_SUPPORT = new InjectionToken('[WA_SPEECH_SYNTHESIS_SUPPORT]', {
factory: () => !!inject(SPEECH_SYNTHESIS),
});
/**
* @deprecated: drop in v5.0, use {@link WA_SPEECH_SYNTHESIS_SUPPORT}
*/
const SPEECH_SYNTHESIS_SUPPORT = WA_SPEECH_SYNTHESIS_SUPPORT;
const WA_SPEECH_SYNTHESIS_VOICES = new InjectionToken('[WA_SPEECH_SYNTHESIS_VOICES]', {
factory: () => {
const speechSynthesisRef = inject(SPEECH_SYNTHESIS);
return fromEvent(speechSynthesisRef, 'voiceschanged').pipe(startWith(null), map(() => speechSynthesisRef.getVoices()));
},
});
/**
* @deprecated: drop in v5.0, use {@link WA_SPEECH_SYNTHESIS_VOICES}
*/
const SPEECH_SYNTHESIS_VOICES = WA_SPEECH_SYNTHESIS_VOICES;
/**
* Generated bundle index. Do not edit.
*/
export { SPEECH_RECOGNITION_MAX_ALTERNATIVES, SPEECH_RECOGNITION_SUPPORT, SPEECH_SYNTHESIS_SUPPORT, SPEECH_SYNTHESIS_VOICES, SpeechRecognitionService, TextToSpeechDirective, UtterancePipe, WA_SPEECH_RECOGNITION_MAX_ALTERNATIVES, WA_SPEECH_RECOGNITION_SUPPORT, WA_SPEECH_SYNTHESIS_SUPPORT, WA_SPEECH_SYNTHESIS_VOICES, WaTextToSpeech, confidenceAbove, continuous, final, firstAlternative, isSaid, skipUntilSaid, takeUntilSaid };
//# sourceMappingURL=ng-web-apis-speech.mjs.map