@jambonz/speech-utils 0.1.16 → 0.1.18
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 +44 -8
- package/lib/utils.js +6 -0
- package/package.json +1 -1
- package/test/synth.js +16 -8
package/lib/synth-audio.js
CHANGED
|
@@ -20,7 +20,8 @@ const {
|
|
|
20
20
|
createKryptonClient,
|
|
21
21
|
createRivaClient,
|
|
22
22
|
noopLogger,
|
|
23
|
-
makeFilePath
|
|
23
|
+
makeFilePath,
|
|
24
|
+
makePlayhtKey
|
|
24
25
|
} = require('./utils');
|
|
25
26
|
const getNuanceAccessToken = require('./get-nuance-access-token');
|
|
26
27
|
const getVerbioAccessToken = require('./get-verbio-token');
|
|
@@ -244,7 +245,7 @@ async function synthAudio(client, createHash, retrieveHash, logger, stats, { acc
|
|
|
244
245
|
});
|
|
245
246
|
break;
|
|
246
247
|
case 'playht':
|
|
247
|
-
audioBuffer = await synthPlayHT(logger, {
|
|
248
|
+
audioBuffer = await synthPlayHT(client, logger, {
|
|
248
249
|
credentials, options, stats, language, voice, text, renderForCaching, disableTtsStreaming, filePath
|
|
249
250
|
});
|
|
250
251
|
break;
|
|
@@ -755,12 +756,40 @@ const synthElevenlabs = async(logger, {
|
|
|
755
756
|
}
|
|
756
757
|
};
|
|
757
758
|
|
|
758
|
-
const synthPlayHT = async(logger, {
|
|
759
|
-
credentials, options, stats, voice, text, renderForCaching, disableTtsStreaming
|
|
759
|
+
const synthPlayHT = async(client, logger, {
|
|
760
|
+
credentials, options, stats, voice, language, text, renderForCaching, disableTtsStreaming
|
|
760
761
|
}) => {
|
|
761
762
|
const {api_key, user_id, voice_engine, options: credOpts} = credentials;
|
|
762
763
|
const opts = !!options && Object.keys(options).length !== 0 ? options : JSON.parse(credOpts || '{}');
|
|
763
764
|
|
|
765
|
+
let synthesizeUrl = 'https://api.play.ht/api/v2/tts/stream';
|
|
766
|
+
|
|
767
|
+
// If model is play3.0, the synthesizeUrl is got from authentication endpoint
|
|
768
|
+
if (voice_engine === 'Play3.0') {
|
|
769
|
+
try {
|
|
770
|
+
const post = bent('https://api.play.ht', 'POST', 'json', 201, {
|
|
771
|
+
'AUTHORIZATION': api_key,
|
|
772
|
+
'X-USER-ID': user_id,
|
|
773
|
+
'Accept': 'application/json'
|
|
774
|
+
});
|
|
775
|
+
const key = makePlayhtKey(api_key);
|
|
776
|
+
const url = await client.get(key);
|
|
777
|
+
if (!url) {
|
|
778
|
+
const {inference_address, expires_at_ms} = await post('/api/v3/auth');
|
|
779
|
+
synthesizeUrl = inference_address;
|
|
780
|
+
const expiry = Math.floor((expires_at_ms - Date.now()) / 1000 - 30);
|
|
781
|
+
await client.set(key, inference_address, 'EX', expiry);
|
|
782
|
+
} else {
|
|
783
|
+
// Use cached URL
|
|
784
|
+
synthesizeUrl = url;
|
|
785
|
+
}
|
|
786
|
+
} catch (err) {
|
|
787
|
+
logger.info({err}, 'synth PlayHT returned error for authentication version 3.0');
|
|
788
|
+
stats.increment('tts.count', ['vendor:playht', 'accepted:no']);
|
|
789
|
+
throw err;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
764
793
|
/* default to using the streaming interface, unless disabled by env var OR we want just a cache file */
|
|
765
794
|
if (!JAMBONES_DISABLE_TTS_STREAMING && !renderForCaching && !disableTtsStreaming) {
|
|
766
795
|
let params = '';
|
|
@@ -769,6 +798,7 @@ const synthPlayHT = async(logger, {
|
|
|
769
798
|
params += ',vendor=playht';
|
|
770
799
|
params += `,voice=${voice}`;
|
|
771
800
|
params += `,voice_engine=${voice_engine}`;
|
|
801
|
+
params += `,synthesize_url=${synthesizeUrl}`;
|
|
772
802
|
params += ',write_cache_file=1';
|
|
773
803
|
if (opts.quality) params += `,quality=${opts.quality}`;
|
|
774
804
|
if (opts.speed) params += `,speed=${opts.speed}`;
|
|
@@ -778,6 +808,8 @@ const synthPlayHT = async(logger, {
|
|
|
778
808
|
if (opts.voice_guidance) params += `,voice_guidance=${opts.voice_guidance}`;
|
|
779
809
|
if (opts.style_guidance) params += `,style_guidance=${opts.style_guidance}`;
|
|
780
810
|
if (opts.text_guidance) params += `,text_guidance=${opts.text_guidance}`;
|
|
811
|
+
if (opts.top_p) params += `,top_p=${opts.top_p}`;
|
|
812
|
+
if (opts.repetition_penalty) params += `,repetition_penalty=${opts.repetition_penalty}`;
|
|
781
813
|
params += '}';
|
|
782
814
|
|
|
783
815
|
return {
|
|
@@ -788,14 +820,18 @@ const synthPlayHT = async(logger, {
|
|
|
788
820
|
}
|
|
789
821
|
|
|
790
822
|
try {
|
|
791
|
-
const post = bent('
|
|
792
|
-
'
|
|
793
|
-
|
|
823
|
+
const post = bent('POST', 'buffer', {
|
|
824
|
+
...(voice_engine !== 'Play3.0' && {
|
|
825
|
+
'AUTHORIZATION': api_key,
|
|
826
|
+
'X-USER-ID': user_id,
|
|
827
|
+
}),
|
|
794
828
|
'Accept': 'audio/mpeg',
|
|
795
829
|
'Content-Type': 'application/json'
|
|
796
830
|
});
|
|
797
|
-
|
|
831
|
+
|
|
832
|
+
const mp3 = await post(synthesizeUrl, {
|
|
798
833
|
text,
|
|
834
|
+
...(voice_engine === 'Play3.0' && { language }),
|
|
799
835
|
voice,
|
|
800
836
|
voice_engine,
|
|
801
837
|
output_format: 'mp3',
|
package/lib/utils.js
CHANGED
|
@@ -98,6 +98,11 @@ function makeAwsKey(awsAccessKeyId) {
|
|
|
98
98
|
return `aws:${hash.digest('hex')}`;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
function makePlayhtKey(apiKey) {
|
|
102
|
+
const hash = crypto.createHash('sha1');
|
|
103
|
+
hash.update(apiKey);
|
|
104
|
+
return `playht:${hash.digest('hex')}`;
|
|
105
|
+
}
|
|
101
106
|
function makeVerbioKey(client_id) {
|
|
102
107
|
const hash = crypto.createHash('sha1');
|
|
103
108
|
hash.update(client_id);
|
|
@@ -171,6 +176,7 @@ module.exports = {
|
|
|
171
176
|
makeSynthKey,
|
|
172
177
|
makeNuanceKey,
|
|
173
178
|
makeIbmKey,
|
|
179
|
+
makePlayhtKey,
|
|
174
180
|
makeAwsKey,
|
|
175
181
|
makeVerbioKey,
|
|
176
182
|
getNuanceAccessToken,
|
package/package.json
CHANGED
package/test/synth.js
CHANGED
|
@@ -574,9 +574,9 @@ test('Elevenlabs speech synth tests', async(t) => {
|
|
|
574
574
|
t.end(err);
|
|
575
575
|
}
|
|
576
576
|
client.quit();
|
|
577
|
-
})
|
|
577
|
+
});
|
|
578
578
|
|
|
579
|
-
|
|
579
|
+
const testPlayHT = async(t, voice_engine) => {
|
|
580
580
|
const fn = require('..');
|
|
581
581
|
const {synthAudio, client} = fn(opts, logger);
|
|
582
582
|
|
|
@@ -584,26 +584,26 @@ test('PlayHT speech synth tests', async(t) => {
|
|
|
584
584
|
t.pass('skipping PlayHT speech synth tests since PLAYHT_API_KEY or PLAYHT_USER_ID is/are not provided');
|
|
585
585
|
return t.end();
|
|
586
586
|
}
|
|
587
|
-
const text = 'Hi there and welcome to jambones!';
|
|
587
|
+
const text = 'Hi there and welcome to jambones! ' + Date.now();
|
|
588
588
|
try {
|
|
589
|
-
|
|
589
|
+
const opts = await synthAudio(stats, {
|
|
590
590
|
vendor: 'playht',
|
|
591
591
|
credentials: {
|
|
592
592
|
api_key: process.env.PLAYHT_API_KEY,
|
|
593
593
|
user_id: process.env.PLAYHT_USER_ID,
|
|
594
|
-
voice_engine
|
|
594
|
+
voice_engine,
|
|
595
595
|
options: JSON.stringify({
|
|
596
|
-
quality:
|
|
596
|
+
quality: 'medium',
|
|
597
597
|
speed: 1,
|
|
598
598
|
seed: 1,
|
|
599
599
|
temperature: 1,
|
|
600
|
-
emotion:
|
|
600
|
+
emotion: 'female_happy',
|
|
601
601
|
voice_guidance: 3,
|
|
602
602
|
style_guidance: 20,
|
|
603
603
|
text_guidance: 1,
|
|
604
604
|
})
|
|
605
605
|
},
|
|
606
|
-
language: '
|
|
606
|
+
language: 'english',
|
|
607
607
|
voice: 's3://voice-cloning-zero-shot/d9ff78ba-d016-47f6-b0ef-dd630f59414e/female-cs/manifest.json',
|
|
608
608
|
text,
|
|
609
609
|
renderForCaching: true
|
|
@@ -615,6 +615,14 @@ test('PlayHT speech synth tests', async(t) => {
|
|
|
615
615
|
t.end(err);
|
|
616
616
|
}
|
|
617
617
|
client.quit();
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
test('PlayHT speech synth tests', async(t) => {
|
|
621
|
+
await testPlayHT(t, 'PlayHT2.0-turbo');
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
test('PlayHT3.0 speech synth tests', async(t) => {
|
|
625
|
+
await testPlayHT(t, 'Play3.0');
|
|
618
626
|
});
|
|
619
627
|
|
|
620
628
|
test('rimelabs speech synth tests', async(t) => {
|