@jambonz/speech-utils 0.1.16 → 0.1.17
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 +41 -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,37 @@ 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
|
+
}
|
|
783
|
+
} catch (err) {
|
|
784
|
+
logger.info({err}, 'synth PlayHT returned error for authentication version 3.0');
|
|
785
|
+
stats.increment('tts.count', ['vendor:playht', 'accepted:no']);
|
|
786
|
+
throw err;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
764
790
|
/* default to using the streaming interface, unless disabled by env var OR we want just a cache file */
|
|
765
791
|
if (!JAMBONES_DISABLE_TTS_STREAMING && !renderForCaching && !disableTtsStreaming) {
|
|
766
792
|
let params = '';
|
|
@@ -769,6 +795,7 @@ const synthPlayHT = async(logger, {
|
|
|
769
795
|
params += ',vendor=playht';
|
|
770
796
|
params += `,voice=${voice}`;
|
|
771
797
|
params += `,voice_engine=${voice_engine}`;
|
|
798
|
+
params += `,synthesize_url=${synthesizeUrl}`;
|
|
772
799
|
params += ',write_cache_file=1';
|
|
773
800
|
if (opts.quality) params += `,quality=${opts.quality}`;
|
|
774
801
|
if (opts.speed) params += `,speed=${opts.speed}`;
|
|
@@ -778,6 +805,8 @@ const synthPlayHT = async(logger, {
|
|
|
778
805
|
if (opts.voice_guidance) params += `,voice_guidance=${opts.voice_guidance}`;
|
|
779
806
|
if (opts.style_guidance) params += `,style_guidance=${opts.style_guidance}`;
|
|
780
807
|
if (opts.text_guidance) params += `,text_guidance=${opts.text_guidance}`;
|
|
808
|
+
if (opts.top_p) params += `,top_p=${opts.top_p}`;
|
|
809
|
+
if (opts.repetition_penalty) params += `,repetition_penalty=${opts.repetition_penalty}`;
|
|
781
810
|
params += '}';
|
|
782
811
|
|
|
783
812
|
return {
|
|
@@ -788,14 +817,18 @@ const synthPlayHT = async(logger, {
|
|
|
788
817
|
}
|
|
789
818
|
|
|
790
819
|
try {
|
|
791
|
-
const post = bent('
|
|
792
|
-
'
|
|
793
|
-
|
|
820
|
+
const post = bent('POST', 'buffer', {
|
|
821
|
+
...(voice_engine !== 'Play3.0' && {
|
|
822
|
+
'AUTHORIZATION': api_key,
|
|
823
|
+
'X-USER-ID': user_id,
|
|
824
|
+
}),
|
|
794
825
|
'Accept': 'audio/mpeg',
|
|
795
826
|
'Content-Type': 'application/json'
|
|
796
827
|
});
|
|
797
|
-
|
|
828
|
+
|
|
829
|
+
const mp3 = await post(synthesizeUrl, {
|
|
798
830
|
text,
|
|
831
|
+
...(voice_engine === 'Play3.0' && { language }),
|
|
799
832
|
voice,
|
|
800
833
|
voice_engine,
|
|
801
834
|
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) => {
|