@jambonz/speech-utils 0.1.15 → 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 +18 -3
- 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
|
@@ -48,16 +48,25 @@ function getFileExtension({vendor, renderForCaching = false}) {
|
|
|
48
48
|
case 'deepgram':
|
|
49
49
|
case 'elevenlabs':
|
|
50
50
|
case 'rimlabs':
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
} else {
|
|
51
|
+
case 'playht':
|
|
52
|
+
if (renderForCaching || JAMBONES_DISABLE_TTS_STREAMING) {
|
|
54
53
|
return mp3Extension;
|
|
54
|
+
} else {
|
|
55
|
+
return r8Extension;
|
|
55
56
|
}
|
|
56
57
|
case 'nuance':
|
|
57
58
|
case 'nvidia':
|
|
58
59
|
case 'verbio':
|
|
59
60
|
return r8Extension;
|
|
60
61
|
default:
|
|
62
|
+
// If vendor is custom
|
|
63
|
+
if (vendor.startsWith('custom')) {
|
|
64
|
+
if (renderForCaching || JAMBONES_DISABLE_TTS_STREAMING) {
|
|
65
|
+
return mp3Extension;
|
|
66
|
+
} else {
|
|
67
|
+
return r8Extension;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
61
70
|
return mp3Extension;
|
|
62
71
|
}
|
|
63
72
|
}
|
|
@@ -89,6 +98,11 @@ function makeAwsKey(awsAccessKeyId) {
|
|
|
89
98
|
return `aws:${hash.digest('hex')}`;
|
|
90
99
|
}
|
|
91
100
|
|
|
101
|
+
function makePlayhtKey(apiKey) {
|
|
102
|
+
const hash = crypto.createHash('sha1');
|
|
103
|
+
hash.update(apiKey);
|
|
104
|
+
return `playht:${hash.digest('hex')}`;
|
|
105
|
+
}
|
|
92
106
|
function makeVerbioKey(client_id) {
|
|
93
107
|
const hash = crypto.createHash('sha1');
|
|
94
108
|
hash.update(client_id);
|
|
@@ -162,6 +176,7 @@ module.exports = {
|
|
|
162
176
|
makeSynthKey,
|
|
163
177
|
makeNuanceKey,
|
|
164
178
|
makeIbmKey,
|
|
179
|
+
makePlayhtKey,
|
|
165
180
|
makeAwsKey,
|
|
166
181
|
makeVerbioKey,
|
|
167
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) => {
|