mia-narrative 1.0.1 → 1.0.3
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/dist/src/audio/processor.js +2 -2
- package/dist/src/audio/processor.js.map +1 -1
- package/dist/src/config/voices.js +4 -4
- package/dist/src/config/voices.js.map +1 -1
- package/dist/src/engines/piper.d.ts.map +1 -1
- package/dist/src/engines/piper.js +18 -4
- package/dist/src/engines/piper.js.map +1 -1
- package/mk-test-samples.sh +1 -0
- package/output.mp3 +0 -0
- package/package.json +1 -1
- package/samples/miette/miette.EXAVITQu4vr4xnSDxMaL.mp3 +0 -0
- package/samples/miette/miette.FGY2WhTYpPnrIDTdsKH5.mp3 +0 -0
- package/samples/miette/miette.SAz9YHcvj6GT2YYXdXww.mp3 +0 -0
- package/samples/miette/miette.XrExE9yKIg1WjnnlVkGX.mp3 +0 -0
- package/samples/miette/miette.cgSgspJ2msm6clMCkdW9.mp3 +0 -0
- package/samples/miette/miette.pFZP5JQG7iQjIQuC4Bku.mp3 +0 -0
- package/samples/miette/play.sh +6 -0
- package/scripts/setup.js +62 -36
- package/src/audio/processor.ts +2 -2
- package/src/config/voices.ts +4 -4
- package/src/engines/piper.ts +23 -4
|
@@ -10,8 +10,8 @@ export class AudioProcessor {
|
|
|
10
10
|
filters.push(`volume=${Math.max(0.0, Math.min(1.0, params.volume))}`);
|
|
11
11
|
}
|
|
12
12
|
if (params.pitch !== 1.0) {
|
|
13
|
-
const
|
|
14
|
-
filters.push(`
|
|
13
|
+
const pitchShift = Math.max(0.5, Math.min(2.0, params.pitch));
|
|
14
|
+
filters.push(`rubberband=pitch=${pitchShift}`);
|
|
15
15
|
}
|
|
16
16
|
if (params.reverb > 0.0) {
|
|
17
17
|
const reverb = Math.min(1.0, params.reverb);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"processor.js","sourceRoot":"","sources":["../../../src/audio/processor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAEtC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,OAAO,cAAc;IACzB,MAAM,CAAC,iBAAiB,CAAC,MAAuB;QAC9C,MAAM,OAAO,GAAa,EAAE,CAAC;QAE7B,IAAI,MAAM,CAAC,KAAK,KAAK,GAAG,EAAE,CAAC;YACzB,OAAO,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QACvE,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC1B,OAAO,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC;QACxE,CAAC;QAED,IAAI,MAAM,CAAC,KAAK,KAAK,GAAG,EAAE,CAAC;YACzB,MAAM,
|
|
1
|
+
{"version":3,"file":"processor.js","sourceRoot":"","sources":["../../../src/audio/processor.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ export const VOICE_PROFILES = {
|
|
|
5
5
|
gender: "female",
|
|
6
6
|
style: "professional",
|
|
7
7
|
description: "Professional narrator for expert technical content",
|
|
8
|
-
piperModel:
|
|
8
|
+
piperModel: "en_US-hfc_female-medium",
|
|
9
9
|
},
|
|
10
10
|
"miette-default": {
|
|
11
11
|
id: "miette-default",
|
|
@@ -13,7 +13,7 @@ export const VOICE_PROFILES = {
|
|
|
13
13
|
gender: "female",
|
|
14
14
|
style: "conversational",
|
|
15
15
|
description: "Friendly conversational narrator",
|
|
16
|
-
piperModel: "
|
|
16
|
+
piperModel: "en_GB-alba-medium",
|
|
17
17
|
},
|
|
18
18
|
"seraphine-default": {
|
|
19
19
|
id: "seraphine-default",
|
|
@@ -34,10 +34,10 @@ export const VOICE_PROFILES = {
|
|
|
34
34
|
"resonova-default": {
|
|
35
35
|
id: "resonova-default",
|
|
36
36
|
name: "ResoNova",
|
|
37
|
-
gender: "
|
|
37
|
+
gender: "female",
|
|
38
38
|
style: "expressive",
|
|
39
39
|
description: "Varied and experimental voice",
|
|
40
|
-
piperModel: "en_US-
|
|
40
|
+
piperModel: "en_US-kristin-medium",
|
|
41
41
|
},
|
|
42
42
|
"zephyr-default": {
|
|
43
43
|
id: "zephyr-default",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voices.js","sourceRoot":"","sources":["../../../src/config/voices.ts"],"names":[],"mappings":"AASA,MAAM,CAAC,MAAM,cAAc,GAAiC;IAC1D,aAAa,EAAE;QACb,EAAE,EAAE,aAAa;QACjB,IAAI,EAAE,KAAK;QACX,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,oDAAoD;QACjE,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"voices.js","sourceRoot":"","sources":["../../../src/config/voices.ts"],"names":[],"mappings":"AASA,MAAM,CAAC,MAAM,cAAc,GAAiC;IAC1D,aAAa,EAAE;QACb,EAAE,EAAE,aAAa;QACjB,IAAI,EAAE,KAAK;QACX,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,oDAAoD;QACjE,UAAU,EAAE,yBAAyB;KACtC;IACD,gBAAgB,EAAE;QAChB,EAAE,EAAE,gBAAgB;QACpB,IAAI,EAAE,QAAQ;QACd,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,gBAAgB;QACvB,WAAW,EAAE,kCAAkC;QAC/C,UAAU,EAAE,mBAAmB;KAChC;IACD,mBAAmB,EAAE;QACnB,EAAE,EAAE,mBAAmB;QACvB,IAAI,EAAE,WAAW;QACjB,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,WAAW;QAClB,WAAW,EAAE,uCAAuC;QACpD,UAAU,EAAE,qBAAqB;KAClC;IACD,gBAAgB,EAAE;QAChB,EAAE,EAAE,gBAAgB;QACpB,IAAI,EAAE,QAAQ;QACd,MAAM,EAAE,MAAM;QACd,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,4BAA4B;QACzC,UAAU,EAAE,mBAAmB;KAChC;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,kBAAkB;QACtB,IAAI,EAAE,UAAU;QAChB,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,YAAY;QACnB,WAAW,EAAE,+BAA+B;QAC5C,UAAU,EAAE,sBAAsB;KACnC;IACD,gBAAgB,EAAE;QAChB,EAAE,EAAE,gBAAgB;QACpB,IAAI,EAAE,QAAQ;QACd,MAAM,EAAE,SAAS;QACjB,KAAK,EAAE,WAAW;QAClB,WAAW,EAAE,0CAA0C;QACvD,UAAU,EAAE,oBAAoB;KACjC;IACD,cAAc,EAAE;QACd,EAAE,EAAE,cAAc;QAClB,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,YAAY;QACnB,WAAW,EAAE,yCAAyC;QACtD,UAAU,EAAE,qBAAqB;KAClC;IACD,eAAe,EAAE;QACf,EAAE,EAAE,eAAe;QACnB,IAAI,EAAE,OAAO;QACb,MAAM,EAAE,MAAM;QACd,KAAK,EAAE,gBAAgB;QACvB,WAAW,EAAE,oCAAoC;QACjD,UAAU,EAAE,mBAAmB;KAChC;CACF,CAAC;AAEF,MAAM,UAAU,eAAe,CAAC,EAAU;IACxC,OAAO,cAAc,CAAC,EAAE,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,UAAU,CACxB,MAA4C;IAE5C,OAAO,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QACpD,IAAI,MAAM,EAAE,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QACnE,IAAI,MAAM,EAAE,KAAK,IAAI,KAAK,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC;QAChE,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"piper.d.ts","sourceRoot":"","sources":["../../../src/engines/piper.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAGpF,qBAAa,WAAY,SAAQ,SAAS;IACxC,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,SAAS,CAAc;IAEzB,UAAU,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAclD,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"piper.d.ts","sourceRoot":"","sources":["../../../src/engines/piper.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAGpF,qBAAa,WAAY,SAAQ,SAAS;IACxC,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,SAAS,CAAc;IAEzB,UAAU,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAclD,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;IAkD7D,SAAS,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;IAW7B,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAgBrC,OAAO,IAAI,MAAM;IAIjB,OAAO,CAAC,QAAQ;CA4CjB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn, execSync } from 'child_process';
|
|
2
|
-
import { writeFileSync, unlinkSync, readFileSync } from 'fs';
|
|
3
|
-
import { tmpdir } from 'os';
|
|
2
|
+
import { writeFileSync, unlinkSync, readFileSync, existsSync } from 'fs';
|
|
3
|
+
import { tmpdir, homedir } from 'os';
|
|
4
4
|
import { join } from 'path';
|
|
5
5
|
import { randomUUID } from 'crypto';
|
|
6
6
|
import { Logger } from '../utils/logger.js';
|
|
@@ -33,13 +33,21 @@ export class PiperEngine extends TTSEngine {
|
|
|
33
33
|
if (!text || text.trim().length === 0) {
|
|
34
34
|
throw new Error('Text cannot be empty');
|
|
35
35
|
}
|
|
36
|
+
// Build path to model file
|
|
37
|
+
const modelDir = join(homedir(), '.local/share/piper-tts');
|
|
38
|
+
const modelFile = join(modelDir, `${voiceProfile.piperModel}.onnx`);
|
|
39
|
+
if (!existsSync(modelFile)) {
|
|
40
|
+
throw new Error(`Piper model not found: ${voiceProfile.piperModel}\n` +
|
|
41
|
+
`Expected location: ${modelFile}\n` +
|
|
42
|
+
`Run 'npm run setup' to download models.`);
|
|
43
|
+
}
|
|
36
44
|
const tempDir = tmpdir();
|
|
37
45
|
const uid = randomUUID();
|
|
38
46
|
const textFile = join(tempDir, `piper-input-${uid}.txt`);
|
|
39
47
|
const audioFile = join(tempDir, `piper-output-${uid}.wav`);
|
|
40
48
|
try {
|
|
41
49
|
writeFileSync(textFile, text, 'utf-8');
|
|
42
|
-
await this.runPiper(textFile, audioFile,
|
|
50
|
+
await this.runPiper(textFile, audioFile, modelFile);
|
|
43
51
|
const audioBuffer = readFileSync(audioFile);
|
|
44
52
|
return audioBuffer;
|
|
45
53
|
}
|
|
@@ -61,7 +69,13 @@ export class PiperEngine extends TTSEngine {
|
|
|
61
69
|
}
|
|
62
70
|
}
|
|
63
71
|
async getVoices() {
|
|
64
|
-
|
|
72
|
+
const modelDir = join(homedir(), '.local/share/piper-tts');
|
|
73
|
+
return Object.values(VOICE_PROFILES)
|
|
74
|
+
.filter(v => {
|
|
75
|
+
const modelFile = join(modelDir, `${v.piperModel}.onnx`);
|
|
76
|
+
return existsSync(modelFile);
|
|
77
|
+
})
|
|
78
|
+
.map(v => ({ id: v.id, name: `${v.name} (${v.style})` }));
|
|
65
79
|
}
|
|
66
80
|
async isAvailable() {
|
|
67
81
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"piper.js","sourceRoot":"","sources":["../../../src/engines/piper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"piper.js","sourceRoot":"","sources":["../../../src/engines/piper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
for v in EXAVITQu4vr4xnSDxMaL FGY2WhTYpPnrIDTdsKH5 SAz9YHcvj6GT2YYXdXww pFZP5JQG7iQjIQuC4Bku XrExE9yKIg1WjnnlVkGX cgSgspJ2msm6clMCkdW9;do o=/tmp/miette.$v.mp3;mia-narrative generate --engine elevenlabs --voiceId $v --text "hello, this is Miette" --output $o &>/dev/null && mia-narrative voices --engine elevenlabs|grep $v && mpg123 $o &>/dev/null;done
|
package/output.mp3
ADDED
|
Binary file
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/scripts/setup.js
CHANGED
|
@@ -7,8 +7,16 @@ import { join } from 'path';
|
|
|
7
7
|
|
|
8
8
|
const HOME = homedir();
|
|
9
9
|
const PIPER_MODEL_DIR = join(HOME, '.local/share/piper-tts');
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
|
|
11
|
+
// Voice models to download (maps to voice profiles)
|
|
12
|
+
const MODELS = [
|
|
13
|
+
{ name: 'en_US-hfc_female-medium', voice: 'Mia', size: '54MB' },
|
|
14
|
+
{ name: 'en_GB-alba-medium', voice: 'Miette', size: '53MB' },
|
|
15
|
+
{ name: 'en_US-libritts-high', voice: 'Seraphine/Echo', size: '89MB' },
|
|
16
|
+
{ name: 'en_US-ryan-medium', voice: 'Jeremy/Atlas', size: '61MB' },
|
|
17
|
+
{ name: 'en_US-kristin-medium', voice: 'ResoNova', size: '60MB' },
|
|
18
|
+
{ name: 'en_US-kusal-medium', voice: 'Zephyr', size: '57MB' },
|
|
19
|
+
];
|
|
12
20
|
|
|
13
21
|
console.log('\n🎙️ mia-narrative Setup\n');
|
|
14
22
|
|
|
@@ -35,51 +43,69 @@ try {
|
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
// Check/Setup Piper models
|
|
38
|
-
console.log(`3️⃣
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
console.log(`3️⃣ Setting up Piper voice models...`);
|
|
47
|
+
mkdirSync(PIPER_MODEL_DIR, { recursive: true });
|
|
48
|
+
|
|
49
|
+
const totalSize = MODELS.reduce((sum, m) => {
|
|
50
|
+
const match = m.size.match(/(\d+)/);
|
|
51
|
+
return sum + (match ? parseInt(match[1]) : 0);
|
|
52
|
+
}, 0);
|
|
53
|
+
|
|
54
|
+
console.log(` 📥 Total download size: ~${totalSize}MB (may take 5-10 minutes)`);
|
|
55
|
+
console.log('');
|
|
56
|
+
|
|
57
|
+
let downloadedCount = 0;
|
|
58
|
+
for (const model of MODELS) {
|
|
59
|
+
const modelPath = join(PIPER_MODEL_DIR, `${model.name}.onnx`);
|
|
60
|
+
const configPath = `${modelPath}.json`;
|
|
61
|
+
|
|
62
|
+
if (existsSync(modelPath) && existsSync(configPath)) {
|
|
63
|
+
console.log(` ✓ ${model.name}`);
|
|
64
|
+
downloadedCount++;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
46
67
|
|
|
47
68
|
try {
|
|
48
|
-
|
|
69
|
+
console.log(` ⏳ ${model.name} (${model.size})...`);
|
|
70
|
+
|
|
71
|
+
// Determine model path in HuggingFace repo
|
|
72
|
+
// Model names like: en_US-hfc_female-medium, en_GB-alba-medium
|
|
73
|
+
let repoPath;
|
|
74
|
+
const parts = model.name.split('_');
|
|
75
|
+
const lang = parts[0]; // "en"
|
|
76
|
+
const country = parts[1].split('-')[0]; // "US" or "GB"
|
|
77
|
+
const voiceAndQuality = model.name.replace(`${lang}_${country}-`, '');
|
|
78
|
+
const qualityIndex = voiceAndQuality.lastIndexOf('-');
|
|
79
|
+
const voice = voiceAndQuality.substring(0, qualityIndex);
|
|
80
|
+
const quality = voiceAndQuality.substring(qualityIndex + 1);
|
|
81
|
+
|
|
82
|
+
repoPath = `${lang}/${lang}_${country}/${voice}/${quality}`;
|
|
83
|
+
|
|
84
|
+
const modelUrl = `https://huggingface.co/rhasspy/piper-voices/resolve/main/${repoPath}/${model.name}.onnx`;
|
|
49
85
|
const configUrl = `${modelUrl}.json`;
|
|
50
86
|
|
|
51
|
-
execSync(`curl -L "${modelUrl}" -o "${
|
|
52
|
-
execSync(`curl -L "${configUrl}" -o "${
|
|
87
|
+
execSync(`curl -L "${modelUrl}" -o "${modelPath}"`, { stdio: 'pipe' });
|
|
88
|
+
execSync(`curl -L "${configUrl}" -o "${configPath}"`, { stdio: 'pipe' });
|
|
53
89
|
|
|
54
|
-
console.log(
|
|
90
|
+
console.log(` ✓ ${model.name}`);
|
|
91
|
+
downloadedCount++;
|
|
55
92
|
} catch (error) {
|
|
56
|
-
console.log(
|
|
57
|
-
console.log(' → You can manually download from:');
|
|
58
|
-
console.log(` https://huggingface.co/rhasspy/piper-voices`);
|
|
59
|
-
process.exit(1);
|
|
93
|
+
console.log(` ✗ Failed to download ${model.name}`);
|
|
60
94
|
}
|
|
61
95
|
}
|
|
62
96
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (process.env.SHELL?.includes('zsh')) {
|
|
68
|
-
const rcFile = join(HOME, '.zshrc');
|
|
69
|
-
console.log(` Add this to ${rcFile}:`);
|
|
70
|
-
console.log(` export ${envVar}`);
|
|
71
|
-
} else if (process.env.SHELL?.includes('bash')) {
|
|
72
|
-
const rcFile = join(HOME, '.bashrc');
|
|
73
|
-
console.log(` Add this to ${rcFile}:`);
|
|
74
|
-
console.log(` export ${envVar}`);
|
|
75
|
-
} else {
|
|
76
|
-
console.log(` Set this in your shell profile:`);
|
|
77
|
-
console.log(` export ${envVar}`);
|
|
97
|
+
console.log('');
|
|
98
|
+
if (downloadedCount === 0) {
|
|
99
|
+
console.log(' ⚠️ No models downloaded. Check your internet connection.');
|
|
100
|
+
process.exit(1);
|
|
78
101
|
}
|
|
79
102
|
|
|
103
|
+
console.log(` ✓ Downloaded ${downloadedCount}/${MODELS.length} models`);
|
|
104
|
+
|
|
80
105
|
console.log('\n✅ Setup complete!');
|
|
106
|
+
console.log('\nModels installed to:', PIPER_MODEL_DIR);
|
|
81
107
|
console.log('\nNext steps:');
|
|
82
|
-
console.log('1.
|
|
83
|
-
console.log('2.
|
|
84
|
-
console.log('3.
|
|
108
|
+
console.log('1. Test: mia-narrative generate --engine piper --text "hello"');
|
|
109
|
+
console.log('2. List voices: mia-narrative voices --engine piper');
|
|
110
|
+
console.log('3. Try a specific voice: mia-narrative generate --engine piper --voice miette-default --text "hello"');
|
|
85
111
|
console.log('\n');
|
package/src/audio/processor.ts
CHANGED
|
@@ -15,8 +15,8 @@ export class AudioProcessor {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
if (params.pitch !== 1.0) {
|
|
18
|
-
const
|
|
19
|
-
filters.push(`
|
|
18
|
+
const pitchShift = Math.max(0.5, Math.min(2.0, params.pitch));
|
|
19
|
+
filters.push(`rubberband=pitch=${pitchShift}`);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
if (params.reverb > 0.0) {
|
package/src/config/voices.ts
CHANGED
|
@@ -14,7 +14,7 @@ export const VOICE_PROFILES: Record<string, VoiceProfile> = {
|
|
|
14
14
|
gender: "female",
|
|
15
15
|
style: "professional",
|
|
16
16
|
description: "Professional narrator for expert technical content",
|
|
17
|
-
piperModel:
|
|
17
|
+
piperModel: "en_US-hfc_female-medium",
|
|
18
18
|
},
|
|
19
19
|
"miette-default": {
|
|
20
20
|
id: "miette-default",
|
|
@@ -22,7 +22,7 @@ export const VOICE_PROFILES: Record<string, VoiceProfile> = {
|
|
|
22
22
|
gender: "female",
|
|
23
23
|
style: "conversational",
|
|
24
24
|
description: "Friendly conversational narrator",
|
|
25
|
-
piperModel: "
|
|
25
|
+
piperModel: "en_GB-alba-medium",
|
|
26
26
|
},
|
|
27
27
|
"seraphine-default": {
|
|
28
28
|
id: "seraphine-default",
|
|
@@ -43,10 +43,10 @@ export const VOICE_PROFILES: Record<string, VoiceProfile> = {
|
|
|
43
43
|
"resonova-default": {
|
|
44
44
|
id: "resonova-default",
|
|
45
45
|
name: "ResoNova",
|
|
46
|
-
gender: "
|
|
46
|
+
gender: "female",
|
|
47
47
|
style: "expressive",
|
|
48
48
|
description: "Varied and experimental voice",
|
|
49
|
-
piperModel: "en_US-
|
|
49
|
+
piperModel: "en_US-kristin-medium",
|
|
50
50
|
},
|
|
51
51
|
"zephyr-default": {
|
|
52
52
|
id: "zephyr-default",
|
package/src/engines/piper.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn, execSync } from 'child_process';
|
|
2
|
-
import { writeFileSync, unlinkSync, readFileSync } from 'fs';
|
|
3
|
-
import { tmpdir } from 'os';
|
|
2
|
+
import { writeFileSync, unlinkSync, readFileSync, existsSync } from 'fs';
|
|
3
|
+
import { tmpdir, homedir } from 'os';
|
|
4
4
|
import { join } from 'path';
|
|
5
5
|
import { randomUUID } from 'crypto';
|
|
6
6
|
import { Logger } from '../utils/logger.js';
|
|
@@ -37,6 +37,18 @@ export class PiperEngine extends TTSEngine {
|
|
|
37
37
|
throw new Error('Text cannot be empty');
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
// Build path to model file
|
|
41
|
+
const modelDir = join(homedir(), '.local/share/piper-tts');
|
|
42
|
+
const modelFile = join(modelDir, `${voiceProfile.piperModel}.onnx`);
|
|
43
|
+
|
|
44
|
+
if (!existsSync(modelFile)) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`Piper model not found: ${voiceProfile.piperModel}\n` +
|
|
47
|
+
`Expected location: ${modelFile}\n` +
|
|
48
|
+
`Run 'npm run setup' to download models.`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
40
52
|
const tempDir = tmpdir();
|
|
41
53
|
const uid = randomUUID();
|
|
42
54
|
const textFile = join(tempDir, `piper-input-${uid}.txt`);
|
|
@@ -44,7 +56,7 @@ export class PiperEngine extends TTSEngine {
|
|
|
44
56
|
|
|
45
57
|
try {
|
|
46
58
|
writeFileSync(textFile, text, 'utf-8');
|
|
47
|
-
await this.runPiper(textFile, audioFile,
|
|
59
|
+
await this.runPiper(textFile, audioFile, modelFile);
|
|
48
60
|
const audioBuffer = readFileSync(audioFile);
|
|
49
61
|
return audioBuffer;
|
|
50
62
|
} finally {
|
|
@@ -64,7 +76,14 @@ export class PiperEngine extends TTSEngine {
|
|
|
64
76
|
}
|
|
65
77
|
|
|
66
78
|
async getVoices(): Promise<Voice[]> {
|
|
67
|
-
|
|
79
|
+
const modelDir = join(homedir(), '.local/share/piper-tts');
|
|
80
|
+
|
|
81
|
+
return Object.values(VOICE_PROFILES)
|
|
82
|
+
.filter(v => {
|
|
83
|
+
const modelFile = join(modelDir, `${v.piperModel}.onnx`);
|
|
84
|
+
return existsSync(modelFile);
|
|
85
|
+
})
|
|
86
|
+
.map(v => ({ id: v.id, name: `${v.name} (${v.style})` }));
|
|
68
87
|
}
|
|
69
88
|
|
|
70
89
|
async isAvailable(): Promise<boolean> {
|