agentvibes 5.9.0 โ 5.11.0
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/.agentvibes/config.json +17 -12
- package/.agentvibes/install-manifest.json +98 -86
- package/.claude/audio/ui/CREDITS.txt +16 -0
- package/.claude/audio/ui/bling-success.wav +0 -0
- package/.claude/commands/agent-vibes/receiver.md +64 -0
- package/.claude/config/audio-effects.cfg +3 -3
- package/.claude/config/personality.txt +1 -0
- package/.claude/config/reverb-level.txt +1 -1
- package/.claude/github-star-reminder.txt +1 -1
- package/.claude/hooks/audio-processor.sh +57 -18
- package/.claude/hooks/kokoro-installer.sh +117 -0
- package/.claude/hooks/kokoro-server.py +219 -0
- package/.claude/hooks/kokoro-tts.py +141 -0
- package/.claude/hooks/piper-download-voices.sh +39 -16
- package/.claude/hooks/piper-installer.sh +11 -6
- package/.claude/hooks/piper-voice-manager.sh +6 -6
- package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +3 -1
- package/.claude/hooks/play-tts-elevenlabs.sh +360 -0
- package/.claude/hooks/play-tts-kokoro.sh +127 -0
- package/.claude/hooks/play-tts-piper.sh +20 -13
- package/.claude/hooks/play-tts-ssh-remote.sh +9 -2
- package/.claude/hooks/play-tts-termux-ssh.sh +3 -1
- package/.claude/hooks/play-tts.sh +78 -2
- package/.claude/hooks/provider-commands.sh +71 -22
- package/.claude/hooks/provider-manager.sh +15 -7
- package/.claude/hooks/voice-manager.sh +6 -0
- package/.claude/hooks-windows/kokoro-server.py +219 -0
- package/.claude/hooks-windows/kokoro-tts.py +107 -0
- package/.claude/hooks-windows/play-tts-kokoro.ps1 +191 -0
- package/.claude/hooks-windows/play-tts-windows-piper.ps1 +22 -16
- package/.claude/hooks-windows/play-tts.ps1 +29 -1
- package/README.md +12 -86
- package/RELEASE_NOTES.md +60 -0
- package/mcp-server/server.py +17 -7
- package/package.json +4 -3
- package/src/commands/install-mcp.js +730 -476
- package/src/console/app.js +28 -12
- package/src/console/audio-env.js +85 -1
- package/src/console/navigation.js +4 -0
- package/src/console/tabs/agents-tab.js +18 -12
- package/src/console/tabs/music-tab.js +7 -25
- package/src/console/tabs/receiver-tab.js +13 -13
- package/src/console/tabs/settings-tab.js +2 -2
- package/src/console/tabs/setup-tab.js +1708 -124
- package/src/console/tabs/voices-tab.js +76 -92
- package/src/console/widgets/format-utils.js +14 -2
- package/src/console/widgets/help-bar.js +55 -0
- package/src/console/widgets/personality-picker.js +2 -2
- package/src/console/widgets/reverb-picker.js +429 -41
- package/src/console/widgets/track-picker.js +60 -51
- package/src/i18n/de.js +204 -203
- package/src/i18n/en.js +1 -0
- package/src/i18n/es.js +204 -203
- package/src/i18n/fr.js +204 -203
- package/src/i18n/hi.js +204 -203
- package/src/i18n/ja.js +204 -203
- package/src/i18n/ko.js +204 -203
- package/src/i18n/pt.js +204 -203
- package/src/i18n/strings.js +54 -54
- package/src/i18n/zh-CN.js +204 -203
- package/src/installer.js +127 -34
- package/src/services/provider-service.js +178 -143
- package/src/services/provider-voice-catalog.js +126 -0
- package/src/services/tts-engine-service.js +53 -4
- package/src/utils/audio-duration-validator.js +341 -298
- package/src/utils/list-formatter.js +200 -194
- package/src/utils/platform-resolver.js +369 -0
- package/src/utils/preview-list-prompt.js +8 -0
- package/src/utils/provider-validator.js +79 -9
- package/templates/agentvibes-receiver.sh +6 -2
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
AgentVibes โ Kokoro TTS synthesizer
|
|
4
|
+
Synthesizes text to a WAV file using the kokoro-onnx package.
|
|
5
|
+
Prints the output WAV path to stdout.
|
|
6
|
+
|
|
7
|
+
Usage: kokoro-tts.py <text> <voice> <output_path> [speed]
|
|
8
|
+
|
|
9
|
+
Voices (examples):
|
|
10
|
+
af_heart af_nova af_sky af_bella af_sarah af_nicole
|
|
11
|
+
am_adam am_michael
|
|
12
|
+
bf_emma bf_isabella
|
|
13
|
+
bm_george bm_lewis
|
|
14
|
+
|
|
15
|
+
Install: pip install kokoro-onnx soundfile numpy
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import sys
|
|
19
|
+
import os
|
|
20
|
+
|
|
21
|
+
def main():
|
|
22
|
+
if len(sys.argv) < 4:
|
|
23
|
+
print("Usage: kokoro-tts.py <text> <voice> <output_path> [speed]", file=sys.stderr)
|
|
24
|
+
sys.exit(1)
|
|
25
|
+
|
|
26
|
+
text = sys.argv[1]
|
|
27
|
+
voice = sys.argv[2]
|
|
28
|
+
output_path = sys.argv[3]
|
|
29
|
+
try:
|
|
30
|
+
speed = float(sys.argv[4]) if len(sys.argv) > 4 else 1.0
|
|
31
|
+
except ValueError:
|
|
32
|
+
speed = 1.0
|
|
33
|
+
|
|
34
|
+
# Determine language code from voice prefix
|
|
35
|
+
# af/am = American English ('a'), bf/bm = British English ('b')
|
|
36
|
+
# jf/jm = Japanese ('j'), kf/km = Korean ('k'), etc.
|
|
37
|
+
prefix = voice[:2].lower() if len(voice) >= 2 else 'af'
|
|
38
|
+
lang_map = {
|
|
39
|
+
'af': 'a', 'am': 'a', # American English
|
|
40
|
+
'bf': 'b', 'bm': 'b', # British English
|
|
41
|
+
'jf': 'j', 'jm': 'j', # Japanese
|
|
42
|
+
'kf': 'k', 'km': 'k', # Korean
|
|
43
|
+
'zf': 'z', 'zm': 'z', # Mandarin
|
|
44
|
+
'ff': 'f', 'fm': 'f', # French
|
|
45
|
+
'hf': 'h', 'hm': 'h', # Hindi
|
|
46
|
+
'if': 'i', 'im': 'i', # Italian
|
|
47
|
+
'pf': 'p', 'pm': 'p', # Brazilian Portuguese
|
|
48
|
+
'ef': 'e', 'em': 'e', # Spanish (Spain)
|
|
49
|
+
'nf': 'n', 'nm': 'n', # Spanish (Latin America)
|
|
50
|
+
}
|
|
51
|
+
lang_code = lang_map.get(prefix, 'a')
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
from kokoro import KPipeline
|
|
55
|
+
except ImportError:
|
|
56
|
+
print("โ kokoro-onnx not installed. Run: pip install kokoro-onnx soundfile numpy", file=sys.stderr)
|
|
57
|
+
sys.exit(2)
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
import numpy as np
|
|
61
|
+
import soundfile as sf
|
|
62
|
+
except ImportError:
|
|
63
|
+
print("โ soundfile/numpy not installed. Run: pip install soundfile numpy", file=sys.stderr)
|
|
64
|
+
sys.exit(2)
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
pipeline = KPipeline(lang_code=lang_code)
|
|
68
|
+
generator = pipeline(text, voice=voice, speed=speed)
|
|
69
|
+
|
|
70
|
+
all_samples = []
|
|
71
|
+
sample_rate = 24000 # kokoro always outputs 24 kHz
|
|
72
|
+
|
|
73
|
+
for result in generator:
|
|
74
|
+
# kokoro >=0.9 yields a KPipeline.Result whose waveform is the
|
|
75
|
+
# `.audio` attribute (a torch.Tensor); older builds yield a
|
|
76
|
+
# (graphemes, phonemes, audio) tuple. Prefer `.audio`, then fall
|
|
77
|
+
# back to the last tuple element.
|
|
78
|
+
audio = getattr(result, "audio", None)
|
|
79
|
+
if audio is None:
|
|
80
|
+
try:
|
|
81
|
+
audio = result[-1]
|
|
82
|
+
except (TypeError, IndexError, KeyError):
|
|
83
|
+
audio = None
|
|
84
|
+
if audio is None:
|
|
85
|
+
continue
|
|
86
|
+
# Tensor -> numpy, force 1-D so np.concatenate never sees a 0-d array
|
|
87
|
+
if hasattr(audio, "detach"):
|
|
88
|
+
audio = audio.detach().cpu().numpy()
|
|
89
|
+
audio = np.asarray(audio).reshape(-1)
|
|
90
|
+
if audio.size > 0:
|
|
91
|
+
all_samples.append(audio)
|
|
92
|
+
|
|
93
|
+
if not all_samples:
|
|
94
|
+
print("โ Kokoro returned no audio samples", file=sys.stderr)
|
|
95
|
+
sys.exit(3)
|
|
96
|
+
|
|
97
|
+
combined = np.concatenate(all_samples)
|
|
98
|
+
sf.write(output_path, combined, sample_rate)
|
|
99
|
+
print(output_path)
|
|
100
|
+
|
|
101
|
+
except Exception as e:
|
|
102
|
+
print(f"โ Kokoro synthesis failed: {e}", file=sys.stderr)
|
|
103
|
+
sys.exit(3)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
if __name__ == '__main__':
|
|
107
|
+
main()
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#
|
|
2
|
+
# File: .claude/hooks-windows/play-tts-kokoro.ps1
|
|
3
|
+
#
|
|
4
|
+
# AgentVibes - Windows Kokoro TTS Provider
|
|
5
|
+
# High-quality local neural TTS via the `kokoro` Python package (KPipeline).
|
|
6
|
+
# Synthesizes text to a WAV file with kokoro-tts.py and emits the path so the
|
|
7
|
+
# parent play-tts.ps1 router coordinates playback / effects / background music.
|
|
8
|
+
#
|
|
9
|
+
# Contract (mirrors play-tts-piper.ps1):
|
|
10
|
+
# - Receives $Text and optional $VoiceOverride (a Kokoro voice id, e.g. af_heart)
|
|
11
|
+
# - Synthesizes a WAV, optionally applies per-agent effects via audio-processor.ps1
|
|
12
|
+
# - Does NOT play audio; emits the final WAV path via Write-Output for the parent
|
|
13
|
+
#
|
|
14
|
+
# Install deps: pip install kokoro soundfile numpy
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
param(
|
|
18
|
+
[Parameter(Mandatory = $true)]
|
|
19
|
+
[string]$Text,
|
|
20
|
+
|
|
21
|
+
[Parameter(Mandatory = $false)]
|
|
22
|
+
[string]$VoiceOverride
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# --- Configuration paths (mirror play-tts-piper.ps1) -------------------------
|
|
26
|
+
$ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
27
|
+
$ProjectClaudeDir = Join-Path (Split-Path -Parent (Split-Path -Parent $ScriptPath)) ".claude"
|
|
28
|
+
if (Test-Path $ProjectClaudeDir) { $ClaudeDir = $ProjectClaudeDir } else { $ClaudeDir = "$env:USERPROFILE\.claude" }
|
|
29
|
+
|
|
30
|
+
$AudioDir = "$ClaudeDir\audio"
|
|
31
|
+
$ConfigDir = "$ClaudeDir\config"
|
|
32
|
+
if (-not (Test-Path $AudioDir)) { New-Item -ItemType Directory -Path $AudioDir -Force | Out-Null }
|
|
33
|
+
|
|
34
|
+
# --- Locate the kokoro synth helper (lives next to this script) --------------
|
|
35
|
+
$KokoroPy = Join-Path $ScriptPath "kokoro-tts.py"
|
|
36
|
+
if (-not (Test-Path $KokoroPy)) {
|
|
37
|
+
Write-Host "[ERROR] kokoro-tts.py not found at: $KokoroPy" -ForegroundColor Red
|
|
38
|
+
exit 1
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# --- Resolve the Python executable -------------------------------------------
|
|
42
|
+
# SSH-receiver watcher sessions can inherit a minimal PATH; refresh from the
|
|
43
|
+
# registry if the first lookup fails (same pattern as play-tts-piper.ps1).
|
|
44
|
+
function Find-Python {
|
|
45
|
+
foreach ($cand in @("python", "python3", "py")) {
|
|
46
|
+
$c = Get-Command $cand -ErrorAction SilentlyContinue
|
|
47
|
+
if ($c) { return $c.Source }
|
|
48
|
+
}
|
|
49
|
+
return $null
|
|
50
|
+
}
|
|
51
|
+
$PythonExe = Find-Python
|
|
52
|
+
if (-not $PythonExe) {
|
|
53
|
+
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" +
|
|
54
|
+
[System.Environment]::GetEnvironmentVariable("Path", "User")
|
|
55
|
+
$PythonExe = Find-Python
|
|
56
|
+
}
|
|
57
|
+
if (-not $PythonExe) {
|
|
58
|
+
Write-Host "[ERROR] Python not found. Install Python 3, then: pip install kokoro soundfile numpy" -ForegroundColor Red
|
|
59
|
+
exit 1
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# --- Determine the Kokoro voice ----------------------------------------------
|
|
63
|
+
$VoiceName = ""
|
|
64
|
+
if ($VoiceOverride) {
|
|
65
|
+
$VoiceName = $VoiceOverride.Trim()
|
|
66
|
+
} else {
|
|
67
|
+
$VoiceFile = "$ClaudeDir\tts-voice-kokoro.txt"
|
|
68
|
+
if (-not (Test-Path $VoiceFile)) { $VoiceFile = "$ClaudeDir\tts-voice.txt" }
|
|
69
|
+
if (Test-Path $VoiceFile) { $VoiceName = (Get-Content $VoiceFile -Raw).Trim() }
|
|
70
|
+
}
|
|
71
|
+
# Kokoro voice ids look like af_heart, am_michael, bf_emma. Strip any ::display suffix.
|
|
72
|
+
if ($VoiceName -match '::') { $VoiceName = ($VoiceName -split '::')[0] }
|
|
73
|
+
if (-not $VoiceName) { $VoiceName = "af_heart" }
|
|
74
|
+
|
|
75
|
+
# Security: validate voice id (lowercase prefix + underscore + name).
|
|
76
|
+
# Rejects path-traversal / injection before it reaches the command line.
|
|
77
|
+
if ($VoiceName -notmatch '^[a-z]{2}_[a-z0-9_]+$') {
|
|
78
|
+
Write-Host "[WARNING] Invalid Kokoro voice '$VoiceName' - falling back to af_heart" -ForegroundColor Yellow
|
|
79
|
+
$VoiceName = "af_heart"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# --- Optional speed ----------------------------------------------------------
|
|
83
|
+
$Speed = "1.0"
|
|
84
|
+
$SpeedFile = "$ConfigDir\tts-speed.txt"
|
|
85
|
+
if (Test-Path $SpeedFile) {
|
|
86
|
+
$s = (Get-Content $SpeedFile -Raw).Trim()
|
|
87
|
+
if ($s -match '^\d+(\.\d+)?$') { $Speed = $s }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# --- Sanitize text -----------------------------------------------------------
|
|
91
|
+
$Text = $Text -replace '\\', ' '
|
|
92
|
+
$Text = $Text -replace '[{}<>|`~^;]', ''
|
|
93
|
+
$Text = $Text -replace '\s+', ' '
|
|
94
|
+
$Text = $Text.Trim()
|
|
95
|
+
if (-not $Text) { Write-Host "[ERROR] No text to synthesize" -ForegroundColor Red; exit 1 }
|
|
96
|
+
|
|
97
|
+
# --- Output path (random name, no predictable timestamp; issue #130) ----------
|
|
98
|
+
$AudioFile = "$AudioDir\tts-$([System.IO.Path]::GetRandomFileName() -replace '\..*').wav"
|
|
99
|
+
|
|
100
|
+
# --- Persistent daemon configuration -----------------------------------------
|
|
101
|
+
# A resident kokoro-server keeps the model loaded on the GPU so each request is
|
|
102
|
+
# ~2-3s (just synthesis) instead of ~31s (fresh model load + CUDA init per call).
|
|
103
|
+
# Fast path: POST to the daemon. Fallback: auto-start the daemon for next time,
|
|
104
|
+
# and synthesize this one message directly so nothing is dropped while it warms.
|
|
105
|
+
$KokoroPort = 7855
|
|
106
|
+
$KokoroServerPy = Join-Path $ScriptPath "kokoro-server.py"
|
|
107
|
+
|
|
108
|
+
function Test-KokoroServer {
|
|
109
|
+
try {
|
|
110
|
+
$r = Invoke-WebRequest -Uri "http://127.0.0.1:$KokoroPort/health" -TimeoutSec 2 -UseBasicParsing -ErrorAction Stop
|
|
111
|
+
return $r.StatusCode -eq 200
|
|
112
|
+
} catch { return $false }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function Start-KokoroServer {
|
|
116
|
+
if (-not (Test-Path $KokoroServerPy)) { return }
|
|
117
|
+
# Prefer pythonw.exe (no console window); fall back to python.exe hidden.
|
|
118
|
+
$pythonw = $PythonExe -replace 'python\.exe$', 'pythonw.exe'
|
|
119
|
+
$exe = if (Test-Path $pythonw) { $pythonw } else { $PythonExe }
|
|
120
|
+
try {
|
|
121
|
+
Start-Process -FilePath $exe -ArgumentList @($KokoroServerPy, "$KokoroPort") `
|
|
122
|
+
-WindowStyle Hidden -ErrorAction Stop | Out-Null
|
|
123
|
+
} catch {
|
|
124
|
+
Write-Host "[WARNING] Could not auto-start kokoro-server: $_" -ForegroundColor Yellow
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# --- Synthesize --------------------------------------------------------------
|
|
129
|
+
try {
|
|
130
|
+
$usedServer = $false
|
|
131
|
+
if (Test-KokoroServer) {
|
|
132
|
+
Write-Host "[SYNTH] Synthesizing with Kokoro daemon (voice=$VoiceName speed=$Speed)..." -ForegroundColor Cyan
|
|
133
|
+
try {
|
|
134
|
+
$payload = @{ text = $Text; voice = $VoiceName; speed = [double]$Speed; output = $AudioFile } | ConvertTo-Json -Compress
|
|
135
|
+
$resp = Invoke-WebRequest -Uri "http://127.0.0.1:$KokoroPort/synth" -Method POST `
|
|
136
|
+
-Body $payload -ContentType 'application/json' -TimeoutSec 120 -UseBasicParsing -ErrorAction Stop
|
|
137
|
+
if ($resp.StatusCode -eq 200 -and (Test-Path $AudioFile) -and (Get-Item $AudioFile).Length -gt 0) {
|
|
138
|
+
$usedServer = $true
|
|
139
|
+
} else {
|
|
140
|
+
Write-Host "[WARNING] Kokoro daemon returned no audio; falling back to direct synth" -ForegroundColor Yellow
|
|
141
|
+
}
|
|
142
|
+
} catch {
|
|
143
|
+
Write-Host "[WARNING] Kokoro daemon request failed ($_); falling back to direct synth" -ForegroundColor Yellow
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (-not $usedServer) {
|
|
148
|
+
# Daemon not up (or failed): start it for subsequent messages, then
|
|
149
|
+
# synthesize this one directly so the current message is never dropped.
|
|
150
|
+
Start-KokoroServer
|
|
151
|
+
Write-Host "[SYNTH] Synthesizing with Kokoro direct (voice=$VoiceName speed=$Speed)..." -ForegroundColor Cyan
|
|
152
|
+
$synthOut = & $PythonExe $KokoroPy $Text $VoiceName $AudioFile $Speed 2>&1
|
|
153
|
+
if (-not (Test-Path $AudioFile) -or (Get-Item $AudioFile).Length -eq 0) {
|
|
154
|
+
Write-Host "[ERROR] Kokoro synthesis failed" -ForegroundColor Red
|
|
155
|
+
if ($synthOut) { $synthOut | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray } }
|
|
156
|
+
exit 1
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
Write-Host "[OK] Saved to: $AudioFile" -ForegroundColor Green
|
|
161
|
+
Write-Host "[VOICE] Voice used: $VoiceName (Kokoro TTS)" -ForegroundColor Green
|
|
162
|
+
|
|
163
|
+
# Apply per-agent audio effects via audio-processor.ps1, but SKIP when
|
|
164
|
+
# AGENTVIBES_NO_PLAY is set - that means the parent play-tts.ps1 will do its
|
|
165
|
+
# own reverb / background-music post-processing and running effects here too
|
|
166
|
+
# would double-process the audio.
|
|
167
|
+
$ProcessorScript = Join-Path $ScriptPath "audio-processor.ps1"
|
|
168
|
+
$ProcessedFile = $AudioFile
|
|
169
|
+
if (-not $env:AGENTVIBES_NO_PLAY -and (Test-Path $ProcessorScript)) {
|
|
170
|
+
$AgentName = if ($env:AGENTVIBES_AGENT_NAME) { $env:AGENTVIBES_AGENT_NAME } elseif ($env:AGENTVIBES_LLM_KEY) { $env:AGENTVIBES_LLM_KEY } else { "default" }
|
|
171
|
+
$EffectedFile = "$AudioFile.effected.wav"
|
|
172
|
+
try {
|
|
173
|
+
& $ProcessorScript $AudioFile $AgentName $EffectedFile
|
|
174
|
+
if ((Test-Path $EffectedFile) -and (Get-Item $EffectedFile).Length -gt 0) {
|
|
175
|
+
$ProcessedFile = $EffectedFile
|
|
176
|
+
Write-Host "[EFFECTS] Audio effects applied" -ForegroundColor Cyan
|
|
177
|
+
}
|
|
178
|
+
} catch {
|
|
179
|
+
Write-Host "[WARNING] Audio effects processing skipped: $_" -ForegroundColor Yellow
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
# Emit the final path for the parent router to coordinate playback.
|
|
184
|
+
# DO NOT play here - play-tts.ps1 owns all audio playback.
|
|
185
|
+
Write-Host "[OUTPUT] Processed audio: $ProcessedFile" -ForegroundColor Gray
|
|
186
|
+
Write-Output $ProcessedFile
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
Write-Host "[ERROR] Error running Kokoro: $_" -ForegroundColor Red
|
|
190
|
+
exit 1
|
|
191
|
+
}
|
|
@@ -62,15 +62,20 @@ if (-not $VoiceName) {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
# Security: Validate voice name to prevent path traversal
|
|
65
|
-
#
|
|
66
|
-
|
|
65
|
+
# Format: <model> or <model>::<speaker> for multi-speaker Piper models
|
|
66
|
+
# Only allow alphanumeric, underscore, hyphen, period, and the :: separator
|
|
67
|
+
if ($VoiceName -notmatch '^[a-zA-Z0-9_\-\.]+(::[a-zA-Z0-9_\-\.]+)?$') {
|
|
67
68
|
Write-Host "[ERROR] Invalid voice name: $VoiceName" -ForegroundColor Red
|
|
68
69
|
exit 1
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
# Extract model name and optional speaker (multi-speaker format: model::Speaker)
|
|
73
|
+
$VoiceModel = ($VoiceName -split '::')[0]
|
|
74
|
+
$SpeakerName = if ($VoiceName -match '::(.+)$') { $Matches[1] } else { "" }
|
|
75
|
+
|
|
71
76
|
# Resolve voice model path and validate it stays within VoicesDir
|
|
72
|
-
$VoiceModelFile = [System.IO.Path]::GetFullPath("$VoicesDir\$
|
|
73
|
-
$VoiceJsonFile
|
|
77
|
+
$VoiceModelFile = [System.IO.Path]::GetFullPath("$VoicesDir\$VoiceModel.onnx")
|
|
78
|
+
$VoiceJsonFile = [System.IO.Path]::GetFullPath("$VoicesDir\$VoiceModel.onnx.json")
|
|
74
79
|
$ResolvedVoicesDir = [System.IO.Path]::GetFullPath($VoicesDir)
|
|
75
80
|
if (-not $VoiceModelFile.StartsWith($ResolvedVoicesDir)) {
|
|
76
81
|
Write-Host "[ERROR] Voice path outside voices directory" -ForegroundColor Red
|
|
@@ -79,15 +84,15 @@ if (-not $VoiceModelFile.StartsWith($ResolvedVoicesDir)) {
|
|
|
79
84
|
|
|
80
85
|
# Check if voice model exists, download if missing
|
|
81
86
|
if (-not (Test-Path $VoiceModelFile)) {
|
|
82
|
-
Write-Host "[DOWNLOAD] Voice model: $
|
|
87
|
+
Write-Host "[DOWNLOAD] Voice model: $VoiceModel" -ForegroundColor Yellow
|
|
83
88
|
|
|
84
89
|
# Try to download from Hugging Face
|
|
85
90
|
# Voice name format: {lang}_{region}-{speaker}-{quality}
|
|
86
91
|
# HF path format: {lang}/{lang}_{region}/{speaker}/{quality}/{voicename}.onnx
|
|
87
92
|
try {
|
|
88
|
-
# Parse
|
|
93
|
+
# Parse model name to build correct HF path (strip ::Speaker suffix first)
|
|
89
94
|
# e.g. en_US-ryan-high -> en/en_US/ryan/high/en_US-ryan-high.onnx
|
|
90
|
-
if ($
|
|
95
|
+
if ($VoiceModel -match '^([a-z]{2})_([A-Z]{2})-([a-zA-Z0-9_]+)-([a-z]+)$') {
|
|
91
96
|
$Lang = $Matches[1]
|
|
92
97
|
$LangRegion = "$($Matches[1])_$($Matches[2])"
|
|
93
98
|
$Speaker = $Matches[3]
|
|
@@ -97,8 +102,8 @@ if (-not (Test-Path $VoiceModelFile)) {
|
|
|
97
102
|
# Fallback for non-standard voice names
|
|
98
103
|
$HFBase = "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/ryan/high"
|
|
99
104
|
}
|
|
100
|
-
$ModelUrl = "$HFBase/$
|
|
101
|
-
$JsonUrl = "$HFBase/$
|
|
105
|
+
$ModelUrl = "$HFBase/$VoiceModel.onnx"
|
|
106
|
+
$JsonUrl = "$HFBase/$VoiceModel.onnx.json"
|
|
102
107
|
|
|
103
108
|
Write-Host " Downloading model..." -ForegroundColor Cyan
|
|
104
109
|
Invoke-WebRequest -Uri $ModelUrl -OutFile $VoiceModelFile -ErrorAction Stop
|
|
@@ -127,11 +132,10 @@ $AudioFile = "$AudioDir\tts-$Timestamp.wav"
|
|
|
127
132
|
try {
|
|
128
133
|
Write-Host "[SYNTH] Synthesizing with Piper..." -ForegroundColor Cyan
|
|
129
134
|
|
|
130
|
-
# Run Piper with text input
|
|
131
|
-
$
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
2>$null
|
|
135
|
+
# Run Piper with text input; pass --speaker for multi-speaker models (model::Speaker format)
|
|
136
|
+
$piperArgs = @('--model', $VoiceModelFile, '--output-file', $AudioFile)
|
|
137
|
+
if ($SpeakerName) { $piperArgs += @('--speaker', $SpeakerName) }
|
|
138
|
+
$Text | & $PiperExe @piperArgs 2>$null
|
|
135
139
|
|
|
136
140
|
if (-not (Test-Path $AudioFile)) {
|
|
137
141
|
Write-Host "[ERROR] Piper synthesis failed" -ForegroundColor Red
|
|
@@ -151,12 +155,14 @@ try {
|
|
|
151
155
|
if (-not $env:AGENTVIBES_NO_PLAY) {
|
|
152
156
|
# Prefer ffplay: handles 22050 Hz โ 48000 Hz resampling cleanly (SoundPlayer uses
|
|
153
157
|
# WinMM's low-quality resampler which produces choppy audio at non-native rates).
|
|
154
|
-
$
|
|
158
|
+
$ffplayCmd = Get-Command ffplay -ErrorAction SilentlyContinue
|
|
159
|
+
$ffplayPath = if ($ffplayCmd) { $ffplayCmd.Source }
|
|
155
160
|
if (-not $ffplayPath) {
|
|
156
161
|
# SSH/watcher sessions may have a minimal PATH โ refresh from registry
|
|
157
162
|
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" +
|
|
158
163
|
[System.Environment]::GetEnvironmentVariable("Path","User")
|
|
159
|
-
$
|
|
164
|
+
$ffplayCmd = Get-Command ffplay -ErrorAction SilentlyContinue
|
|
165
|
+
$ffplayPath = if ($ffplayCmd) { $ffplayCmd.Source }
|
|
160
166
|
}
|
|
161
167
|
if ($ffplayPath) {
|
|
162
168
|
& $ffplayPath -autoexit -nodisp -loglevel quiet $AudioFile 2>$null
|
|
@@ -102,6 +102,9 @@ switch ($ActiveProvider) {
|
|
|
102
102
|
"soprano" {
|
|
103
103
|
$ProviderScript = "$HooksDir\play-tts-soprano.ps1"
|
|
104
104
|
}
|
|
105
|
+
"kokoro" {
|
|
106
|
+
$ProviderScript = "$HooksDir\play-tts-kokoro.ps1"
|
|
107
|
+
}
|
|
105
108
|
default {
|
|
106
109
|
Write-Host "[ERROR] Unknown provider: $ActiveProvider" -ForegroundColor Red
|
|
107
110
|
Write-Host "Use: .\provider-manager.ps1 list" -ForegroundColor Yellow
|
|
@@ -126,6 +129,7 @@ if ($ProviderOverride) {
|
|
|
126
129
|
if (-not (Test-Path $ProviderScript)) { $ProviderScript = "$HooksDir\play-tts-windows-sapi.ps1" }
|
|
127
130
|
}
|
|
128
131
|
"soprano" { $ProviderScript = "$HooksDir\play-tts-soprano.ps1" }
|
|
132
|
+
"kokoro" { $ProviderScript = "$HooksDir\play-tts-kokoro.ps1" }
|
|
129
133
|
default {
|
|
130
134
|
Write-Host "[WARNING] play-tts.ps1: Unknown ProviderOverride '$ProviderOverride' ignored" -ForegroundColor Yellow
|
|
131
135
|
}
|
|
@@ -382,7 +386,25 @@ if ($OverrideEffects -ne "" -and $OverrideEffects -in @("off", "light", "medium"
|
|
|
382
386
|
# Transport providers (ssh-remote etc.) are not listed because they forward
|
|
383
387
|
# TTS to a remote host โ overriding with a local engine would synthesize on
|
|
384
388
|
# the wrong machine.
|
|
385
|
-
|
|
389
|
+
#
|
|
390
|
+
# Voice/engine coupling guard: Kokoro voice ids follow a strict
|
|
391
|
+
# "<2-letter prefix>_<name>" pattern where the 2nd char is the gender (f/m)
|
|
392
|
+
# โ e.g. af_river, am_eric, bf_emma, jf_alpha. They are all-lowercase with no
|
|
393
|
+
# locale, hyphen, digit, or "::" multi-speaker separator that Piper/LibriTTS
|
|
394
|
+
# voices always carry, so the pattern can never match a Piper voice. Such a
|
|
395
|
+
# voice can ONLY be synthesised by the Kokoro engine. When the active voice is
|
|
396
|
+
# a Kokoro voice (e.g. the Kokoro voice picker preview, which sends
|
|
397
|
+
# provider=kokoro + a Kokoro voice), the per-LLM ENGINE column โ which may name
|
|
398
|
+
# piper/sapi for that LLM's normal text responses that use Piper voices โ must
|
|
399
|
+
# NOT redirect it to an incompatible engine, or synthesis fails silently
|
|
400
|
+
# (Piper can't find the Kokoro voice model โ no audio, exit 0).
|
|
401
|
+
$_VoiceIsKokoro = $VoiceOverride -match '^[a-z]{2}_[a-z0-9_]+$'
|
|
402
|
+
if ($_VoiceIsKokoro) {
|
|
403
|
+
# A Kokoro-format voice forces the Kokoro engine regardless of the per-LLM
|
|
404
|
+
# ENGINE column or the global tts-provider.txt default.
|
|
405
|
+
$ProviderScript = "$HooksDir\play-tts-kokoro.ps1"
|
|
406
|
+
}
|
|
407
|
+
elseif ($_LlmEngine) {
|
|
386
408
|
# Accept both canonical Windows names and the cross-platform aliases the TUI
|
|
387
409
|
# writes (e.g. "piper" saved on a Linux/WSL install that is later read on
|
|
388
410
|
# Windows, or "sapi" as a short form). Unknown values keep the global default.
|
|
@@ -398,6 +420,7 @@ if ($_LlmEngine) {
|
|
|
398
420
|
if (-not (Test-Path $ProviderScript)) { $ProviderScript = "$HooksDir\play-tts-windows-piper.ps1" }
|
|
399
421
|
}
|
|
400
422
|
"soprano" { $ProviderScript = "$HooksDir\play-tts-soprano.ps1" }
|
|
423
|
+
"kokoro" { $ProviderScript = "$HooksDir\play-tts-kokoro.ps1" }
|
|
401
424
|
default {
|
|
402
425
|
Write-Host "[INFO] play-tts.ps1: Unrecognised engine '$_LlmEngine' โ keeping default provider" -ForegroundColor DarkGray
|
|
403
426
|
}
|
|
@@ -428,6 +451,11 @@ if ($env:AGENTVIBES_VERBOSE -eq "1") {
|
|
|
428
451
|
# ffplay uses libswresample with sinc resampling โ no artefacts.
|
|
429
452
|
function Invoke-AudioPlay {
|
|
430
453
|
param([string]$FilePath)
|
|
454
|
+
# Stay silent while the automated test suite is running. The windows-tts/effects
|
|
455
|
+
# tests invoke this script and assert on its [VOICE]/routing output (printed
|
|
456
|
+
# earlier) โ they never check playback โ so skipping only the audio output keeps
|
|
457
|
+
# them green while preventing real speech on the developer's machine.
|
|
458
|
+
if (Test-Path (Join-Path $env:USERPROFILE ".agentvibes-tests-running")) { return }
|
|
431
459
|
$ffplayCmd = Get-Command ffplay -ErrorAction SilentlyContinue
|
|
432
460
|
$fp = if ($ffplayCmd) { $ffplayCmd.Source } else { $null }
|
|
433
461
|
if (-not $fp) {
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
[](https://github.com/paulpreibisch/AgentVibes/actions/workflows/publish.yml)
|
|
12
12
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
13
13
|
|
|
14
|
-
**Author**: Paul Preibisch ([@997Fire](https://x.com/997Fire)) | **Version**: v5.
|
|
14
|
+
**Author**: Paul Preibisch ([@997Fire](https://x.com/997Fire)) | **Version**: v5.11.0
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
@@ -347,7 +347,7 @@ All 50+ Piper voices AgentVibes provides are sourced from Hugging Face's open-so
|
|
|
347
347
|
- [๐ค Voices Tab](#-voices-tab) - Browse & sample 914 voices in the TUI
|
|
348
348
|
- [๐ฌ Intro Text](#-intro-text-pretext---your-personal-ai-branding) - Custom TTS prefixes
|
|
349
349
|
- [๐ต Custom Background Music](#-custom-background-music---complete-audio-control) - Upload your own tracks
|
|
350
|
-
- [๐ฐ Latest Release](#-latest-release) - v5.
|
|
350
|
+
- [๐ฐ Latest Release](#-latest-release) - v5.11.0 with Kokoro & ElevenLabs Providers, Audio Effects
|
|
351
351
|
- [๐ช Windows Setup Guide for Claude Desktop](mcp-server/WINDOWS_SETUP.md) - Complete Windows installation with WSL & Python
|
|
352
352
|
|
|
353
353
|
### AgentVibes MCP (Natural Language Control)
|
|
@@ -394,94 +394,20 @@ All 50+ Piper voices AgentVibes provides are sourced from Hugging Face's open-so
|
|
|
394
394
|
|
|
395
395
|
## ๐ฐ Latest Release
|
|
396
396
|
|
|
397
|
-
**[
|
|
397
|
+
**[v5.11.0 โ Kokoro & ElevenLabs Providers + Audio Effects](https://github.com/paulpreibisch/AgentVibes/releases/tag/v5.11.0)** ๐
|
|
398
398
|
|
|
399
|
-
|
|
399
|
+
AgentVibes 5.11.0 adds two new TTS engines, combinable audio effects, a streamlined voices tab, and a deterministically green test suite.
|
|
400
400
|
|
|
401
|
-
|
|
401
|
+
### โจ Highlights
|
|
402
402
|
|
|
403
|
-
|
|
403
|
+
- **๐ Kokoro provider** โ high-quality **local neural** voices (CPU, no GPU needed), including Chinese / Japanese / Korean, with in-TUI dependency install.
|
|
404
|
+
- **๐ ElevenLabs provider** โ premium **cloud** voices via an in-app API-key dialog.
|
|
405
|
+
- **๐๏ธ Combinable audio effects** โ stack **Reverb** (Room / Hall / Cathedral), **Echo** (short / cave), and **Chorus**; preview live with Space.
|
|
406
|
+
- **๐ช Windows SAPI** routing fixed, and a **๐จ unified selector chrome** across every picker.
|
|
407
|
+
- **๐ค Streamlined voices tab** โ keystroke-only (Enter selects, `f` favorites โ
); **BMAD auto-assign** now follows the active provider.
|
|
408
|
+
- **๐งช Rock-solid CI** โ eliminated the flaky failures; the test suite is deterministically green.
|
|
404
409
|
|
|
405
|
-
|
|
406
|
-
- โญ Favorite system - mark your top voices
|
|
407
|
-
- ๐ Search & filter - find voices by personality, accent, gender
|
|
408
|
-
- ๐ฆ One-click select - press Enter to install directly
|
|
409
|
-
- ๐จ Beautiful UI - built into the AgentVibes TUI
|
|
410
|
-
|
|
411
|
-
**914 Total Voices:**
|
|
412
|
-
- 904 Piper speaker variations (libritts-high)
|
|
413
|
-
- 10 curated personality voices
|
|
414
|
-
|
|
415
|
-
### ๐ฏ Major Features
|
|
416
|
-
|
|
417
|
-
**๐ท๏ธ Friendly Voice Names**
|
|
418
|
-
- No more cryptic IDs! Switch voices with names like "Ryan", "Joe", "Sarah"
|
|
419
|
-
- All 904+ voices have memorable, personality-matched names
|
|
420
|
-
- Voice metadata includes personalities, accents, and recommendations
|
|
421
|
-
|
|
422
|
-
```bash
|
|
423
|
-
# Before: /agent-vibes:switch en_US-libritts_r-medium-speaker-123
|
|
424
|
-
# After:
|
|
425
|
-
/agent-vibes:switch Ryan
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
**๐ฌ Intro Text (Pretext) Feature**
|
|
429
|
-
- Custom prefix for all TTS announcements
|
|
430
|
-
- Set during installation or anytime after
|
|
431
|
-
- Perfect for personal branding: "FireBot: Starting analysis..."
|
|
432
|
-
- Up to 50 characters, UTF-8 and emoji support
|
|
433
|
-
|
|
434
|
-
```bash
|
|
435
|
-
npx agentvibes config intro-text
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
**๐ต Custom Background Music**
|
|
439
|
-
- Upload your own audio files (.mp3, .wav, .ogg, .m4a)
|
|
440
|
-
- **Battle-tested security:** 180+ attack variations blocked
|
|
441
|
-
- Magic number validation ensures real audio files
|
|
442
|
-
- File ownership verification (UID checks)
|
|
443
|
-
- Audio duration validation (30-90s recommended, 300s max)
|
|
444
|
-
- Secure storage with 600 permissions
|
|
445
|
-
- Perfect for team audio branding
|
|
446
|
-
|
|
447
|
-
```bash
|
|
448
|
-
npx agentvibes config music
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
**๐จ Interactive Installer**
|
|
452
|
-
- Preview voices during installation
|
|
453
|
-
- Sample all 16 background music tracks
|
|
454
|
-
- Audio environment auto-detection
|
|
455
|
-
- Cross-platform preview support
|
|
456
|
-
|
|
457
|
-
**๐ก๏ธ Security Hardening**
|
|
458
|
-
- **180+ attack variations tested** - Path traversal, symlinks, Unicode, null bytes
|
|
459
|
-
- **100% attack rejection rate** - All malicious attempts blocked
|
|
460
|
-
- **OWASP compliant** - CWE-22 path traversal prevention verified
|
|
461
|
-
- **Production certified** - Comprehensive security audit completed
|
|
462
|
-
- **Defense-in-depth** - 7 validation layers protect your system
|
|
463
|
-
- File ownership verification and secure storage (600 permissions)
|
|
464
|
-
- Security audit report: `docs/security/SECURITY-AUDIT.md`
|
|
465
|
-
|
|
466
|
-
### Quick Install
|
|
467
|
-
|
|
468
|
-
```bash
|
|
469
|
-
# Install AgentVibes
|
|
470
|
-
npx agentvibes install
|
|
471
|
-
|
|
472
|
-
# Browse voices in the TUI
|
|
473
|
-
npx agentvibes # press V for Voices tab
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
**๐ Bug Fixes in v3.6.0:**
|
|
477
|
-
- Fixed `get_verbosity` MCP tool returning wrong level after fresh install (now reads from correct project directory, defaults to `high`)
|
|
478
|
-
- Fixed Voice Browser Soprano TTS detection, Custom Music race conditions, installer emoji rendering
|
|
479
|
-
|
|
480
|
-
๐ก **Tip:** If `npx agentvibes` shows an older version, clear cache: `npm cache clean --force && npx agentvibes@latest --help`
|
|
481
|
-
|
|
482
|
-
๐ **Found a bug?** Report at [GitHub Issues](https://github.com/paulpreibisch/AgentVibes/issues)
|
|
483
|
-
|
|
484
|
-
[โ View Complete Release Notes](RELEASE_NOTES_v3.6.0.md) | [โ View All Releases](https://github.com/paulpreibisch/AgentVibes/releases)
|
|
410
|
+
๐ Full details: [RELEASE_NOTES.md](RELEASE_NOTES.md) | [โ All Releases](https://github.com/paulpreibisch/AgentVibes/releases)
|
|
485
411
|
|
|
486
412
|
[โ Back to top](#-table-of-contents)
|
|
487
413
|
|
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,5 +1,65 @@
|
|
|
1
1
|
# AgentVibes Release Notes
|
|
2
2
|
|
|
3
|
+
## ๐๏ธ v5.11.0 โ Kokoro & ElevenLabs Providers, Unified Pickers, Windows SAPI, Rock-Solid CI
|
|
4
|
+
|
|
5
|
+
**Released:** 2026-06-26
|
|
6
|
+
|
|
7
|
+
This is a big one. AgentVibes gains two new TTS engines โ **Kokoro** (high-quality local neural voices, including Chinese/Japanese/Korean) and **ElevenLabs** (premium cloud voices) โ combinable audio effects, a unified selector UI, a Windows SAPI routing fix, and a deterministically green test suite. It also rolls up everything from the earlier 5.11.0 preview (LibriTTS 900+ voices, setup UX).
|
|
8
|
+
|
|
9
|
+
### ๐ New TTS Provider: Kokoro (local neural voices)
|
|
10
|
+
- Browse and preview Kokoro voices from the Setup tab using the standardized picker.
|
|
11
|
+
- **CPU synthesis** path so it runs without a GPU.
|
|
12
|
+
- Smart dependency detection with in-TUI install dialogs โ installs `kokoro`, `soundfile`, and `numpy` together, with a real progress bar (not just a spinner).
|
|
13
|
+
- **CJK support**: Chinese, Japanese, and Korean voices, with per-language `misaki` / `pyopenjtalk` dependency handling and native sample phrases.
|
|
14
|
+
- Windows playback fixed (Forms assembly load, correct paths, clearer exit-code errors).
|
|
15
|
+
- A readiness "bling" cue (bundled CC0 sound) signals when a voice is ready to preview.
|
|
16
|
+
|
|
17
|
+
### ๐ New TTS Provider: ElevenLabs (premium cloud voices)
|
|
18
|
+
- API-key dialog built into the Setup tab, with a navigation hint and Escape-to-close.
|
|
19
|
+
- Routed through the same per-LLM voice system as every other provider.
|
|
20
|
+
|
|
21
|
+
### ๐๏ธ New: Combinable Audio Effects โ Reverb ยท Echo ยท Chorus
|
|
22
|
+
Give any voice real character. A new multi-select effects modal lets you stack **reverb**, **echo**, and **chorus** independently:
|
|
23
|
+
- **Reverb:** Room, Hall, Cathedral, and Warm (reverb + bass).
|
|
24
|
+
- **Echo:** Echo (short delay) and Cave Echo (long) โ brand new; there was no echo before.
|
|
25
|
+
- **Chorus** for a richer, doubled timbre.
|
|
26
|
+
- Effects **combine** (e.g. a little reverb *plus* a short echo), preview live with Space, and the picker now speaks the effect name and shows a spinner while auditioning.
|
|
27
|
+
|
|
28
|
+
### ๐จ Unified Selector Chrome
|
|
29
|
+
- All selector pickers (voices, tracks, reverb/effects) now share a common help bar, title style, and a fixed-width voice-row formatter โ a consistent look and aligned status columns across every picker.
|
|
30
|
+
|
|
31
|
+
### ๐ช Windows SAPI Routing Fixed
|
|
32
|
+
- The bash hook router now recognizes the `sapi` / `windows-sapi` provider and routes to `play-tts-sapi.ps1` correctly (previously errored with "Unknown provider: sapi").
|
|
33
|
+
|
|
34
|
+
### ๐ค Voices & BMAD Tab Improvements
|
|
35
|
+
- **Streamlined, keystroke-only voice list.** Removed the redundant Switch / Favorite / Download button row โ **Enter** selects (and downloads an uninstalled Piper voice first), **Space** previews, and **`f`** toggles a favorite โ
. Thumbs-up/thumbs-down collapsed into a single favorite star.
|
|
36
|
+
- **Removed the confusing "Install BMAD Voices" button.** Bulk voice downloads are available from the CLI (`npx agentvibes voices download`) instead.
|
|
37
|
+
- **BMAD auto-assign now respects the active provider.** Pressing **[A] Auto-assign** previously always picked Piper voices; it now assigns voices from whichever provider is currently selected (Piper, Kokoro, ElevenLabs, soprano, โฆ).
|
|
38
|
+
|
|
39
|
+
### ๐๏ธ Picker & Preview Polish
|
|
40
|
+
- **Effects preview:** correct voice + effect routing, an inline spinner on the item row, and a `pyopenjtalk` install prompt where needed.
|
|
41
|
+
- **Track picker:** uses an `ffmpeg โ pacat` pipe on headless PulseAudio TCP servers so previews work in remote/headless setups.
|
|
42
|
+
- **Navigation:** pressing a tab's shortcut key while already on that tab is now a no-op.
|
|
43
|
+
|
|
44
|
+
### ๐งช Rock-Solid CI (no more flaky failures)
|
|
45
|
+
- Eliminated the intermittent CI red: fixed a `blessed.log()`-after-teardown crash in the Voices tab that randomly failed an innocent test file, and added a coverage runner (`scripts/run-coverage.mjs`) that gates on the actual reported test results instead of node's flaky `--test-force-exit` exit code. Real failures still fail the build; node's force-exit artifacts are tolerated.
|
|
46
|
+
- Test audio is fully silenced during runs (marker file + env flags) so the suite never bleeds to your speakers.
|
|
47
|
+
|
|
48
|
+
### ๐ Audio Robustness Fixes
|
|
49
|
+
- `getAudioDuration()` now attaches an error listener to its `which ffprobe` probe (previously this could throw an unhandled error and crash on Windows, where `which` doesn't exist) and bounds both spawns with 5s/10s timeouts so a stalled `ffprobe` can never hang the caller.
|
|
50
|
+
- The interactive voice-preview prompt now releases (`pause()`) the stdin handle it opens, so the process exits cleanly instead of lingering.
|
|
51
|
+
|
|
52
|
+
### ๐ฆ Also in 5.11.0 (earlier preview)
|
|
53
|
+
- LibriTTS 900+ voice library offer during install (~114 MB, 904 speakers).
|
|
54
|
+
- Linux Piper install now uses the project's own `piper-installer.sh --non-interactive`.
|
|
55
|
+
- Setup tab: spinner during engine install, smarter Tab navigation to Continue, install timeout raised to 30 minutes.
|
|
56
|
+
|
|
57
|
+
### ๐ก๏ธ Quality
|
|
58
|
+
- Full test suite green. New audio code covered: `audio-duration-validator` 96%, `preview-list-prompt` 90%.
|
|
59
|
+
- Known pre-existing: `piper-voice-manager.sh` / `piper-installer.sh` use partial shell strict mode; tracked for a future hardening pass.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
3
63
|
## ๐ง v5.9.0 โ SSH Remote + Windows Home Directory Fixes
|
|
4
64
|
|
|
5
65
|
**Released:** 2026-05-18
|