@jambonz/speech-utils 0.0.51 → 0.1.1

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/index.js CHANGED
@@ -13,10 +13,11 @@ module.exports = (opts, logger) => {
13
13
  getTtsSize: require('./lib/get-tts-size').bind(null, client, logger),
14
14
  purgeTtsCache: require('./lib/purge-tts-cache').bind(null, client, logger),
15
15
  addFileToCache: require('./lib/add-file-to-cache').bind(null, client, logger),
16
- synthAudio: require('./lib/synth-audio').bind(null, client, logger),
16
+ synthAudio: require('./lib/synth-audio').bind(null, client, createHash, retrieveHash, logger),
17
+ getVerbioAccessToken: require('./lib/get-verbio-token').bind(null, client, logger),
17
18
  getNuanceAccessToken: require('./lib/get-nuance-access-token').bind(null, client, logger),
18
19
  getIbmAccessToken: require('./lib/get-ibm-access-token').bind(null, client, logger),
19
20
  getAwsAuthToken: require('./lib/get-aws-sts-token').bind(null, logger, createHash, retrieveHash),
20
- getTtsVoices: require('./lib/get-tts-voices').bind(null, client, logger),
21
+ getTtsVoices: require('./lib/get-tts-voices').bind(null, client, createHash, retrieveHash, logger),
21
22
  };
22
23
  };
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ HTTP_TIMEOUT: 5000
3
+ };
@@ -1,32 +1,41 @@
1
- const { STSClient, GetSessionTokenCommand } = require('@aws-sdk/client-sts');
1
+ const { STSClient, GetSessionTokenCommand, AssumeRoleCommand } = require('@aws-sdk/client-sts');
2
2
  const {makeAwsKey, noopLogger} = require('./utils');
3
3
  const debug = require('debug')('jambonz:speech-utils');
4
4
  const EXPIRY = 3600;
5
5
 
