@jambonz/speech-utils 0.1.12 → 0.1.15
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/.github/workflows/ci.yml +5 -0
- package/lib/config.js +2 -0
- package/lib/synth-audio.js +36 -6
- package/lib/utils.js +9 -7
- package/package.json +1 -1
package/.github/workflows/ci.yml
CHANGED
|
@@ -13,6 +13,11 @@ jobs:
|
|
|
13
13
|
with:
|
|
14
14
|
node-version: lts/*
|
|
15
15
|
- run: npm install
|
|
16
|
+
- name: Install Docker Compose
|
|
17
|
+
run: |
|
|
18
|
+
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
|
19
|
+
sudo chmod +x /usr/local/bin/docker-compose
|
|
20
|
+
docker-compose --version
|
|
16
21
|
- run: npm run jslint
|
|
17
22
|
- run: sudo apt update && sudo apt install -y squid
|
|
18
23
|
- run: sudo cp test/squid.conf /etc/squid/squid.conf
|
package/lib/config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const JAMBONES_TTS_TRIM_SILENCE = process.env.JAMBONES_TTS_TRIM_SILENCE;
|
|
2
2
|
const JAMBONES_DISABLE_TTS_STREAMING = process.env.JAMBONES_DISABLE_TTS_STREAMING;
|
|
3
3
|
const JAMBONES_DISABLE_AZURE_TTS_STREAMING = process.env.JAMBONES_DISABLE_AZURE_TTS_STREAMING;
|
|
4
|
+
const JAMBONES_EAGERLY_PRE_CACHE_AUDIO = process.env.JAMBONES_EAGERLY_PRE_CACHE_AUDIO;
|
|
4
5
|
|
|
5
6
|
const JAMBONES_HTTP_PROXY_IP = process.env.JAMBONES_HTTP_PROXY_IP;
|
|
6
7
|
const JAMBONES_HTTP_PROXY_PORT = process.env.JAMBONES_HTTP_PROXY_PORT;
|
|
@@ -18,6 +19,7 @@ module.exports = {
|
|
|
18
19
|
JAMBONES_HTTP_PROXY_IP,
|
|
19
20
|
JAMBONES_HTTP_PROXY_PORT,
|
|
20
21
|
JAMBONES_TTS_CACHE_DURATION_MINS,
|
|
22
|
+
JAMBONES_EAGERLY_PRE_CACHE_AUDIO,
|
|
21
23
|
TMP_FOLDER,
|
|
22
24
|
HTTP_TIMEOUT
|
|
23
25
|
};
|
package/lib/synth-audio.js
CHANGED
|
@@ -44,6 +44,7 @@ const {
|
|
|
44
44
|
JAMBONES_HTTP_PROXY_IP,
|
|
45
45
|
JAMBONES_HTTP_PROXY_PORT,
|
|
46
46
|
JAMBONES_TTS_CACHE_DURATION_MINS,
|
|
47
|
+
JAMBONES_EAGERLY_PRE_CACHE_AUDIO,
|
|
47
48
|
} = require('./config');
|
|
48
49
|
const EXPIRES = JAMBONES_TTS_CACHE_DURATION_MINS;
|
|
49
50
|
const OpenAI = require('openai');
|
|
@@ -86,7 +87,7 @@ const trimTrailingSilence = (buffer) => {
|
|
|
86
87
|
*/
|
|
87
88
|
async function synthAudio(client, createHash, retrieveHash, logger, stats, { account_sid,
|
|
88
89
|
vendor, language, voice, gender, text, engine, salt, model, credentials, deploymentId,
|
|
89
|
-
disableTtsCache, renderForCaching, disableTtsStreaming, options
|
|
90
|
+
disableTtsCache, renderForCaching = false, disableTtsStreaming, options
|
|
90
91
|
}) {
|
|
91
92
|
let audioBuffer;
|
|
92
93
|
let servedFromCache = false;
|
|
@@ -151,21 +152,48 @@ async function synthAudio(client, createHash, retrieveHash, logger, stats, { acc
|
|
|
151
152
|
assert.ok(voice, 'synthAudio requires voice when verbio is used');
|
|
152
153
|
assert.ok(credentials.client_id, 'synthAudio requires client_id when verbio is used');
|
|
153
154
|
assert.ok(credentials.client_secret, 'synthAudio requires client_secret when verbio is used');
|
|
155
|
+
} else if ('deepgram' === vendor) {
|
|
156
|
+
if (!credentials.deepgram_tts_uri) {
|
|
157
|
+
assert.ok(credentials.api_key, 'synthAudio requires api_key when deepgram is used');
|
|
158
|
+
}
|
|
159
|
+
|
|
154
160
|
}
|
|
161
|
+
|
|
155
162
|
const key = makeSynthKey({
|
|
156
163
|
account_sid,
|
|
157
164
|
vendor,
|
|
158
165
|
language: language || '',
|
|
159
166
|
voice: voice || deploymentId,
|
|
160
167
|
engine,
|
|
161
|
-
text
|
|
168
|
+
text,
|
|
169
|
+
renderForCaching
|
|
162
170
|
});
|
|
163
171
|
let filePath;
|
|
164
|
-
filePath = makeFilePath(vendor, key, salt);
|
|
172
|
+
filePath = makeFilePath({vendor, key, salt, renderForCaching});
|
|
165
173
|
debug(`synth key is ${key}`);
|
|
166
174
|
let cached;
|
|
167
175
|
if (!disableTtsCache) {
|
|
168
176
|
cached = await client.get(key);
|
|
177
|
+
/**
|
|
178
|
+
* If we are using tts streaming and also precaching audio, audio could have been cached by streaming (r8)
|
|
179
|
+
* or here in speech-utils due to precaching (mp3), so we need to check for both keys.
|
|
180
|
+
*/
|
|
181
|
+
if (!cached && JAMBONES_EAGERLY_PRE_CACHE_AUDIO) {
|
|
182
|
+
const preCachekey = makeSynthKey({
|
|
183
|
+
account_sid,
|
|
184
|
+
vendor,
|
|
185
|
+
language: language || '',
|
|
186
|
+
voice: voice || deploymentId,
|
|
187
|
+
engine,
|
|
188
|
+
text,
|
|
189
|
+
renderForCaching: true
|
|
190
|
+
});
|
|
191
|
+
cached = await client.get(preCachekey);
|
|
192
|
+
if (cached) {
|
|
193
|
+
// Precache audio is available update filpath with precache file extension.
|
|
194
|
+
filePath = makeFilePath({vendor, key, salt, renderForCaching: true});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
169
197
|
}
|
|
170
198
|
if (cached) {
|
|
171
199
|
// found in cache - extend the expiry and use it
|
|
@@ -909,13 +937,14 @@ const synthWhisper = async(logger, {credentials, stats, voice, text, renderForCa
|
|
|
909
937
|
};
|
|
910
938
|
|
|
911
939
|
const synthDeepgram = async(logger, {credentials, stats, model, text, renderForCaching, disableTtsStreaming}) => {
|
|
912
|
-
const {api_key} = credentials;
|
|
940
|
+
const {api_key, deepgram_tts_uri} = credentials;
|
|
913
941
|
if (!JAMBONES_DISABLE_TTS_STREAMING && !renderForCaching && !disableTtsStreaming) {
|
|
914
942
|
let params = '';
|
|
915
943
|
params += `{api_key=${api_key}`;
|
|
916
944
|
params += ',vendor=deepgram';
|
|
917
945
|
params += `,voice=${model}`;
|
|
918
946
|
params += ',write_cache_file=1';
|
|
947
|
+
if (deepgram_tts_uri) params += `,endpoint=${deepgram_tts_uri}`;
|
|
919
948
|
params += '}';
|
|
920
949
|
|
|
921
950
|
return {
|
|
@@ -925,8 +954,9 @@ const synthDeepgram = async(logger, {credentials, stats, model, text, renderForC
|
|
|
925
954
|
};
|
|
926
955
|
}
|
|
927
956
|
try {
|
|
928
|
-
const post = bent('https://api.deepgram.com', 'POST', 'buffer', {
|
|
929
|
-
|
|
957
|
+
const post = bent(deepgram_tts_uri || 'https://api.deepgram.com', 'POST', 'buffer', {
|
|
958
|
+
// on-premise deepgram does not require to have api_key
|
|
959
|
+
...(api_key && {'Authorization': `Token ${api_key}`}),
|
|
930
960
|
'Accept': 'audio/mpeg',
|
|
931
961
|
'Content-Type': 'application/json'
|
|
932
962
|
});
|
package/lib/utils.js
CHANGED
|
@@ -16,29 +16,31 @@ const debug = require('debug')('jambonz:realtimedb-helpers');
|
|
|
16
16
|
*/
|
|
17
17
|
//const nuanceClientMap = new Map();
|
|
18
18
|
|
|
19
|
-
function makeSynthKey({
|
|
19
|
+
function makeSynthKey({
|
|
20
|
+
account_sid = '', vendor, language, voice, engine = '', text,
|
|
21
|
+
renderForCaching = false}) {
|
|
20
22
|
const hash = crypto.createHash('sha1');
|
|
21
23
|
hash.update(`${language}:${vendor}:${voice}:${engine}:${text}`);
|
|
22
24
|
const hexHashKey = hash.digest('hex');
|
|
23
25
|
const accountKey = account_sid ? `:${account_sid}` : '';
|
|
24
|
-
const namespace = vendor.startsWith('custom') ? vendor : getFileExtension(vendor);
|
|
26
|
+
const namespace = vendor.startsWith('custom') ? vendor : getFileExtension({vendor, renderForCaching});
|
|
25
27
|
const key = `tts${accountKey}:${namespace}:${hexHashKey}`;
|
|
26
28
|
return key;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
function makeFilePath(vendor, key, salt = '') {
|
|
30
|
-
const extension = getFileExtension(vendor);
|
|
31
|
+
function makeFilePath({vendor, key, salt = '', renderForCaching = false}) {
|
|
32
|
+
const extension = getFileExtension({vendor, renderForCaching});
|
|
31
33
|
return `${TMP_FOLDER}/${key.replace('tts:', `tts-${salt}`)}.${extension}`;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
function getFileExtension(vendor) {
|
|
36
|
+
function getFileExtension({vendor, renderForCaching = false}) {
|
|
35
37
|
const mp3Extension = 'mp3';
|
|
36
38
|
const r8Extension = 'r8';
|
|
37
39
|
|
|
38
40
|
switch (vendor) {
|
|
39
41
|
case 'azure':
|
|
40
42
|
case 'microsoft':
|
|
41
|
-
if (!JAMBONES_DISABLE_TTS_STREAMING || JAMBONES_TTS_TRIM_SILENCE) {
|
|
43
|
+
if (!renderForCaching && !JAMBONES_DISABLE_TTS_STREAMING || JAMBONES_TTS_TRIM_SILENCE) {
|
|
42
44
|
return r8Extension;
|
|
43
45
|
} else {
|
|
44
46
|
return mp3Extension;
|
|
@@ -46,7 +48,7 @@ function getFileExtension(vendor) {
|
|
|
46
48
|
case 'deepgram':
|
|
47
49
|
case 'elevenlabs':
|
|
48
50
|
case 'rimlabs':
|
|
49
|
-
if (!JAMBONES_DISABLE_TTS_STREAMING) {
|
|
51
|
+
if (!renderForCaching && !JAMBONES_DISABLE_TTS_STREAMING) {
|
|
50
52
|
return r8Extension;
|
|
51
53
|
} else {
|
|
52
54
|
return mp3Extension;
|