@jambonz/speech-utils 1.0.8 → 1.0.9
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 +55 -1
- package/package.json +1 -1
- package/test/synth.js +28 -0
package/lib/synth-audio.js
CHANGED
|
@@ -80,7 +80,8 @@ async function synthAudio(client, createHash, retrieveHash, logger, stats, { acc
|
|
|
80
80
|
logger = logger || noopLogger;
|
|
81
81
|
|
|
82
82
|
assert.ok(['google', 'aws', 'polly', 'microsoft', 'wellsaid', 'nvidia', 'elevenlabs',
|
|
83
|
-
'whisper', 'deepgram', 'rimelabs', 'cartesia', 'inworld', 'resemble', 'murf', 'xai']
|
|
83
|
+
'whisper', 'deepgram', 'deepgramflux', 'rimelabs', 'cartesia', 'inworld', 'resemble', 'murf', 'xai']
|
|
84
|
+
.includes(vendor) ||
|
|
84
85
|
vendor.startsWith('custom'),
|
|
85
86
|
`synthAudio supported vendors are google, aws, microsoft, nvidia and wellsaid ..etc, not ${vendor}`);
|
|
86
87
|
if ('google' === vendor) {
|
|
@@ -124,6 +125,11 @@ async function synthAudio(client, createHash, retrieveHash, logger, stats, { acc
|
|
|
124
125
|
if (!credentials.deepgram_tts_uri) {
|
|
125
126
|
assert.ok(credentials.api_key, 'synthAudio requires api_key when deepgram is used');
|
|
126
127
|
}
|
|
128
|
+
} else if ('deepgramflux' === vendor) {
|
|
129
|
+
// Deepgram Flux TTS (/v2/speak); the flux model rides on `voice`/`model`
|
|
130
|
+
if (!credentials.deepgram_tts_uri) {
|
|
131
|
+
assert.ok(credentials.api_key, 'synthAudio requires api_key when deepgramflux is used');
|
|
132
|
+
}
|
|
127
133
|
} else if ('xai' === vendor) {
|
|
128
134
|
assert.ok(credentials.api_key, 'synthAudio requires api_key when xai is used');
|
|
129
135
|
} else if ('cartesia' === vendor) {
|
|
@@ -230,6 +236,10 @@ async function synthAudio(client, createHash, retrieveHash, logger, stats, { acc
|
|
|
230
236
|
audioData = await synthDeepgram(logger, {credentials, stats, model, key, text,
|
|
231
237
|
renderForCaching, disableTtsStreaming, disableTtsCache});
|
|
232
238
|
break;
|
|
239
|
+
case 'deepgramflux':
|
|
240
|
+
audioData = await synthDeepgramFlux(logger, {credentials, stats, model: model || voice, key, text,
|
|
241
|
+
renderForCaching, disableTtsStreaming, disableTtsCache});
|
|
242
|
+
break;
|
|
233
243
|
case 'xai':
|
|
234
244
|
audioData = await synthXai(logger, {
|
|
235
245
|
credentials, options, stats, language, voice, key, text, renderForCaching, disableTtsStreaming,
|
|
@@ -1140,6 +1150,50 @@ const synthDeepgram = async(logger, {credentials, stats, model, key, text, rende
|
|
|
1140
1150
|
}
|
|
1141
1151
|
};
|
|
1142
1152
|
|
|
1153
|
+
// Deepgram Flux TTS — the conversation-native model served from /v2/speak.
|
|
1154
|
+
// Streaming rides the mediajam deepgramflux dialect via a say: filePath; the
|
|
1155
|
+
// batch/cache path POSTs to /v2/speak (mp3 is batch-only for Flux).
|
|
1156
|
+
const synthDeepgramFlux = async(logger, {credentials, stats, model, key, text, renderForCaching,
|
|
1157
|
+
disableTtsStreaming, disableTtsCache}) => {
|
|
1158
|
+
const {api_key, deepgram_tts_uri} = credentials;
|
|
1159
|
+
if (!JAMBONES_DISABLE_TTS_STREAMING && !renderForCaching && !disableTtsStreaming) {
|
|
1160
|
+
let params = '{';
|
|
1161
|
+
params += `api_key=${api_key}`;
|
|
1162
|
+
params += `,playback_id=${key}`;
|
|
1163
|
+
params += ',vendor=deepgramflux';
|
|
1164
|
+
params += `,voice=${model}`;
|
|
1165
|
+
params += `,write_cache_file=${disableTtsCache ? 0 : 1}`;
|
|
1166
|
+
if (deepgram_tts_uri) params += `,endpoint=${deepgram_tts_uri}`;
|
|
1167
|
+
params += '}';
|
|
1168
|
+
|
|
1169
|
+
return {
|
|
1170
|
+
filePath: `say:${params}${text.replace(/\n/g, ' ')}`,
|
|
1171
|
+
servedFromCache: false,
|
|
1172
|
+
rtt: 0
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
try {
|
|
1176
|
+
const post = bent(deepgram_tts_uri || 'https://api.deepgram.com', 'POST', 'buffer', {
|
|
1177
|
+
// on-premise deepgram does not require to have api_key
|
|
1178
|
+
...(api_key && {'Authorization': `Token ${api_key}`}),
|
|
1179
|
+
'Accept': 'audio/mpeg',
|
|
1180
|
+
'Content-Type': 'application/json'
|
|
1181
|
+
});
|
|
1182
|
+
const audioContent = await post(`/v2/speak?model=${model}`, {
|
|
1183
|
+
text
|
|
1184
|
+
});
|
|
1185
|
+
return {
|
|
1186
|
+
audioContent,
|
|
1187
|
+
extension: 'mp3',
|
|
1188
|
+
sampleRate: 8000
|
|
1189
|
+
};
|
|
1190
|
+
} catch (err) {
|
|
1191
|
+
logger.info({err}, 'synth Deepgram Flux returned error');
|
|
1192
|
+
stats.increment('tts.count', ['vendor:deepgramflux', 'accepted:no']);
|
|
1193
|
+
throw err;
|
|
1194
|
+
}
|
|
1195
|
+
};
|
|
1196
|
+
|
|
1143
1197
|
const synthXai = async(logger, {
|
|
1144
1198
|
credentials, options, stats, language, voice, key, text, renderForCaching, disableTtsStreaming, disableTtsCache
|
|
1145
1199
|
}) => {
|
package/package.json
CHANGED
package/test/synth.js
CHANGED
|
@@ -1118,6 +1118,34 @@ test('Deepgram speech synth tests', async(t) => {
|
|
|
1118
1118
|
client.quit();
|
|
1119
1119
|
})
|
|
1120
1120
|
|
|
1121
|
+
test('Deepgram Flux speech synth tests', async(t) => {
|
|
1122
|
+
const fn = require('..');
|
|
1123
|
+
const {synthAudio, client} = fn(opts, logger);
|
|
1124
|
+
|
|
1125
|
+
if (!process.env.DEEPGRAM_API_KEY) {
|
|
1126
|
+
t.pass('skipping Deepgram Flux speech synth tests since DEEPGRAM_API_KEY');
|
|
1127
|
+
return t.end();
|
|
1128
|
+
}
|
|
1129
|
+
const text = 'Hi there and welcome to jambones!';
|
|
1130
|
+
try {
|
|
1131
|
+
const opts = await synthAudio(stats, {
|
|
1132
|
+
vendor: 'deepgramflux',
|
|
1133
|
+
credentials: {
|
|
1134
|
+
api_key: process.env.DEEPGRAM_API_KEY
|
|
1135
|
+
},
|
|
1136
|
+
model: process.env.DEEPGRAM_FLUX_MODEL || 'flux-alexis-en',
|
|
1137
|
+
text,
|
|
1138
|
+
renderForCaching: true
|
|
1139
|
+
});
|
|
1140
|
+
t.ok(!opts.servedFromCache, `successfully synthesized deepgramflux audio to ${opts.filePath}`);
|
|
1141
|
+
|
|
1142
|
+
} catch (err) {
|
|
1143
|
+
console.error(JSON.stringify(err));
|
|
1144
|
+
t.end(err);
|
|
1145
|
+
}
|
|
1146
|
+
client.quit();
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1121
1149
|
test('xai speech synth tests', async(t) => {
|
|
1122
1150
|
const fn = require('..');
|
|
1123
1151
|
const {synthAudio, client} = fn(opts, logger);
|