@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.
@@ -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('https://api.play.ht', 'POST', 'buffer', {
792
- 'AUTHORIZATION': api_key,
793
- 'X-USER-ID': user_id,
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
- const mp3 = await post('/api/v2/tts/stream', {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jambonz/speech-utils",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "TTS-related speech utilities for jambonz",
5
5
  "main": "index.js",
6
6
  "author": "Dave Horton",
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
- test('PlayHT speech synth tests', async(t) => {
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
- let opts = await synthAudio(stats, {
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: 'PlayHT2.0-turbo',
594
+ voice_engine,
595
595
  options: JSON.stringify({
596
- quality: "medium",
596
+ quality: 'medium',
597
597
  speed: 1,
598
598
  seed: 1,
599
599
  temperature: 1,
600
- emotion: "female_happy",
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: 'en-US',
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) => {