From 58270ad87f30f1e2e4602cf5df0c26309e197d74 Mon Sep 17 00:00:00 2001 From: Dave Horton Date: Fri, 24 Mar 2023 14:43:51 -0400 Subject: [PATCH] add functions to retrieve voices for google and tts --- lib/get-tts-voices.js | 33 ++++++- test/index.js | 3 +- test/list-voices.js | 205 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 test/list-voices.js diff --git a/lib/get-tts-voices.js b/lib/get-tts-voices.js index c7a901c..982d472 100644 --- a/lib/get-tts-voices.js +++ b/lib/get-tts-voices.js @@ -4,6 +4,8 @@ const getNuanceAccessToken = require('./get-nuance-access-token'); const {GetVoicesRequest, Voice} = require('../stubs/nuance/synthesizer_pb'); const TextToSpeechV1 = require('ibm-watson/text-to-speech/v1'); const { IamAuthenticator } = require('ibm-watson/auth'); +const ttsGoogle = require('@google-cloud/text-to-speech'); +const { PollyClient, DescribeVoicesCommand } = require('@aws-sdk/client-polly'); const getIbmVoices = async(client, logger, credentials) => { const {tts_region, tts_api_key} = credentials; @@ -80,6 +82,30 @@ const getNuanceVoices = async(client, logger, credentials) => { }); }; +const getGoogleVoices = async(_client, logger, credentials) => { + const client = new ttsGoogle.TextToSpeechClient({credentials}); + return await client.listVoices(); +}; + +const getAwsVoices = async(_client, logger, credentials) => { + try { + const {region, accessKeyId, secretAccessKey} = credentials; + const client = new PollyClient({ + region, + credentials: { + accessKeyId, + secretAccessKey + } + }); + const command = new DescribeVoicesCommand({LanguageCode: 'en-US'}); + const response = await client.send(command); + return response; + } catch (err) { + logger.info({err}, 'testMicrosoftTts - failed to list voices for region ${region}'); + throw err; + } +}; + /** * Synthesize speech to an mp3 file, and also cache the generated speech * in redis (base64 format) for 24 hours so as to avoid unnecessarily paying @@ -99,7 +125,7 @@ const getNuanceVoices = async(client, logger, credentials) => { async function getTtsVoices(client, logger, {vendor, credentials}) { logger = logger || noopLogger; - assert.ok(['nuance', 'ibm'].includes(vendor), + assert.ok(['nuance', 'ibm', 'google', 'aws', 'polly'].includes(vendor), `getTtsVoices not supported for vendor ${vendor}`); switch (vendor) { @@ -107,6 +133,11 @@ async function getTtsVoices(client, logger, {vendor, credentials}) { return getNuanceVoices(client, logger, credentials); case 'ibm': return getIbmVoices(client, logger, credentials); + case 'google': + return getGoogleVoices(client, logger, credentials); + case 'aws': + case 'polly': + return getAwsVoices(client, logger, credentials); default: break; } diff --git a/test/index.js b/test/index.js index 23d57a3..17bda2c 100644 --- a/test/index.js +++ b/test/index.js @@ -1,5 +1,4 @@ require('./docker_start'); require('./synth'); -require('./nuance'); -require('./ibm'); +require('./list-voices'); require('./docker_stop'); diff --git a/test/list-voices.js b/test/list-voices.js new file mode 100644 index 0000000..befd2b4 --- /dev/null +++ b/test/list-voices.js @@ -0,0 +1,205 @@ +const test = require('tape').test ; +const config = require('config'); +const opts = config.get('redis'); +const fs = require('fs'); +const logger = require('pino')({level: 'error'}); +process.on('unhandledRejection', (reason, p) => { + console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); +}); + +const stats = { + increment: () => {}, + histogram: () => {} +}; + +test('IBM - create access key', async(t) => { + const fn = require('..'); + const {client, getIbmAccessToken} = fn(opts, logger); + + if (!process.env.IBM_API_KEY ) { + t.pass('skipping IBM test since no IBM api_key provided'); + t.end(); + client.quit(); + return; + } + try { + let obj = await getIbmAccessToken(process.env.IBM_API_KEY); + //console.log({obj}, 'received access token from IBM'); + t.ok(obj.access_token && !obj.servedFromCache, 'successfull received access token from IBM'); + + obj = await getIbmAccessToken(process.env.IBM_API_KEY); + //console.log({obj}, 'received access token from IBM - second request'); + t.ok(obj.access_token && obj.servedFromCache, 'successfully received access token from cache'); + + await client.flushallAsync(); + t.end(); + } + catch (err) { + console.error(err); + t.end(err); + } + client.quit(); +}); + +test('IBM - retrieve tts voices test', async(t) => { + const fn = require('..'); + const {client, getTtsVoices} = fn(opts, logger); + + if (!process.env.IBM_TTS_API_KEY || !process.env.IBM_TTS_REGION) { + t.pass('skipping IBM test since no IBM api_key and/or region provided'); + t.end(); + client.quit(); + return; + } + try { + const opts = { + vendor: 'ibm', + credentials: { + tts_api_key: process.env.IBM_TTS_API_KEY, + tts_region: process.env.IBM_TTS_REGION + } + }; + const obj = await getTtsVoices(opts); + const {voices} = obj.result; + //console.log(JSON.stringify(voices)); + t.ok(voices.length > 0 && voices[0].language, + `GetVoices: successfully retrieved ${voices.length} voices from IBM`); + + await client.flushallAsync(); + + t.end(); + + } + catch (err) { + console.error(err); + t.end(err); + } + client.quit(); +}); + +test('Nuance hosted tests', async(t) => { + const fn = require('..'); + const {client, getTtsVoices} = fn(opts, logger); + + if (!process.env.NUANCE_CLIENT_ID || !process.env.NUANCE_SECRET ) { + t.pass('skipping Nuance hosted test since no Nuance client_id and secret provided'); + t.end(); + client.quit(); + return; + } + try { + const opts = { + vendor: 'nuance', + credentials: { + client_id: process.env.NUANCE_CLIENT_ID, + secret: process.env.NUANCE_SECRET + } + }; + let voices = await getTtsVoices(opts); + t.ok(voices.length > 0 && voices[0].language, + `GetVoices: successfully retrieved ${voices.length} voices from Nuance`); + + await client.flushallAsync(); + + t.end(); + + } + catch (err) { + console.error(err); + t.end(err); + } + client.quit(); +}); + +test('Nuance on-prem tests', async(t) => { + const fn = require('..'); + const {client, getTtsVoices} = fn(opts, logger); + + if (!process.env.NUANCE_TTS_URI ) { + t.pass('skipping Nuance on-prem test since no Nuance uri provided'); + t.end(); + client.quit(); + return; + } + try { + const opts = { + vendor: 'nuance', + credentials: { + nuance_tts_uri: process.env.NUANCE_TTS_URI + } + }; + let voices = await getTtsVoices(opts); + t.ok(voices.length > 0 && voices[0].language, + `GetVoices: successfully retrieved ${voices.length} voices from Nuance`); + + await client.flushallAsync(); + + t.end(); + + } + catch (err) { + console.error(err); + t.end(err); + } + client.quit(); +}); + +test('Google tests', async(t) => { + const fn = require('..'); + const {client, getTtsVoices} = fn(opts, logger); + + if (!process.env.GCP_FILE && !process.env.GCP_JSON_KEY) { + t.pass('skipping google speech synth tests since neither GCP_FILE nor GCP_JSON_KEY provided'); + return t.end(); + } + try { + const str = process.env.GCP_JSON_KEY || fs.readFileSync(process.env.GCP_FILE); + const credentials = JSON.parse(str); + const opts = { + vendor: 'google', + credentials + }; + let result = await getTtsVoices(opts); + t.ok(result[0].voices.length > 0, `GetVoices: successfully retrieved ${result[0].voices.length} voices from Google`); + + await client.flushallAsync(); + + t.end(); + } + catch (err) { + console.error(err); + t.end(err); + } + client.quit(); +}); + +test('AWS tests', async(t) => { + const fn = require('..'); + const {client, getTtsVoices} = fn(opts, logger); + + if (!process.env.AWS_ACCESS_KEY_ID || !process.env.AWS_SECRET_ACCESS_KEY || !process.env.AWS_REGION) { + t.pass('skipping AWS speech synth tests since AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, or AWS_REGION not provided'); + return t.end(); + } + try { + const opts = { + vendor: 'aws', + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + region: process.env.AWS_REGION, + } + }; + let result = await getTtsVoices(opts); + t.ok(result?.Voices?.length > 0, `GetVoices: successfully retrieved ${result.Voices.length} voices from AWS`); + + await client.flushallAsync(); + + t.end(); + } + catch (err) { + console.error(err); + t.end(err); + } + client.quit(); +});