aeap-speech-to-text/lib/dispatcher.js
Kevin Harwell 1f701bd265 Adding Asterisk external speech to text application
This patch adds basic functionality for translating speech to text, using
the Google speech API, from audio received over a websocket after a successful
negotiation with Asterisk using the Asterisk External Application Protocol.
2022-02-15 13:05:43 -06:00

175 lines
4 KiB
JavaScript

/*
* Copyright 2022 Sangoma Technologies Corporation
* Kevin Harwell <kharwell@sangoma.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { randomUUID } = require('crypto');
function handleError(e, msg) {
msg.error_msg = e.message;
}
function sendMessage(speech, msg) {
speech.transport.send(JSON.stringify(msg), { binary: false });
}
function sendSetRequest(speech, params) {
request = {
request: "set",
id: randomUUID(),
params,
};
sendMessage(speech, request);
}
function handleGetRequest(speech, request, response) {
if (!request.params) {
throw new Error("Missing request parameters");
}
let params = {};
for (let p of request.params) {
if (p === "codec") {
params.codecs = speech.codecs.selected;
} else if (p === "language") {
params.language = speech.languages.selected;
} else if (p === "results") {
params.results = speech.provider.results.splice(0);
} else {
console.warn("Ignoring unsupported parameter '" + k + "' in '" +
request.request + "' request");
}
}
response.params = params;
}
function handleSetRequest(speech, request, response) {
if (!request.codecs || !request.params) {
throw new Error("Missing request parameters");
}
/*
* It's all or nothing for an incoming set request. So first validate
* all values, then set newly selected, and lastly set the response.
*/
let codec = null;
let params = {};
if (request.codecs) {
codec = speech.codecs.first(request.codecs);
}
for (let [k, v] of Object.entries(request.params)) {
if (k == "language") {
params.language = speech.languages.first(v);
} else {
console.warn("Ignoring unsupported parameter '" + k + "' in '" +
request.request + "' request");
}
}
if (codec) {
response.codecs = [speech.codecs.selected = codec];
}
if (Object.keys(params).length) {
if (params.language) {
speech.languages.selected = params.language;
}
response.params = params;
}
if (response.codecs || response.params) {
// Start/Restart provider if any parameters were changed
speech.provider.restart({
codec: speech.codecs.selected,
language: speech.languages.selected,
});
}
}
function handleRequest(speech, msg) {
const handlers = {
"get": handleGetRequest,
"set": handleSetRequest,
"setup": handleSetRequest,
};
let response = { response: msg.request, id: msg.id };
try {
handlers[msg.request](speech, msg, response);
} catch (e) {
handleError(e, response);
}
return response;
}
function handleResponse(speech, msg) {
return null; // TODO
}
/**
* Manages configuration, communication, messaging, and data between
* a connected transport and speech provider.
*
* @param {Object} speech - speech object
* @param {Object} speech.codecs - allowed codec(s)
* @param {Object} speech.languages - allowed language(s)
* @param {Object} speech.transport - remote connection
* @param {Object} speech.provider - speech provider
*/
function dispatch(speech) {
speech.transport.on("close", () => {
speech.provider.end();
});
speech.transport.on("message", (data, isBinary) => {
if (isBinary) {
speech.provider.write(data);
return;
}
console.debug("message: " + data);
let msg = JSON.parse(data);
if (msg.hasOwnProperty('request')) {
msg = handleRequest(speech, msg);
} else if (msg.hasOwnProperty('response')) {
msg = handleResponse(speech, msg);
} else {
msg = null;
}
if (msg) {
sendMessage(speech, msg);
}
});
speech.provider.on("result", (result) => {
sendSetRequest(speech, { results: [ result ] });
});
}
module.exports = {
dispatch,
}