add speed parameter

This commit is contained in:
ANLGBOY
2025-11-19 19:42:24 +09:00
parent c31b6745e4
commit 8518b839c1
30 changed files with 246 additions and 61 deletions

View File

@@ -4,6 +4,8 @@ Node.js implementation for TTS inference. Uses ONNX Runtime to generate speech f
## 📰 Update News
**2025.11.19** - Added `--speed` parameter to control speech synthesis speed (default: 1.05, recommended range: 0.9-1.5).
**2025.11.19** - Added automatic text chunking for long-form inference. Long texts are split into chunks and synthesized with natural pauses.
## Requirements

View File

@@ -15,6 +15,7 @@ function parseArgs() {
useGpu: false,
onnxDir: 'assets/onnx',
totalStep: 5,
speed: 1.05,
nTest: 4,
voiceStyle: ['assets/voice_styles/M1.json'],
text: ['This morning, I took a walk in the park, and the sound of the birds and the breeze was so pleasant that I stopped for a long time just to listen.'],
@@ -32,6 +33,8 @@ function parseArgs() {
args.onnxDir = process.argv[++i];
} else if (arg === '--total-step' && i + 1 < process.argv.length) {
args.totalStep = parseInt(process.argv[++i]);
} else if (arg === '--speed' && i + 1 < process.argv.length) {
args.speed = parseFloat(process.argv[++i]);
} else if (arg === '--n-test' && i + 1 < process.argv.length) {
args.nTest = parseInt(process.argv[++i]);
} else if (arg === '--voice-style' && i + 1 < process.argv.length) {
@@ -55,6 +58,7 @@ async function main() {
// --- 1. Parse arguments --- //
const args = parseArgs();
const totalStep = args.totalStep;
const speed = args.speed;
const nTest = args.nTest;
const saveDir = args.saveDir;
const voiceStylePaths = args.voiceStyle.map(p => path.resolve(__dirname, p));
@@ -79,9 +83,9 @@ async function main() {
const { wav, duration } = await timer('Generating speech from text', async () => {
if (batch) {
return await textToSpeech.batch(textList, style, totalStep);
return await textToSpeech.batch(textList, style, totalStep, speed);
} else {
return await textToSpeech.call(textList[0], style, totalStep);
return await textToSpeech.call(textList[0], style, totalStep, speed);
}
});

View File

@@ -114,7 +114,7 @@ class TextToSpeech {
return { noisyLatent, latentMask };
}
async _infer(textList, style, totalStep) {
async _infer(textList, style, totalStep, speed = 1.05) {
if (textList.length !== style.ttl.dims[0]) {
throw new Error('Number of texts must match number of style vectors');
}
@@ -133,6 +133,11 @@ class TextToSpeech {
const durOnnx = Array.from(dpResult.duration.data);
// Apply speed factor to duration
for (let i = 0; i < durOnnx.length; i++) {
durOnnx[i] /= speed;
}
const textEncResult = await this.textEncOrt.run({
text_ids: intArrayToTensor(textIds, textIdsShape),
style_ttl: style.ttl,
@@ -185,7 +190,7 @@ class TextToSpeech {
return { wav, duration: durOnnx };
}
async call(text, style, totalStep, silenceDuration = 0.3) {
async call(text, style, totalStep, speed = 1.05, silenceDuration = 0.3) {
if (style.ttl.dims[0] !== 1) {
throw new Error('Single speaker text to speech only supports single style');
}
@@ -194,7 +199,7 @@ class TextToSpeech {
let durCat = 0;
for (const chunk of textList) {
const { wav, duration } = await this._infer([chunk], style, totalStep);
const { wav, duration } = await this._infer([chunk], style, totalStep, speed);
if (wavCat === null) {
wavCat = wav;
@@ -210,8 +215,8 @@ class TextToSpeech {
return { wav: wavCat, duration: [durCat] };
}
async batch(textList, style, totalStep) {
return await this._infer(textList, style, totalStep);
async batch(textList, style, totalStep, speed = 1.05) {
return await this._infer(textList, style, totalStep, speed);
}
}