6
6
  async function getAwsAuthToken(
7
- logger,
8
- createHash, retrieveHash,
9
- awsAccessKeyId, awsSecretAccessKey, awsRegion) {
7
+ logger, createHash, retrieveHash,
8
+ awsAccessKeyId, awsSecretAccessKey, awsRegion, roleArn = null) {
10
9
  logger = logger || noopLogger;
11
10
  try {
12
- const key = makeAwsKey(awsAccessKeyId);
11
+ const key = makeAwsKey(roleArn || awsAccessKeyId);
13
12
  const obj = await retrieveHash(key);
14
13
  if (obj) return {...obj, servedFromCache: true};
15
14
 
16
- /* access token not found in cache, so generate it using STS */
17
- const stsClient = new STSClient({
18
- region: awsRegion,
19
- credentials: {
20
- accessKeyId: awsAccessKeyId,
21
- secretAccessKey: awsSecretAccessKey,
22
- }
23
- });
24
- const command = new GetSessionTokenCommand({DurationSeconds: EXPIRY});
25
- const data = await stsClient.send(command);
15
+ let data;
16
+ if (roleArn) {
17
+ const stsClient = new STSClient({ region: awsRegion});
18
+ const roleToAssume = { RoleArn: roleArn, RoleSessionName: 'Jambonz_Speech', DurationSeconds: EXPIRY};
19
+ const command = new AssumeRoleCommand(roleToAssume);
20
+
21
+ data = await stsClient.send(command);
22
+ } else {
23
+ /* access token not found in cache, so generate it using STS */
24
+ const stsClient = new STSClient({
25
+ region: awsRegion,
26
+ credentials: {
27
+ accessKeyId: awsAccessKeyId,
28
+ secretAccessKey: awsSecretAccessKey,
29
+ }
30
+ });
31
+ const command = new GetSessionTokenCommand({DurationSeconds: EXPIRY});
32
+ data = await stsClient.send(command);
33
+ }
26
34
 
27
35
  const credentials = {
28
36
  accessKeyId: data.Credentials.AccessKeyId,
29
37
  secretAccessKey: data.Credentials.SecretAccessKey,
38
+ sessionToken: data.Credentials.SessionToken,
30
39
  securityToken: data.Credentials.SessionToken
31
40
  };
32
41
 
@@ -2,8 +2,8 @@ const formurlencoded = require('form-urlencoded');
2
2
  const {Pool} = require('undici');
3
3
  const pool = new Pool('https://iam.cloud.ibm.com');
4
4
  const {makeIbmKey, noopLogger} = require('./utils');
5
+ const { HTTP_TIMEOUT } = require('./constants');
5
6
  const debug = require('debug')('jambonz:realtimedb-helpers');
6
- const HTTP_TIMEOUT = 5000;
7
7
 
8
8
  async function getIbmAccessToken(client, logger, apiKey) {
9
9
  logger = logger || noopLogger;
@@ -2,8 +2,8 @@ const formurlencoded = require('form-urlencoded');
2
2
  const {Pool} = require('undici');
3
3
  const pool = new Pool('https://auth.crt.nuance.com');
4
4
  const {makeNuanceKey, makeBasicAuthHeader, noopLogger} = require('./utils');
5
+ const { HTTP_TIMEOUT } = require('./constants');
5
6
  const debug = require('debug')('jambonz:realtimedb-helpers');
6
- const HTTP_TIMEOUT = 5000;
7
7
 
8
8
  async function getNuanceAccessToken(client, logger, clientId, secret, scope) {
9
9
  logger = logger || noopLogger;
@@ -1,11 +1,16 @@
1
1
  const assert = require('assert');
2
2
  const {noopLogger, createNuanceClient, createKryptonClient} = require('./utils');
3
3
  const getNuanceAccessToken = require('./get-nuance-access-token');
4
+ const getVerbioAccessToken = require('./get-verbio-token');
4
5
  const {GetVoicesRequest, Voice} = require('../stubs/nuance/synthesizer_pb');
5
6
  const TextToSpeechV1 = require('ibm-watson/text-to-speech/v1');
6
7
  const { IamAuthenticator } = require('ibm-watson/auth');
7
8
  const ttsGoogle = require('@google-cloud/text-to-speech');
8
9
  const { PollyClient, DescribeVoicesCommand } = require('@aws-sdk/client-polly');
10
+ const getAwsAuthToken = require('./get-aws-sts-token');
11
+ const {Pool} = require('undici');
12
+ const { HTTP_TIMEOUT } = require('./constants');
13
+ const verbioVoicePool = new Pool('https://us.rest.speechcenter.verbio.com');
9
14
 
10
15
  const getIbmVoices = async(client, logger, credentials) => {
11
16
  const {tts_region, tts_api_key} = credentials;
@@ -87,16 +92,26 @@ const getGoogleVoices = async(_client, logger, credentials) => {
87
92
  return await client.listVoices();
88
93
  };
89
94
 
90
- const getAwsVoices = async(_client, logger, credentials) => {
95
+ const getAwsVoices = async(_client, createHash, retrieveHash, logger, credentials) => {
91
96
  try {
92
- const {region, accessKeyId, secretAccessKey} = credentials;
93
- const client = new PollyClient({
94
- region,
95
- credentials: {
96
- accessKeyId,
97
- secretAccessKey
98
- }
99
- });
97
+ const {region, accessKeyId, secretAccessKey, roleArn} = credentials;
98
+ let client = null;
99
+ if (accessKeyId && secretAccessKey) {
100
+ client = new PollyClient({
101
+ region,
102
+ credentials: {
103
+ accessKeyId,
104
+ secretAccessKey
105
+ }
106
+ });
107
+ } else if (roleArn) {
108
+ client = new PollyClient({
109
+ region,
110
+ credentials: await getAwsAuthToken(logger, createHash, retrieveHash, null, null, region, roleArn),
111
+ });
112
+ } else {
113
+ client = new PollyClient({region});
114
+ }
100
115
  const command = new DescribeVoicesCommand({});
101
116
  const response = await client.send(command);
102
117
  return response;
@@ -106,6 +121,26 @@ const getAwsVoices = async(_client, logger, credentials) => {
106
121
  }
107
122
  };
108
123
 
124
+ const getVerbioVoices = async(client, logger, credentials) => {
125
+ try {
126
+ const access_token = await getVerbioAccessToken(client, logger, credentials);
127
+ const { body} = await verbioVoicePool.request({
128
+ path: '/api/v1/voices',
129
+ method: 'GET',
130
+ headers: {
131
+ 'Authorization': `Bearer ${access_token.access_token}`,
132
+ 'User-Agent': 'jambonz'
133
+ },
134
+ timeout: HTTP_TIMEOUT,
135
+ followRedirects: false
136
+ });
137
+ return await body.json();
138
+ } catch (err) {
139
+ logger.info({err}, 'getVerbioVoices - failed to list voices for Verbio');
140
+ throw err;
141
+ }
142
+ };
143
+
109
144
  /**
110
145
  * Synthesize speech to an mp3 file, and also cache the generated speech
111
146
  * in redis (base64 format) for 24 hours so as to avoid unnecessarily paying
@@ -122,10 +157,10 @@ const getAwsVoices = async(_client, logger, credentials) => {
122
157
  * @returns object containing filepath to an mp3 file in the /tmp folder containing
123
158
  * the synthesized audio, and a variable indicating whether it was served from cache
124
159
  */
125
- async function getTtsVoices(client, logger, {vendor, credentials}) {
160
+ async function getTtsVoices(client, createHash, retrieveHash, logger, {vendor, credentials}) {
126
161
  logger = logger || noopLogger;
127
162
 
128
- assert.ok(['nuance', 'ibm', 'google', 'aws', 'polly'].includes(vendor),
163
+ assert.ok(['nuance', 'ibm', 'google', 'aws', 'polly', 'verbio'].includes(vendor),
129
164
  `getTtsVoices not supported for vendor ${vendor}`);
130
165
 
131
166
  switch (vendor) {
@@ -137,7 +172,9 @@ async function getTtsVoices(client, logger, {vendor, credentials}) {
137
172
  return getGoogleVoices(client, logger, credentials);
138
173
  case 'aws':
139
174
  case 'polly':
140
- return getAwsVoices(client, logger, credentials);
175
+ return getAwsVoices(client, createHash, retrieveHash, logger, credentials);
176
+ case 'verbio':
177
+ return getVerbioVoices(client, logger, credentials);
141
178
  default:
142
179
  break;
143
180
  }
@@ -0,0 +1,51 @@
1
+ const {Pool} = require('undici');
2
+ const { noopLogger, makeVerbioKey } = require('./utils');
3
+ const { HTTP_TIMEOUT } = require('./constants');
4
+ const pool = new Pool('https://auth.speechcenter.verbio.com:444');
5
+ const debug = require('debug')('jambonz:realtimedb-helpers');
6
+
7
+ async function getVerbioAccessToken(client, logger, credentials) {
8
+ logger = logger || noopLogger;
9
+ const { client_id, client_secret } = credentials;
10
+ try {
11
+ const key = makeVerbioKey(client_id);
12
+ const access_token = await client.get(key);
13
+ if (access_token) {
14
+ return {access_token, servedFromCache: true};
15
+ }
16
+
17
+ const payload = {
18
+ client_id,
19
+ client_secret
20
+ };
21
+
22
+ const {statusCode, headers, body} = await pool.request({
23
+ path: '/api/v1/token',
24
+ method: 'POST',
25
+ headers: {
26
+ 'Content-Type': 'application/json',
27
+ 'User-Agent': 'jambonz'
28
+ },
29
+ body: JSON.stringify(payload),
30
+ timeout: HTTP_TIMEOUT,
31
+ followRedirects: false
32
+ });
33
+
34
+ if (200 !== statusCode) {
35
+ logger.debug({statusCode, headers, body: await body.text()}, 'error fetching access token from Verbio');
36
+ const err = new Error();
37
+ err.statusCode = statusCode;
38
+ throw err;
39
+ }
40
+ const json = await body.json();
41
+ const expiry = Math.floor(json.expiration_time - Date.now() / 1000 - 30);
42
+ await client.set(key, json.access_token, 'EX', expiry);
43
+ return {...json, servedFromCache: false};
44
+ } catch (err) {
45
+ debug(err, `getVerbioAccessToken: Error retrieving Verbio access token for client_id ${client_id}`);
46
+ logger.error(err, `getVerbioAccessToken: Error retrieving Verbio access token for client_id ${client_id}`);
47
+ throw err;
48
+ }
49
+ }
50
+
51
+ module.exports = getVerbioAccessToken;
@@ -22,6 +22,7 @@ const {
22
22
  noopLogger
23
23
  } = require('./utils');
24
24
  const getNuanceAccessToken = require('./get-nuance-access-token');
25
+ const getVerbioAccessToken = require('./get-verbio-token');
25
26
  const {
26
27
  SynthesisRequest,
27
28
  Voice,
@@ -39,6 +40,7 @@ const debug = require('debug')('jambonz:realtimedb-helpers');
39
40
  const EXPIRES = (process.env.JAMBONES_TTS_CACHE_DURATION_MINS || 4 * 60) * 60; // cache tts for 4 hours
40
41
  const TMP_FOLDER = '/tmp';
41
42
  const OpenAI = require('openai');
43
+ const getAwsAuthToken = require('./get-aws-sts-token');
42
44
 
43
45
 
44
46
  const trimTrailingSilence = (buffer) => {
@@ -75,7 +77,7 @@ const trimTrailingSilence = (buffer) => {
75
77
  * @returns object containing filepath to an mp3 file in the /tmp folder containing
76
78
  * the synthesized audio, and a variable indicating whether it was served from cache
77
79
  */
78
- async function synthAudio(client, logger, stats, { account_sid,
80
+ async function synthAudio(client, createHash, retrieveHash, logger, stats, { account_sid,
79
81
  vendor, language, voice, gender, text, engine, salt, model, credentials, deploymentId,
80
82
  disableTtsCache, renderForCaching, disableTtsStreaming, options
81
83
  }) {
@@ -85,7 +87,7 @@ async function synthAudio(client, logger, stats, { account_sid,
85
87
  logger = logger || noopLogger;
86
88
 
87
89
  assert.ok(['google', 'aws', 'polly', 'microsoft', 'wellsaid', 'nuance', 'nvidia', 'ibm', 'elevenlabs',
88
- 'whisper', 'deepgram', 'playht', 'rimelabs'].includes(vendor) ||
90
+ 'whisper', 'deepgram', 'playht', 'rimelabs', 'verbio'].includes(vendor) ||
89
91
  vendor.startsWith('custom'),
90
92
  `synthAudio supported vendors are google, aws, microsoft, nuance, nvidia and wellsaid ..etc, not ${vendor}`);
91
93
  if ('google' === vendor) {
@@ -138,6 +140,10 @@ async function synthAudio(client, logger, stats, { account_sid,
138
140
  assert.ok(credentials.api_key, 'synthAudio requires api_key when whisper is used');
139
141
  } else if (vendor.startsWith('custom')) {
140
142
  assert.ok(credentials.custom_tts_url, `synthAudio requires custom_tts_url in credentials when ${vendor} is used`);
143
+ } else if ('verbio' === vendor) {
144
+ assert.ok(voice, 'synthAudio requires voice when verbio is used');
145
+ assert.ok(credentials.client_id, 'synthAudio requires client_id when verbio is used');
146
+ assert.ok(credentials.client_secret, 'synthAudio requires client_secret when verbio is used');
141
147
  }
142
148
  const key = makeSynthKey({
143
149
  account_sid,
@@ -148,7 +154,7 @@ async function synthAudio(client, logger, stats, { account_sid,
148
154
  text
149
155
  });
150
156
  let filePath;
151
- if (['nuance', 'nvidia'].includes(vendor) ||
157
+ if (['nuance', 'nvidia', 'verbio'].includes(vendor) ||
152
158
  (
153
159
  (process.env.JAMBONES_TTS_TRIM_SILENCE || !process.env.JAMBONES_DISABLE_TTS_STREAMING) &&
154
160
  ['microsoft', 'azure'].includes(vendor)
@@ -187,7 +193,8 @@ async function synthAudio(client, logger, stats, { account_sid,
187
193
  case 'aws':
188
194
  case 'polly':
189
195
  vendorLabel = 'aws';
190
- audioBuffer = await synthPolly(logger, {credentials, stats, language, voice, text, engine});
196
+ audioBuffer = await synthPolly(createHash, retrieveHash, logger,
197
+ {credentials, stats, language, voice, text, engine});
191
198
  break;
192
199
  case 'azure':
193
200
  case 'microsoft':
@@ -232,6 +239,11 @@ async function synthAudio(client, logger, stats, { account_sid,
232
239
  credentials, stats, voice, text, renderForCaching, disableTtsStreaming});
233
240
  if (audioBuffer?.filePath) return audioBuffer;
234
241
  break;
242
+ case 'verbio':
243
+ audioBuffer = await synthVerbio(client, logger, {
244
+ credentials, stats, voice, text, renderForCaching, disableTtsStreaming});
245
+ if (audioBuffer?.filePath) return audioBuffer;
246
+ break;
235
247
  case 'deepgram':
236
248
  audioBuffer = await synthDeepgram(logger, {credentials, stats, model, text,
237
249
  renderForCaching, disableTtsStreaming});
@@ -263,16 +275,28 @@ async function synthAudio(client, logger, stats, { account_sid,
263
275
  });
264
276
  }
265
277
 
266
- const synthPolly = async(logger, {credentials, stats, language, voice, engine, text}) => {
278
+ const synthPolly = async(createHash, retrieveHash, logger,
279
+ {credentials, stats, language, voice, engine, text}) => {
267
280
  try {
268
- const {region, accessKeyId, secretAccessKey} = credentials;
269
- const polly = new PollyClient({
270
- region,
271
- credentials: {
272
- accessKeyId,
273
- secretAccessKey
274
- }
275
- });
281
+ const {region, accessKeyId, secretAccessKey, roleArn} = credentials;
282
+ let polly;
283
+ if (accessKeyId && secretAccessKey) {
284
+ polly = new PollyClient({
285
+ region,
286
+ credentials: {
287
+ accessKeyId,
288
+ secretAccessKey
289
+ }
290
+ });
291
+ } else if (roleArn) {
292
+ polly = new PollyClient({
293
+ region,
294
+ credentials: await getAwsAuthToken(logger, createHash, retrieveHash, null, null, region, roleArn),
295
+ });
296
+ } else {
297
+ // AWS RoleArn assigned to Instance profile
298
+ polly = new PollyClient({region});
299
+ }
276
300
  const opts = {
277
301
  Engine: engine,
278
302
  OutputFormat: 'mp3',
@@ -366,7 +390,7 @@ async function _synthOnPremMicrosoft(logger, {
366
390
  text,
367
391
  filePath
368
392
  }) {
369
- const {use_custom_tts, custom_tts_endpoint_url} = credentials;
393
+ const {use_custom_tts, custom_tts_endpoint_url, api_key} = credentials;
370
394
  let content = text;
371
395
 
372
396
  if (use_custom_tts && !content.startsWith('<speak')) {
@@ -390,7 +414,8 @@ async function _synthOnPremMicrosoft(logger, {
390
414
  const post = bent('POST', 'buffer', {
391
415
  'X-Microsoft-OutputFormat': trimSilence ? 'raw-8khz-16bit-mono-pcm' : 'audio-16khz-32kbitrate-mono-mp3',
392
416
  'Content-Type': 'application/ssml+xml',
393
- 'User-Agent': 'Jambonz'
417
+ 'User-Agent': 'Jambonz',
418
+ ...(api_key && {'Ocp-Apim-Subscription-Key': api_key})
394
419
  });
395
420
  const mp3 = await post(custom_tts_endpoint_url, content);
396
421
  return mp3;
@@ -438,6 +463,7 @@ const synthMicrosoft = async(logger, {
438
463
  params += ',write_cache_file=1';
439
464
  if (region) params += `,region=${region}`;
440
465
  if (custom_tts_endpoint) params += `,endpointId=${custom_tts_endpoint}`;
466
+ if (custom_tts_endpoint_url) params += `,endpoint=${custom_tts_endpoint_url}`;
441
467
  if (process.env.JAMBONES_HTTP_PROXY_IP) params += `,http_proxy_ip=${process.env.JAMBONES_HTTP_PROXY_IP}`;
442
468
  if (process.env.JAMBONES_HTTP_PROXY_PORT) params += `,http_proxy_port=${process.env.JAMBONES_HTTP_PROXY_PORT}`;
443
469
  params += '}';
@@ -803,6 +829,46 @@ const synthRimelabs = async(logger, {
803
829
  throw err;
804
830
  }
805
831
  };
832
+ const synthVerbio = async(client, logger, {credentials, stats, voice, text, renderForCaching, disableTtsStreaming}) => {
833
+ //https://doc.speechcenter.verbio.com/#tag/Text-To-Speech-REST-API
834
+ if (text.length > 2000) {
835
+ throw new Error('Verbio cannot synthesize for the text length larger than 2000 characters');
836
+ }
837
+ const token = await getVerbioAccessToken(client, logger, credentials);
838
+ if (!process.env.JAMBONES_DISABLE_TTS_STREAMING && !renderForCaching && !disableTtsStreaming) {
839
+ let params = '';
840
+ params += `{access_token=${token.access_token}`;
841
+ params += ',vendor=verbio';
842
+ params += `,voice=${voice}`;
843
+ params += ',write_cache_file=1';
844
+ params += '}';
845
+
846
+ return {
847
+ filePath: `say:${params}${text.replace(/\n/g, ' ')}`,
848
+ servedFromCache: false,
849
+ rtt: 0
850
+ };
851
+ }
852
+
853
+ try {
854
+ const post = bent('https://us.rest.speechcenter.verbio.com', 'POST', 'buffer', {
855
+ 'Authorization': `Bearer ${token.access_token}`,
856
+ 'User-Agent': 'jambonz',
857
+ 'Content-Type': 'application/json'
858
+ });
859
+ const r8 = await post('/api/v1/synthesize', {
860
+ voice_id: voice,
861
+ output_sample_rate: '8k',
862
+ output_encoding: 'pcm16',
863
+ text
864
+ });
865
+ return r8;
866
+ } catch (err) {
867
+ logger.info({err}, 'synth Verbio returned error');
868
+ stats.increment('tts.count', ['vendor:verbio', 'accepted:no']);
869
+ throw err;
870
+ }
871
+ };
806
872
 
807
873
  const synthWhisper = async(logger, {credentials, stats, voice, text, renderForCaching, disableTtsStreaming}) => {
808
874
  const {api_key, model_id, baseURL, timeout, speed} = credentials;
package/lib/utils.js CHANGED
@@ -3,10 +3,10 @@ const {SynthesizerClient} = require('../stubs/nuance/synthesizer_grpc_pb');
3
3
  const {RivaSpeechSynthesisClient} = require('../stubs/riva/proto/riva_tts_grpc_pb');
4
4
  const {Pool} = require('undici');
5
5
  const pool = new Pool('https://auth.crt.nuance.com');
6
- const HTTP_TIMEOUT = 5000;
7
6
  const NUANCE_AUTH_ENDPOINT = 'tts.api.nuance.com:443';
8
7
  const grpc = require('@grpc/grpc-js');
9
8
  const formurlencoded = require('form-urlencoded');
9
+ const { HTTP_TIMEOUT } = require('./constants');
10
10
 
11
11
  const debug = require('debug')('jambonz:realtimedb-helpers');
12
12
  /**
@@ -49,6 +49,12 @@ function makeAwsKey(awsAccessKeyId) {
49
49
  return `aws:${hash.digest('hex')}`;
50
50
  }
51
51
 
52
+ function makeVerbioKey(client_id) {
53
+ const hash = crypto.createHash('sha1');
54
+ hash.update(client_id);
55
+ return `verbio:${hash.digest('hex')}`;
56
+ }
57
+
52
58
  function makeNuanceKey(clientId, secret, scope) {
53
59
  const hash = crypto.createHash('sha1');
54
60
  hash.update(`${clientId}:${secret}:${scope}`);
@@ -117,6 +123,7 @@ module.exports = {
117
123
  makeNuanceKey,
118
124
  makeIbmKey,
119
125
  makeAwsKey,
126
+ makeVerbioKey,
120
127
  getNuanceAccessToken,
121
128
  createNuanceClient,
122
129
  createKryptonClient,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jambonz/speech-utils",
3
- "version": "0.0.51",
3
+ "version": "0.1.1",
4
4
  "description": "TTS-related speech utilities for jambonz",
5
5
  "main": "index.js",
6
6
  "author": "Dave Horton",
@@ -12,6 +12,32 @@ const stats = {
12
12
  histogram: () => {}
13
13
  };
14
14
 
15
+ test('Verbio - get Access key and voices', async(t) => {
16
+ const fn = require('..');
17
+ const {client, getTtsVoices, getVerbioAccessToken} = fn(opts, logger);
18
+ if (!process.env.VERBIO_CLIENT_ID || !process.env.VERBIO_CLIENT_SECRET) {
19
+ t.pass('skipping Verbio test since no Verbio Keys provided');
20
+ t.end();
21
+ client.quit();
22
+ return;
23
+ }
24
+
25
+ try {
26
+ const credentials = {
27
+ client_id: process.env.VERBIO_CLIENT_ID,
28
+ client_secret: process.env.VERBIO_CLIENT_SECRET
29
+ };
30
+ let obj = await getVerbioAccessToken(credentials);
31
+ t.ok(obj.access_token , 'successfully received access token not from cache');
32
+ const voices = await getTtsVoices({vendor: 'verbio', credentials});
33
+ t.ok(voices && voices.length != 0, 'successfully received verbio voices');
34
+ } catch (err) {
35
+ console.error(err);
36
+ t.end(err);
37
+ }
38
+ client.quit();
39
+ });
40
+
15
41
  test('IBM - create access key', async(t) => {
16
42
  const fn = require('..');
17
43
  const {client, getIbmAccessToken} = fn(opts, logger);
package/test/synth.js CHANGED
@@ -162,6 +162,33 @@ test('AWS speech synth tests', async(t) => {
162
162
  client.quit();
163
163
  });
164
164
 
165
+ test('AWS speech synth tests by RoleArn', async(t) => {
166
+ const fn = require('..');
167
+ const {synthAudio, client} = fn(opts, logger);
168
+
169
+ if (!process.env.AWS_ROLE_ARN || !process.env.AWS_REGION) {
170
+ t.pass('skipping AWS speech synth tests by RoleArn since AWS_ROLE_ARN or AWS_REGION not provided');
171
+ return t.end();
172
+ }
173
+ try {
174
+ let opts = await synthAudio(stats, {
175
+ vendor: 'aws',
176
+ credentials: {
177
+ roleArn: process.env.AWS_ROLE_ARN,
178
+ region: process.env.AWS_REGION,
179
+ },
180
+ language: 'en-US',
181
+ voice: 'Joey',
182
+ text: 'This is a test. This is only a test',
183
+ });
184
+ t.ok(!opts.servedFromCache, `successfully synthesized aws by roleArn audio to ${opts.filePath}`);
185
+ } catch (err) {
186
+ console.error(err);
187
+ t.end(err);
188
+ }
189
+ client.quit();
190
+ });
191
+
165
192
  test('Azure speech synth tests', async(t) => {
166
193
  const fn = require('..');
167
194
  const {synthAudio, client} = fn(opts, logger);
@@ -643,6 +670,40 @@ test('whisper speech synth tests', async(t) => {
643
670
  language: 'en-US',
644
671
  voice: 'alloy',
645
672
  text,
673
+ renderForCaching: true
674
+ });
675
+ t.ok(!opts.servedFromCache, `successfully synthesized whisper audio to ${opts.filePath}`);
676
+
677
+ } catch (err) {
678
+ console.error(JSON.stringify(err));
679
+ t.end(err);
680
+ }
681
+ client.quit();
682
+ });
683
+
684
+ test('Verbio speech synth tests', async(t) => {
685
+ const fn = require('..');
686
+ const {synthAudio, client} = fn(opts, logger);
687
+
688
+ if (!process.env.VERBIO_CLIENT_ID || !process.env.VERBIO_CLIENT_SECRET) {
689
+ t.pass('skipping Verbio Synthesize test since no Verbio Keys provided');
690
+ t.end();
691
+ client.quit();
692
+ return;
693
+ }
694
+
695
+ const text = 'Hi there and welcome to jambones!';
696
+ try {
697
+ let opts = await synthAudio(stats, {
698
+ vendor: 'verbio',
699
+ credentials: {
700
+ client_id: process.env.VERBIO_CLIENT_ID,
701
+ client_secret: process.env.VERBIO_CLIENT_SECRET
702
+ },
703
+ language: 'en-US',
704
+ voice: 'tommy_en-us',
705
+ text,
706
+ renderForCaching: true
646
707
  });
647
708
  t.ok(!opts.servedFromCache, `successfully synthesized whisper audio to ${opts.filePath}`);
648
709