adaptive-speech-recognizer
Version:
Adaptive dictation-mode speech recognizer ponyfill compatible with WebChat that gives the user time to think and stutter/stammer.
227 lines (169 loc) • 6.95 kB
JavaScript
// @ts-check
// import 'ms-cognitive-speech-sdk';
// import { createAdaptiveRecognizerPonyfill, getAdaptiveRecognizerConfig } from 'adaptive-speech-recognizer';
import fireMockActionsEvent from 'fireMockActionsEvent';
import webApiSpeechRecogDemo from 'webApiSpeechRecogDemo';
const { customElements, HTMLElement } = window;
/**
* A demo application of the dictation-mode speech recognizer.
*
* @license MIT.
* @copyright © 2020 Nick Freear.
* @author Nick Freear, 09-October-2020.
*/
export class ExampleRecognizerAppElement extends HTMLElement {
get _form () { return this.querySelector('form'); }
get _elements () { return this._form.elements; }
get _storageElems () { return this.querySelectorAll('my-local-storage'); }
get _SpeechSDK () { return window.SpeechSDK; }
get _sdkVersion () { return this._options.sdkVersion; }
get _microphoneErrorRegex () { return /microphone initialization: NotAllowedError/; }
async connectedCallback () {
console.assert(this._elements.length === 6, '<form>.elements - Expecting a different number!');
console.assert(this._storageElems.length === 2, '<my-local-storage> - Expecting a different number!');
this._useWebApi = this._param(/webapi=(true)/);
if (this._useWebApi) {
webApiSpeechRecogDemo();
} else {
await this._runExampleApp();
}
console.debug('example-recognizer-app:', this._elements, [this]);
}
async _runExampleApp () {
await import('ms-cognitive-speech-sdk');
const { createAdaptiveRecognizerPonyfill, getAdaptiveRecognizerConfig } = await import('adaptive-speech-recognizer');
const options = getAdaptiveRecognizerConfig(); // WAS: getDictationRecognizerConfig();
if (!options.subscriptionKey || /_/.test(options.subscriptionKey)) {
this.dataset.state = 'error';
this.dataset.errorType = 'config-error';
this._updateLog('ERROR: Expecting a URL parameter `?key=AZURE_SPEECH_SUBSCRIPTION_KEY`.');
throw new Error('ERROR: Expecting a URL parameter `?key=AZURE_SPEECH_SUBSCRIPTION_KEY`.');
}
this._ponyfill = createAdaptiveRecognizerPonyfill(options); // WAS: createDictationRecognizerPonyfill(options);
this._recognizer = new this._ponyfill.SpeechRecognition();
this._recognizer.getConfiguration().then((opt) => this._onConfiguration(opt));
this._recognizer.onresult = (ev) => this._onresult(ev);
this._recognizer.onend = (ev) => this._onend(ev);
this._recognizer.onstart = (ev) => this._onstart(ev);
this._recognizer.onerror = (ev) => this._onerror(ev);
this._form.addEventListener('submit', (ev) => this._onFormSubmitEvent(ev));
this._form.addEventListener('reset', (ev) => this._onFormResetEvent(ev));
console.debug('Ponyfill:', this._ponyfill);
}
/* FORM events.
*/
_onFormSubmitEvent (ev) {
ev.preventDefault();
this._onButtonStart();
console.debug('Recognizer start button clicked');
this._recognizer.start();
// setTimeout(() => enumMediaDevices(), 5000);
}
_onFormResetEvent (ev) {
ev.preventDefault();
console.debug('Recognizer stop button clicked');
this._recognizer.stop(() => this._onRecognitionStop());
}
/* Recognizer events.
*/
_onConfiguration (OPT) {
this._options = OPT;
this._elements.options.value = 'Options: ' + JSON.stringify(OPT, null, 2); // Was: '\t';
if (OPT.actionPhrasesEnable) {
const actionList = fireMockActionsEvent();
this._elements.actions.value = `Suggested actions: <q>${actionList.join('</q>, <q>')}</q>`;
}
this._elements.sdkVersion.value = `Speech SDK ${this._sdkVersion}`;
this.dataset.speechSdkVersion = this._sdkVersion;
console.debug('SpeechSDK:', this._sdkVersion, this._SpeechSDK);
}
_onresult (ev) {
const firstResult = ev.results[0];
// Was: const TEXT = ev._data.results[0][0].transcript;
const { confidence, transcript } = firstResult[0];
const { isFinal } = firstResult;
const { source } = ev.data;
console.warn('Result event:', confidence, ev);
this.dataset.confidence = confidence;
this._elements.result.value = transcript; // Or: RESULT.value!
this._updateLog(`Result := ${transcript} (${source})`);
if (isFinal) {
this._onRecognitionStop();
} else {
this.dataset.recognizerState = 'started';
// Was: this._onRecognitionStart();
}
}
_onend (ev) {
console.warn('End event:', ev);
this._updateLog('> End.');
this._onRecognitionStop();
}
_onstart (ev) {
console.debug('Start event:', ev);
this._updateLog('> Start');
this.dataset.recognizerState = 'started';
}
_onerror (ev) {
const { error } = ev.data;
console.error('>> ERROR:', error, ev);
this._onRecognitionStop();
// Was: document.body!
this.classList.add('recognizer-error');
this.dataset.recognizerState = 'error';
this.dataset.errorType = 'recognizer-error';
this.dataset.errorDetail = error; // to string?
if (this._microphoneErrorRegex.test(error)) {
this.dataset.warning = 'microphone-blocked';
this._updateLog('> Warning: microphone blocked.');
}
}
_onButtonStart () {
this._elements.startButton.disabled = true;
this._elements.stopButton.disabled = false;
this._elements.stopButton.focus();
}
_onRecognitionStop () {
this._elements.stopButton.disabled = true;
this._elements.startButton.disabled = false;
this._elements.startButton.focus();
this.dataset.recognizerState = 'stopped';
console.warn('onRecognitionStop:', this.dataset);
}
_param (regex, def = null) {
const matches = window.location.href.match(regex);
return matches ? matches[1] : def;
}
_updateLog (value) {
this._elements.log.value += `${value}\n`;
}
}
customElements.define('example-recognizer-app', ExampleRecognizerAppElement);
// ----------------------------------------------------
/* window.addEventListener(AUDIO_SOURCE_ERROR_EVENT, ev => {
console.error('>>>> ERROR:', ev.detail.event.error, ev);
onRecognitionStop();
document.body.classList.add('recognizer-error');
document.body.setAttribute('data-error', ev.detail.event.error);
if (ev.detail.micNotAllowed) {
LOG.textContent = '> Warning: microphone blocked.';
}
}); */
// ----------------------------------------------------
// Error handling ??
/* SDK_SCRIPT.addEventListener('error', ev => {
console.error('SDK error:', ev);
}); */
// -----------------------------------------------------
export function enumMediaDevices () {
navigator.mediaDevices.enumerateDevices()
.then(function (devices) {
/* devices.forEach(function(device) {
console.log(device.kind + ": " + device.label +
" id = " + device.deviceId);
}); */
console.warn('Devices:', devices);
})
.catch(err => console.error('ERROR:', err));
}
// ----------------------------------------------------