@jambonz/speech-utils 0.0.32 → 0.0.34
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/lib/synth-audio.js +57 -6
- package/package.json +13 -13
- package/test/synth.js +27 -0
package/lib/synth-audio.js
CHANGED
|
@@ -76,7 +76,8 @@ const trimTrailingSilence = (buffer) => {
|
|
|
76
76
|
* the synthesized audio, and a variable indicating whether it was served from cache
|
|
77
77
|
*/
|
|
78
78
|
async function synthAudio(client, logger, stats, { account_sid,
|
|
79
|
-
vendor, language, voice, gender, text, engine, salt, model, credentials, deploymentId,
|
|
79
|
+
vendor, language, voice, gender, text, engine, salt, model, credentials, deploymentId,
|
|
80
|
+
disableTtsCache, renderForCaching, options
|
|
80
81
|
}) {
|
|
81
82
|
let audioBuffer;
|
|
82
83
|
let servedFromCache = false;
|
|
@@ -84,7 +85,7 @@ async function synthAudio(client, logger, stats, { account_sid,
|
|
|
84
85
|
logger = logger || noopLogger;
|
|
85
86
|
|
|
86
87
|
assert.ok(['google', 'aws', 'polly', 'microsoft',
|
|
87
|
-
'wellsaid', 'nuance', 'nvidia', 'ibm', 'elevenlabs', 'whisper'].includes(vendor) ||
|
|
88
|
+
'wellsaid', 'nuance', 'nvidia', 'ibm', 'elevenlabs', 'whisper', 'deepgram'].includes(vendor) ||
|
|
88
89
|
vendor.startsWith('custom'),
|
|
89
90
|
`synthAudio supported vendors are google, aws, microsoft, nuance, nvidia and wellsaid, not ${vendor}`);
|
|
90
91
|
if ('google' === vendor) {
|
|
@@ -194,11 +195,22 @@ async function synthAudio(client, logger, stats, { account_sid,
|
|
|
194
195
|
audioBuffer = await synthWellSaid(logger, {credentials, stats, language, voice, text, filePath});
|
|
195
196
|
break;
|
|
196
197
|
case 'elevenlabs':
|
|
197
|
-
audioBuffer = await synthElevenlabs(logger, {
|
|
198
|
+
audioBuffer = await synthElevenlabs(logger, {
|
|
199
|
+
credentials, options, stats, language, voice, text, renderForCaching, filePath
|
|
200
|
+
});
|
|
201
|
+
if (typeof audioBuffer === 'object' && audioBuffer.filePath) {
|
|
202
|
+
return audioBuffer;
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
audioBuffer = await synthElevenlabs(logger, {credentials, options, stats, language, voice, text, filePath});
|
|
206
|
+
}
|
|
198
207
|
break;
|
|
199
208
|
case 'whisper':
|
|
200
209
|
audioBuffer = await synthWhisper(logger, {credentials, stats, voice, text});
|
|
201
210
|
break;
|
|
211
|
+
case 'deepgram':
|
|
212
|
+
audioBuffer = await synthDeepgram(logger, {credentials, stats, model, text});
|
|
213
|
+
break;
|
|
202
214
|
case vendor.startsWith('custom') ? vendor : 'cant_match_value':
|
|
203
215
|
({ audioBuffer, filePath } = await synthCustomVendor(logger,
|
|
204
216
|
{credentials, stats, language, voice, text, filePath}));
|
|
@@ -591,9 +603,29 @@ const synthCustomVendor = async(logger, {credentials, stats, language, voice, te
|
|
|
591
603
|
}
|
|
592
604
|
};
|
|
593
605
|
|
|
594
|
-
const synthElevenlabs = async(logger, {credentials, options, stats, language, voice, text}) => {
|
|
606
|
+
const synthElevenlabs = async(logger, {credentials, options, stats, language, voice, text, renderForCaching}) => {
|
|
595
607
|
const {api_key, model_id, options: credOpts} = credentials;
|
|
596
608
|
const opts = !!options && Object.keys(options).length !== 0 ? options : JSON.parse(credOpts || '{}');
|
|
609
|
+
|
|
610
|
+
/* if the env is set to stream then bag out, unless we are specifically rendering to generate a cache file */
|
|
611
|
+
if (process.env.JAMBONES_ELEVENLABS_STREAMING && !renderForCaching) {
|
|
612
|
+
let params = '';
|
|
613
|
+
params += `{api_key=${api_key}`;
|
|
614
|
+
params += `,model_id=${model_id}`;
|
|
615
|
+
params += `,optimize_streaming_latency=${opts.optimize_streaming_latency || 2}`;
|
|
616
|
+
if (opts.voice_settings?.similarity_boost) params += `,similarity_boost=${opts.voice_settings.similarity_boost}`;
|
|
617
|
+
if (opts.voice_settings?.stability) params += `,stability=${opts.voice_settings.stability}`;
|
|
618
|
+
if (opts.voice_settings?.style) params += `,style=${opts.voice_settings.style}`;
|
|
619
|
+
if (opts.voice_settings?.use_speaker_boost === false) params += ',use_speaker_boost=false';
|
|
620
|
+
params += '}';
|
|
621
|
+
|
|
622
|
+
return {
|
|
623
|
+
filePath: `say:${params}${text.replace(/\n/g, ' ')}`,
|
|
624
|
+
servedFromCache: false,
|
|
625
|
+
rtt: 0
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
|
|
597
629
|
const optimize_streaming_latency = opts.optimize_streaming_latency ?
|
|
598
630
|
`?optimize_streaming_latency=${opts.optimize_streaming_latency}` : '';
|
|
599
631
|
try {
|
|
@@ -640,8 +672,27 @@ const synthWhisper = async(logger, {credentials, stats, voice, text}) => {
|
|
|
640
672
|
stats.increment('tts.count', ['vendor:openai', 'accepted:no']);
|
|
641
673
|
throw err;
|
|
642
674
|
}
|
|
643
|
-
}
|
|
644
|
-
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
const synthDeepgram = async(logger, {credentials, stats, model, text}) => {
|
|
678
|
+
const {api_key} = credentials;
|
|
679
|
+
try {
|
|
680
|
+
const post = bent('https://api.beta.deepgram.com', 'POST', 'buffer', {
|
|
681
|
+
'Authorization': `Token ${api_key}`,
|
|
682
|
+
'Accept': 'audio/mpeg',
|
|
683
|
+
'Content-Type': 'application/json'
|
|
684
|
+
});
|
|
685
|
+
const mp3 = await post(`/v1/speak?model=${model}`, {
|
|
686
|
+
text
|
|
687
|
+
});
|
|
688
|
+
return mp3;
|
|
689
|
+
} catch (err) {
|
|
690
|
+
logger.info({err}, 'synth Deepgram returned error');
|
|
691
|
+
stats.increment('tts.count', ['vendor:deepgram', 'accepted:no']);
|
|
692
|
+
throw err;
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
|
|
645
696
|
const getFileExtFromMime = (mime) => {
|
|
646
697
|
switch (mime) {
|
|
647
698
|
case 'audio/wav':
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jambonz/speech-utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.34",
|
|
4
4
|
"description": "TTS-related speech utilities for jambonz",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"author": "Dave Horton",
|
|
@@ -24,26 +24,26 @@
|
|
|
24
24
|
},
|
|
25
25
|
"homepage": "https://github.com/jambonz/speech-utils#readme",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@aws-sdk/client-polly": "^3.
|
|
28
|
-
"@aws-sdk/client-sts": "^3.
|
|
29
|
-
"@google-cloud/text-to-speech": "^
|
|
30
|
-
"@grpc/grpc-js": "^1.
|
|
27
|
+
"@aws-sdk/client-polly": "^3.496.0",
|
|
28
|
+
"@aws-sdk/client-sts": "^3.496.0",
|
|
29
|
+
"@google-cloud/text-to-speech": "^5.0.2",
|
|
30
|
+
"@grpc/grpc-js": "^1.9.14",
|
|
31
31
|
"@jambonz/realtimedb-helpers": "^0.8.7",
|
|
32
32
|
"bent": "^7.3.12",
|
|
33
33
|
"debug": "^4.3.4",
|
|
34
|
-
"form-urlencoded": "^6.1.
|
|
34
|
+
"form-urlencoded": "^6.1.4",
|
|
35
35
|
"google-protobuf": "^3.21.2",
|
|
36
36
|
"ibm-watson": "^8.0.0",
|
|
37
|
-
"microsoft-cognitiveservices-speech-sdk": "1.
|
|
38
|
-
"openai": "^4.
|
|
39
|
-
"undici": "^
|
|
37
|
+
"microsoft-cognitiveservices-speech-sdk": "1.34.0",
|
|
38
|
+
"openai": "^4.25.0",
|
|
39
|
+
"undici": "^6.4.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"config": "^3.3.
|
|
43
|
-
"eslint": "^8.
|
|
42
|
+
"config": "^3.3.10",
|
|
43
|
+
"eslint": "^8.56.0",
|
|
44
44
|
"eslint-plugin-promise": "^6.1.1",
|
|
45
45
|
"nyc": "^15.1.0",
|
|
46
|
-
"pino": "^
|
|
47
|
-
"tape": "^5.
|
|
46
|
+
"pino": "^8.17.0",
|
|
47
|
+
"tape": "^5.7.3"
|
|
48
48
|
}
|
|
49
49
|
}
|
package/test/synth.js
CHANGED
|
@@ -514,6 +514,33 @@ test('whisper speech synth tests', async(t) => {
|
|
|
514
514
|
client.quit();
|
|
515
515
|
})
|
|
516
516
|
|
|
517
|
+
test('Deepgram speech synth tests', async(t) => {
|
|
518
|
+
const fn = require('..');
|
|
519
|
+
const {synthAudio, client} = fn(opts, logger);
|
|
520
|
+
|
|
521
|
+
if (!process.env.DEEPGRAM_API_KEY) {
|
|
522
|
+
t.pass('skipping Deepgram speech synth tests since DEEPGRAM_API_KEY');
|
|
523
|
+
return t.end();
|
|
524
|
+
}
|
|
525
|
+
const text = 'Hi there and welcome to jambones!';
|
|
526
|
+
try {
|
|
527
|
+
let opts = await synthAudio(stats, {
|
|
528
|
+
vendor: 'deepgram',
|
|
529
|
+
credentials: {
|
|
530
|
+
api_key: process.env.DEEPGRAM_API_KEY
|
|
531
|
+
},
|
|
532
|
+
model: 'alpha-asteria-en-v2',
|
|
533
|
+
text,
|
|
534
|
+
});
|
|
535
|
+
t.ok(!opts.servedFromCache, `successfully synthesized deepgram audio to ${opts.filePath}`);
|
|
536
|
+
|
|
537
|
+
} catch (err) {
|
|
538
|
+
console.error(JSON.stringify(err));
|
|
539
|
+
t.end(err);
|
|
540
|
+
}
|
|
541
|
+
client.quit();
|
|
542
|
+
})
|
|
543
|
+
|
|
517
544
|
test('TTS Cache tests', async(t) => {
|
|
518
545
|
const fn = require('..');
|
|
519
546
|
const {purgeTtsCache, getTtsSize, client} = fn(opts, logger);
|