node-red-contrib-tts-ultimate 1.0.32 → 1.0.33

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/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  [![Donate via PayPal](https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square)](https://www.paypal.me/techtoday)
4
4
 
5
+ <p>
6
+ <b>Version 1.0.33</b> October 2021<br/>
7
+ - NEW VOICE ENGINE: Microsoft Azure TTS.<br/>
8
+ <p>
5
9
  <p>
6
10
  <b>Version 1.0.32</b> September 2021<br/>
7
11
  - Fix few restore issues. Line-in restore fix and only when it was playing. Amazon Music and Spotify considered as stream instead of music queue.<br/>
package/README.md CHANGED
@@ -22,10 +22,11 @@
22
22
 
23
23
 
24
24
  ## DESCRIPTION
25
- This node transforms a text into a speech audio. You can hear the voice through Sonos.<br/>
26
- Uses Amazon Polly and Google TTS voices (even without credentials nor registration), and you can use it with **your own audio file** as well and it can be used **totally offline** even without the use of TTS, without internet connection.<br/>
25
+ This node transforms a text into a speech audio. You can generate an audio file, or hear the voice through Sonos, bluetooth speakers, web pages, etc.<br/>
26
+ Uses Amazon Polly, Google TTS voices (even without credentials nor registration) and Microsoft TTS Azure voices, and you can use it with **your own audio file** as well and it can be used **totally offline** even without the use of TTS, without internet connection.<br/>
27
+ The node can also create a ***TTS file (without the use of any Sonos device)***, to be read by third parties nodes.<br/>
27
28
  This is a major ***upgrade from the previously popular node SonosPollyTTS*** (SonosPollyTTS is not developed anymore).<br/>
28
- **Node v.10.0.0 or newer is needed**.
29
+ **Node v.12.0.0 or newer is needed**.
29
30
 
30
31
  [![Donate via PayPal](https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square)](https://www.paypal.me/techtoday)
31
32
 
@@ -33,7 +34,8 @@ This is a major ***upgrade from the previously popular node SonosPollyTTS*** (So
33
34
  * See <a href="https://github.com/Supergiovane/node-red-contrib-tts-ultimate/blob/master/CHANGELOG.md">here the changelog</a>
34
35
 
35
36
  ## FEATURES
36
- * **Amazon Voices, Gooogle Translate Voices and Google TTS Voices** are all supported, with all avaiables languages and genders.
37
+ * **Output audio file**: the node can just create the TTS file to be used by other nodes. In this case, you doesn't need to use Sonos as player.
38
+ * **Amazon Voices, Gooogle Translate Voices, Google TTS Voices and Microsoft TTS Azure voices** are all supported, with all avaiables languages and genders.
37
39
  * **Automatic grouping** is supported. You can group all players you want to play your announcements.
38
40
  * **Automatic discovery** of your players.
39
41
  * **Automatic resume of music** queue (including radio stations, but here, some users reports problem resuming ***radio stations*** and, because of lack of Sonos API documentation, the issue cannot currently be fixed), at exact track, at exact time.
@@ -69,16 +71,11 @@ PORT USED BY THE NODE ARE 1980 (DEFAULT) AND 1400 (FOR SONOS DISCOVER). <br/>
69
71
  PLEASE ALLOW MDNS AND UDP AS WELL
70
72
 
71
73
  **TTS Service**<br/>
72
- You can choose between Google (without credentials), Amazon AWS (Polly) or Google TTS (require credentials and registration to google) engines.<br/>
74
+ You can choose between Google (without credentials), Amazon AWS (Polly), Google TTS (require credentials and registration to google) or Microsoft Azure TTS engines.<br/>
73
75
  For Google TTS Engine, you can choose pitch and speed rate of the voice.
74
76
  <br/>
75
77
  <br/>
76
78
 
77
- * **TTS Service using Google (without credentials)**<br/>
78
- This is the simplest way. Just select the voice and you're done. You don't need any credential and you don't even need to be registered to any google service. The voice list is more limited than other services, but it works without hassles.
79
-
80
- <br/>
81
-
82
79
  * **TTS Service using Amazon AWS (Polly)**<br/>
83
80
  > HOW-TO in Deutsch: for german users, there is a very helpful how-to, where you can learn how to use the node and how to register to Amazon AWS Polly as well: here: https://technikkram.net/blog/2020/09/26/sonos-sprachausgabe-mit-raspberry-pi-node-red-und-amazon-polly-fuer-homematic-oder-knx-systeme
84
81
 
@@ -89,6 +86,11 @@ For Google TTS Engine, you can choose pitch and speed rate of the voice.
89
86
  AWS access Secret key.
90
87
  <br/>
91
88
 
89
+ * **TTS Service using Google (without credentials)**<br/>
90
+ This is the simplest way. Just select the voice and you're done. You don't need any credential and you don't even need to be registered to any google service. The voice list is more limited than other services, but it works without hassles.
91
+
92
+ <br/>
93
+
92
94
  * **TTS Service using Google TTS**<br/>
93
95
  For Google TTS Engine, you can choose pitch and speed rate of the voice.<br/>
94
96
  **Google credentials file path**<br/>
@@ -100,6 +102,14 @@ For Google TTS Engine, you can choose pitch and speed rate of the voice.
100
102
 
101
103
  <br/>
102
104
 
105
+ * **TTS Service using Microsot Azure TTS**<br/>
106
+ For Microsoft Azure TTS Engine, you need to have a microsoft account and register to the Azure portal.<br/>
107
+ After your registration here https://portal.azure.com, you need to create a Voice Service, then click to Keys and Endpoint and copy/paste the KEY and your Location (for example westus).<br/>
108
+ Then paste both into the TTS-Ultimate engine configuration window and restart node-red.
109
+
110
+ <br/>
111
+
112
+
103
113
  **Node-Red IP**<br/>
104
114
  set IP of your node-red machine. Sonos will connect to this address in order to play TTS. You can also write any value you want, for example 127.0.0.1 in this field (**don't leave this field blank in any case**), if you don't want to use Sonos as player. Please see below, the section **TTS-ULTIMATE NODE**, property **Player**.
105
115
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "node-red-contrib-tts-ultimate",
3
- "version": "1.0.32",
4
- "description": "Transforms the text in speech and hear it using Sonos player. Works with voices from Amazon, Google (without credentials as well) or your own voice. Update of the popular SonosPollyTTS node.",
3
+ "version": "1.0.33",
4
+ "description": "Transforms the text in speech and hear it using Sonos player or generate an audio file to be used with third parties nodes. Works with voices from Amazon, Google (without credentials as well), Microsoft TTS Azure, or your own voice. You can also only create a TTS file to be read by third party nodes. Update of the popular SonosPollyTTS node.",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "test": "test"
@@ -46,7 +46,8 @@
46
46
  "os": ">=0.1.1",
47
47
  "path": ">=0.12.7",
48
48
  "@google-cloud/text-to-speech": "3.3.1",
49
- "google-translate-tts": ">=0.2.1"
49
+ "google-translate-tts": ">=0.2.1",
50
+ "microsoft-cognitiveservices-speech-sdk": ">=1.18.1"
50
51
  },
51
52
  "devDependencies": {
52
53
  "eslint": ">=4.18.2",
@@ -22,7 +22,9 @@
22
22
  },
23
23
  credentials: {
24
24
  accessKey: { type: "text" },
25
- secretKey: { type: "password" }
25
+ secretKey: { type: "password" },
26
+ mssubscriptionKey: { type: "text" },
27
+ mslocation: { type: "text" }
26
28
  },
27
29
  label: function () {
28
30
  return this.name || "";
@@ -58,14 +60,21 @@
58
60
  // ##########################################################
59
61
  $("#node-config-input-ttsservice").change(function (e) {
60
62
  if ($("#node-config-input-ttsservice").val() == "polly") {
61
- $("#pollyForm").show();
62
63
  $("#GoogleForm").hide();
64
+ $("#microsoftAzureForm").hide();
65
+ $("#pollyForm").show();
63
66
  } else if ($("#node-config-input-ttsservice").val() == "googletts") {
67
+ $("#microsoftAzureForm").hide();
64
68
  $("#pollyForm").hide();
65
69
  $("#GoogleForm").show();
66
- }else{
70
+ } else if ($("#node-config-input-ttsservice").val() == "googletranslate") {
67
71
  $("#pollyForm").hide();
68
72
  $("#GoogleForm").hide();
73
+ $("#microsoftAzureForm").hide();
74
+ } else if ($("#node-config-input-ttsservice").val() == "microsoftazuretts") {
75
+ $("#pollyForm").hide();
76
+ $("#GoogleForm").hide();
77
+ $("#microsoftAzureForm").show();
69
78
  }
70
79
  });
71
80
  // ##########################################################
@@ -161,7 +170,8 @@
161
170
  <select id="node-config-input-ttsservice">
162
171
  <option value="polly">Amazon Polly</option>
163
172
  <option value="googletts">Google TTS</option>
164
- <option value="googletranslate">Google without authentication</option>
173
+ <option value="googletranslate">Google without authentication</option>
174
+ <option value="microsoftazuretts">Microsoft Azure TTS</option>
165
175
  </select>&nbsp&nbsp<b><span style="color:red"><i class="fa fa-question-circle"></i>&nbsp<a target="_blank" href="https://github.com/Supergiovane/node-red-contrib-tts-ultimate"><u>Help configure</u></a></span>
166
176
  </div>
167
177
  <div id="pollyForm">
@@ -180,6 +190,17 @@
180
190
  <input style="width:180px" id="googleCredentialsPath" type="file">
181
191
  </div>
182
192
  </div>
193
+ <div id="microsoftAzureForm">
194
+ <div class="form-row">
195
+ <label style="width:180px" for="node-config-input-mssubscriptionKey"><i class="fa fa-user"></i> Azure subscription key</label>
196
+ <input style="width:200px" type="text" id="node-config-input-mssubscriptionKey">
197
+ </div>
198
+ <div class="form-row">
199
+ <label style="width:180px" for="node-config-input-mslocation"><i class="fa fa-user"></i> Azure location (ex:westeurope)</label>
200
+ <input style="width:200px" type="text" id="node-config-input-mslocation">
201
+ </div>
202
+ </div>
203
+
183
204
  <div class="form-row">
184
205
  <label for="node-config-input-purgediratrestart"><i class="fa fa-folder-o"></i> TTS Cache</label>
185
206
  <select id="node-config-input-purgediratrestart">
@@ -4,6 +4,7 @@ module.exports = function (RED) {
4
4
  const AWS = require('aws-sdk');
5
5
  const GoogleTTS = require('@google-cloud/text-to-speech');
6
6
  const GoogleTranslate = require('google-translate-tts'); // TTS without credentials, limited to 200 chars per row.
7
+ const microsoftAzureTTS = require("microsoft-cognitiveservices-speech-sdk"); // 12/10/2021
7
8
  var fs = require('fs');
8
9
  var path = require("path");
9
10
  var formidable = require('formidable');
@@ -21,7 +22,7 @@ module.exports = function (RED) {
21
22
  node.noderedipaddress = typeof config.noderedipaddress === "undefined" ? "" : config.noderedipaddress;
22
23
  node.userDir = path.join(RED.settings.userDir, "sonospollyttsstorage"); // 09/03/2020 Storage of ttsultimate (otherwise, at each upgrade to a newer version, the node path is wiped out and recreated, loosing all custom files)
23
24
  node.whoIsUsingTheServer = ""; // Client node.id using the server, because only a ttsultimate node can use the serve at once.
24
- node.ttsservice = config.ttsservice || "polly";
25
+ node.ttsservice = config.ttsservice || "googletranslate";
25
26
 
26
27
  // 03/06/2019 you can select the temp dir
27
28
  //#region "SETUP PATHS"
@@ -76,26 +77,114 @@ module.exports = function (RED) {
76
77
  //#endregion
77
78
 
78
79
 
79
- // 23/12/2020 Set environment path of googleTTS
80
80
  //#region "INSTANTIATE SERVICE ENGINES"
81
+ // POLLY
81
82
  var params = {
82
83
  accessKeyId: node.credentials.accessKey,
83
84
  secretAccessKey: node.credentials.secretKey,
84
85
  apiVersion: '2016-06-10'
85
86
  };
86
- node.polly = new AWS.Polly(params);
87
+ try {
88
+ node.polly = new AWS.Polly(params);
89
+ RED.log.info("ttsultimate.config: Polly service enabled.")
90
+ } catch (error) {
91
+ RED.log.warn("ttsultimate.config: Polly service disabled. " + error.message)
92
+ }
87
93
 
94
+ // Google TTS with authentication
88
95
  if (node.ttsservice === "googletts") {
89
96
  try {
90
- // Set the environment variable
97
+ // 23/12/2020 Set environment path of googleTTS
91
98
  RED.log.info("ttsultimate-config: Google credentials are stored in the file " + path.join(node.userDir, "ttsultimategooglecredentials", "googlecredentials.json"));
92
99
  process.env.GOOGLE_APPLICATION_CREDENTIALS = path.join(node.userDir, "ttsultimategooglecredentials", "googlecredentials.json");
93
100
  } catch (error) {
101
+ RED.log.warn("ttsultimate.config: Google Translate free service error: " + error.message)
94
102
  }
95
103
 
96
104
  }
97
- node.googleTTS = new GoogleTTS.TextToSpeechClient();
98
- node.googleTranslateTTS = GoogleTranslate;
105
+ try {
106
+ node.googleTTS = new GoogleTTS.TextToSpeechClient();
107
+ RED.log.info("ttsultimate.config: Google Translate free service enabled. ")
108
+ } catch (error) {
109
+ RED.log.warn("ttsultimate.config: Google Translate free service disabled. " + error.message)
110
+ }
111
+
112
+
113
+ // Google Translate without authentication
114
+ try {
115
+ node.googleTranslateTTS = GoogleTranslate;
116
+ } catch (error) {
117
+ }
118
+
119
+
120
+ // 12/10/2021 Microsoft Azure TTS SpeechConfig.fromSubscription(subscriptionKey, serviceRegion)
121
+ // #########################################
122
+ node.setMicrosoftAzureVoice = function (_voiceName) {
123
+ console.log ("ILSIGNOREBUONO",_voiceName)
124
+ let speechConfig = microsoftAzureTTS.SpeechConfig.fromSubscription(node.credentials.mssubscriptionKey, node.credentials.mslocation);
125
+ speechConfig.speechSynthesisVoiceName = _voiceName;
126
+ speechConfig.speechSynthesisOutputFormat = microsoftAzureTTS.SpeechSynthesisOutputFormat.Audio16Khz32KBitRateMonoMp3;
127
+ node.microsoftAzureTTS = new microsoftAzureTTS.SpeechSynthesizer(speechConfig);
128
+ return node.microsoftAzureTTS;
129
+ }
130
+ try {
131
+ let speechConfig = microsoftAzureTTS.SpeechConfig.fromSubscription(node.credentials.mssubscriptionKey, node.credentials.mslocation);
132
+ speechConfig.speechSynthesisLanguage = "it-IT";
133
+ speechConfig.speechSynthesisVoiceName = "it-IT-IsabellaNeural";
134
+ speechConfig.speechSynthesisOutputFormat = microsoftAzureTTS.SpeechSynthesisOutputFormat.Audio16Khz32KBitRateMonoMp3;
135
+ node.microsoftAzureTTS = new microsoftAzureTTS.SpeechSynthesizer(speechConfig);
136
+ node.microsoftAzureTTSVoiceList = [];
137
+ if (node.ttsservice === "microsoftazuretts") {
138
+ // Get the voices
139
+ async function listVoicesAzure() {
140
+ const httpsAzure = require('https')
141
+ let options = {
142
+ hostname: node.credentials.mslocation + '.tts.speech.microsoft.com',
143
+ port: 443,
144
+ path: '/cognitiveservices/voices/list',
145
+ method: 'GET',
146
+ headers: {
147
+ 'Ocp-Apim-Subscription-Key': node.credentials.mssubscriptionKey
148
+ }
149
+ }
150
+ const reqAzure = httpsAzure.request(options, resVoices => {
151
+ var sChunkResponse = "";
152
+ resVoices.on('data', d => {
153
+ sChunkResponse += d.toString();
154
+ })
155
+ resVoices.on('end', () => {
156
+ try {
157
+ let oVoices = JSON.parse(sChunkResponse);
158
+ RED.log.info('ttsultimate-config: Microsoft Azure voices count: ' + oVoices.length);
159
+ for (let index = 0; index < oVoices.length; index++) {
160
+ const element = oVoices[index];
161
+ node.microsoftAzureTTSVoiceList.push({ name: element.ShortName + " (" + element.Gender + ")", id: element.ShortName })
162
+ }
163
+ } catch (error) {
164
+ RED.log.error('ttsultimate-config: listVoices: Error parsing Microsoft Azure TTS voices: ' + error.message);
165
+ node.microsoftAzureTTSVoiceList.push({ name: "Error parsing Microsoft Azure voices: " + error.message, id: "Ivy" });
166
+ }
167
+ })
168
+ })
169
+ reqAzure.on('error', error => {
170
+ RED.log.error('ttsultimate-config: listVoices: Error contacting Azure for getting the voices list: ' + error.message);
171
+ node.microsoftAzureTTSVoiceList.push({ name: "Error getting Microsoft Azure voices: " + error.message, id: "Ivy" })
172
+ reqAzure.end();
173
+ })
174
+ reqAzure.end();
175
+ };
176
+ RED.log.info("ttsultimate.config: Microsoft AzureTTS service enabled.")
177
+ try {
178
+ listVoicesAzure();
179
+ } catch (error) {
180
+ RED.log.error('ttsultimate-config: listVoices: Error getting Microsoft Azure voices: ' + error.message);
181
+ }
182
+ }
183
+ } catch (error) {
184
+ RED.log.warn("ttsultimate.config: Microsoft AzureTTS service disabled. " + error.message)
185
+ }
186
+ // #########################################
187
+
99
188
  //#endregion
100
189
 
101
190
 
@@ -313,6 +402,8 @@ module.exports = function (RED) {
313
402
  RED.log.error('ttsultimate-config: listVoices: Error getting google Translate voices ' + error.message);
314
403
  }
315
404
 
405
+ } else if (ttsservice === "microsoftazuretts") {
406
+ res.json(node.microsoftAzureTTSVoiceList);
316
407
  }
317
408
  });
318
409
 
@@ -531,7 +622,9 @@ module.exports = function (RED) {
531
622
  RED.nodes.registerType("ttsultimate-config", TTSConfigNode, {
532
623
  credentials: {
533
624
  accessKey: { type: "text" },
534
- secretKey: { type: "password" }
625
+ secretKey: { type: "password" },
626
+ mssubscriptionKey: { type: "text" },
627
+ mslocation: { type: "text" }
535
628
  }
536
629
  });
537
630
 
@@ -376,7 +376,7 @@ module.exports = function (RED) {
376
376
  node.msg.connectionerror = false;
377
377
  }
378
378
 
379
- node.setNodeStatus({ fill: 'green', shape: 'ring', text: 'Initialized.' });
379
+ node.setNodeStatus({ fill: 'grey', shape: 'ring', text: 'Initialized.' });
380
380
 
381
381
 
382
382
 
@@ -399,8 +399,8 @@ module.exports = function (RED) {
399
399
 
400
400
  if (_oTrack !== null) {
401
401
  // Do some checks on the track.
402
- if (_oTrack.hasOwnProperty("duration") && _oTrack.duration === 0 ||
403
- (_oTrack.uri.startsWith("x-sonosprog-http") || _oTrack.uri.startsWith("x-sonosapi-hls-static") || _oTrack.uri.startsWith("x-sonos-spotify"))) {
402
+ if (_oTrack.hasOwnProperty("duration") && _oTrack.duration === 0 ||
403
+ (_oTrack.uri.startsWith("x-sonosprog-http") || _oTrack.uri.startsWith("x-sonosapi-hls-static") || _oTrack.uri.startsWith("x-sonos-spotify"))) {
404
404
  // Stream
405
405
  _oTrack.trackType = "stream";
406
406
  } else if (_oTrack.hasOwnProperty("duration") && isNaN(_oTrack.duration)) {
@@ -566,6 +566,14 @@ module.exports = function (RED) {
566
566
  slow: false // optional
567
567
  };
568
568
  data = await synthesizeSpeechGoogleTranslate(node.server.googleTranslateTTS, params);
569
+ } else if (node.server.ttsservice === "microsoftazuretts") {
570
+ node.setNodeStatus({ fill: 'green', shape: 'ring', text: 'Downloading from Microsoft Azure TTS...' });
571
+ // VoiceId is: code
572
+ const params = {
573
+ text: msg,
574
+ voice: node.voiceId
575
+ };
576
+ data = await synthesizeSpeechMicrosoftAzureTTS(node.server.microsoftAzureTTS, params);
569
577
  }
570
578
  // Save the downloaded file into the cache
571
579
  fs.writeFileSync(sFileToBePlayed, data, function (error, result) {
@@ -988,6 +996,30 @@ module.exports = function (RED) {
988
996
  }
989
997
  };
990
998
 
999
+ // 12/10/2021 Microsoft Azure TTS Service
1000
+ async function synthesizeSpeechMicrosoftAzureTTS(ttsService, params) {
1001
+
1002
+ return new Promise(function (resolve, reject) {
1003
+ try {
1004
+
1005
+ // Microsoft fa sempre tutto diverso dagli altri, per cui mi tocca reinstanziare l'oggetto
1006
+ ttsService = node.server.setMicrosoftAzureVoice(params.voice);
1007
+
1008
+ ttsService.speakTextAsync(
1009
+ params.text,
1010
+ result => {
1011
+ ttsService.close();
1012
+ resolve (Buffer.from(result.audioData));
1013
+ },
1014
+ error => {
1015
+ ttsService.close();
1016
+ reject (error);
1017
+ });
1018
+ } catch (error) {
1019
+ reject (error);
1020
+ }
1021
+ });
1022
+ };
991
1023
 
992
1024
  function getFilename(text, _sVoice, isSSML, extension, _speakingpitch, _speakingrate) {
993
1025
  // Slug the text.