agentvibes 2.13.8 → 2.14.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/.claude/commands/agent-vibes/bmad.md +16 -14
- package/.claude/commands/agent-vibes/translate.md +68 -0
- package/.claude/hooks/learn-manager.sh +14 -3
- package/.claude/hooks/play-tts-elevenlabs.sh +3 -1
- package/.claude/hooks/play-tts.sh +131 -8
- package/.claude/hooks/provider-manager.sh +112 -23
- package/.claude/hooks/requirements.txt +6 -0
- package/.claude/hooks/translate-manager.sh +341 -0
- package/.claude/hooks/translator.py +237 -0
- package/README.md +9 -8
- package/RELEASE_NOTES.md +111 -515
- package/docs/language-learning-mode.md +47 -2
- package/mcp-server/install-deps.js +7 -6
- package/mcp-server/server.py +4 -2
- package/package.json +1 -1
- package/src/commands/bmad-voices.js +5 -16
- package/src/commands/install-mcp.js +15 -9
- package/src/installer.js +21 -23
- package/test/unit/provider-manager.bats +13 -9
- package/test/unit/translator.bats +246 -0
|
@@ -161,20 +161,22 @@ The TTS injection works with **any configured TTS provider**:
|
|
|
161
161
|
|
|
162
162
|
The system automatically detects your configured provider via `/agent-vibes:provider info` and uses the appropriate TTS engine. You can switch providers anytime with `/agent-vibes:provider switch` and the BMAD agents will continue speaking using the new provider.
|
|
163
163
|
|
|
164
|
-
## Available BMAD Agents
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
|
169
|
-
|
|
170
|
-
|
|
|
171
|
-
|
|
|
172
|
-
|
|
|
173
|
-
|
|
|
174
|
-
| sm | Scrum Master |
|
|
175
|
-
|
|
|
176
|
-
|
|
|
177
|
-
|
|
|
164
|
+
## Available BMAD Agents (Provider-Aware)
|
|
165
|
+
|
|
166
|
+
The voice used depends on your active TTS provider. Run `/agent-vibes:provider info` to check your provider.
|
|
167
|
+
|
|
168
|
+
| Agent ID | Role | ElevenLabs Voice | Piper Voice |
|
|
169
|
+
|----------|------|------------------|-------------|
|
|
170
|
+
| pm | Product Manager | Matthew Schmitz | en_US-ryan-high |
|
|
171
|
+
| dev | Developer | Aria | en_US-amy-medium |
|
|
172
|
+
| analyst | Business Analyst | Jessica Anne Bogart | en_US-kristin-medium |
|
|
173
|
+
| architect | Architect | Michael | en_GB-alan-medium |
|
|
174
|
+
| sm | Scrum Master | Matthew Schmitz | en_US-joe-medium |
|
|
175
|
+
| tea | Test Architect | Michael | en_US-arctic-medium |
|
|
176
|
+
| tech-writer | Technical Writer | Aria | en_US-lessac-medium |
|
|
177
|
+
| ux-designer | UX Designer | Jessica Anne Bogart | en_US-lessac-medium |
|
|
178
|
+
| frame-expert | Visual Designer | Matthew Schmitz | en_GB-alan-medium |
|
|
179
|
+
| bmad-master | BMAD Master | Michael | en_US-danny-low |
|
|
178
180
|
|
|
179
181
|
## Implementation Details
|
|
180
182
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 'Configure automatic TTS translation to speak in your preferred language'
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# /agent-vibes:translate - Multi-Language TTS Translation
|
|
6
|
+
|
|
7
|
+
Configure AgentVibes to automatically translate English TTS text to your preferred language before speaking.
|
|
8
|
+
|
|
9
|
+
**Usage:**
|
|
10
|
+
- `/agent-vibes:translate` - Show current translation settings
|
|
11
|
+
- `/agent-vibes:translate set <language>` - Set translation language
|
|
12
|
+
- `/agent-vibes:translate auto` - Use BMAD communication_language setting
|
|
13
|
+
- `/agent-vibes:translate off` - Disable translation (speak English)
|
|
14
|
+
- `/agent-vibes:translate status` - Show detailed status
|
|
15
|
+
|
|
16
|
+
**Arguments:** $ARGUMENTS
|
|
17
|
+
|
|
18
|
+
## How It Works
|
|
19
|
+
|
|
20
|
+
When translation is enabled, AgentVibes will:
|
|
21
|
+
1. Take the English TTS text
|
|
22
|
+
2. Translate it to your target language using Google Translate
|
|
23
|
+
3. Speak the translated text using a language-appropriate voice
|
|
24
|
+
|
|
25
|
+
## Priority Order
|
|
26
|
+
|
|
27
|
+
1. **Manual override** (`/agent-vibes:translate set spanish`) - Highest priority
|
|
28
|
+
2. **BMAD config** (`communication_language` in `.bmad/core/config.yaml`) - Auto-detected
|
|
29
|
+
3. **Default** - No translation (English)
|
|
30
|
+
|
|
31
|
+
## Supported Languages
|
|
32
|
+
|
|
33
|
+
Spanish, French, German, Italian, Portuguese, Chinese, Japanese, Korean, Russian, Polish, Dutch, Turkish, Arabic, Hindi, Swedish, Danish, Norwegian, Finnish, Czech, Romanian, Ukrainian, Greek, Bulgarian, Croatian, Slovak
|
|
34
|
+
|
|
35
|
+
## Examples
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Translate all TTS to Spanish
|
|
39
|
+
/agent-vibes:translate set spanish
|
|
40
|
+
|
|
41
|
+
# Use BMAD's communication_language setting
|
|
42
|
+
/agent-vibes:translate auto
|
|
43
|
+
|
|
44
|
+
# Disable translation (speak English)
|
|
45
|
+
/agent-vibes:translate off
|
|
46
|
+
|
|
47
|
+
# Check current settings
|
|
48
|
+
/agent-vibes:translate status
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Integration with BMAD
|
|
52
|
+
|
|
53
|
+
If you have BMAD installed with a `communication_language` setting:
|
|
54
|
+
|
|
55
|
+
```yaml
|
|
56
|
+
# .bmad/core/config.yaml
|
|
57
|
+
communication_language: Spanish
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
AgentVibes will automatically detect this and translate TTS to Spanish when you run `/agent-vibes:translate auto`.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
Execute the translate-manager.sh script:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
.claude/hooks/translate-manager.sh $ARGUMENTS
|
|
68
|
+
```
|
|
@@ -34,10 +34,19 @@
|
|
|
34
34
|
# @patterns Dual-voice orchestration, auto-configuration, greeting on activation, provider-aware voice selection
|
|
35
35
|
# @related language-manager.sh, play-tts.sh, .claude/tts-learn-mode.txt, .claude/tts-target-language.txt
|
|
36
36
|
|
|
37
|
-
set
|
|
37
|
+
# Only set strict mode when executed directly, not when sourced
|
|
38
|
+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
39
|
+
set -e
|
|
40
|
+
fi
|
|
38
41
|
|
|
39
42
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
40
|
-
|
|
43
|
+
|
|
44
|
+
# Use PWD for project dir when called from project context, fall back to script-relative
|
|
45
|
+
if [[ -d "$PWD/.claude" ]]; then
|
|
46
|
+
PROJECT_DIR="$PWD"
|
|
47
|
+
else
|
|
48
|
+
PROJECT_DIR="$SCRIPT_DIR/../.."
|
|
49
|
+
fi
|
|
41
50
|
|
|
42
51
|
# Configuration files (project-local first, then global fallback)
|
|
43
52
|
MAIN_LANG_FILE="$PROJECT_DIR/.claude/tts-main-language.txt"
|
|
@@ -430,7 +439,8 @@ show_status() {
|
|
|
430
439
|
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
431
440
|
}
|
|
432
441
|
|
|
433
|
-
# Main command handler
|
|
442
|
+
# Main command handler - only run if script is executed directly, not sourced
|
|
443
|
+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
434
444
|
case "${1:-}" in
|
|
435
445
|
get-main-language)
|
|
436
446
|
get_main_language
|
|
@@ -473,3 +483,4 @@ case "${1:-}" in
|
|
|
473
483
|
exit 1
|
|
474
484
|
;;
|
|
475
485
|
esac
|
|
486
|
+
fi
|
|
@@ -198,8 +198,10 @@ TEMP_FILE="$AUDIO_DIR/tts-$(date +%s).mp3"
|
|
|
198
198
|
# @sideeffects Creates MP3 file in audio directory
|
|
199
199
|
# @edgecases Handles network failures, API errors, rate limiting
|
|
200
200
|
# Choose model based on language
|
|
201
|
+
# Note: eleven_monolingual_v1 deprecated for free tier as of Nov 2025
|
|
202
|
+
# Using eleven_turbo_v2_5 for English (fast, high quality, free tier compatible)
|
|
201
203
|
if [[ "$LANGUAGE_CODE" == "en" ]]; then
|
|
202
|
-
MODEL_ID="
|
|
204
|
+
MODEL_ID="eleven_turbo_v2_5"
|
|
203
205
|
else
|
|
204
206
|
MODEL_ID="eleven_multilingual_v2"
|
|
205
207
|
fi
|
|
@@ -31,13 +31,13 @@
|
|
|
31
31
|
#
|
|
32
32
|
# ---
|
|
33
33
|
#
|
|
34
|
-
# @fileoverview TTS Provider Router with Language Learning Support
|
|
35
|
-
# @context Routes TTS requests to active provider (ElevenLabs or Piper)
|
|
36
|
-
# @architecture Provider abstraction layer - single entry point for all TTS
|
|
37
|
-
# @dependencies provider-manager.sh, play-tts-elevenlabs.sh, play-tts-piper.sh,
|
|
34
|
+
# @fileoverview TTS Provider Router with Translation and Language Learning Support
|
|
35
|
+
# @context Routes TTS requests to active provider (ElevenLabs or Piper) with optional translation
|
|
36
|
+
# @architecture Provider abstraction layer - single entry point for all TTS, handles translation and learning mode
|
|
37
|
+
# @dependencies provider-manager.sh, play-tts-elevenlabs.sh, play-tts-piper.sh, translator.py, translate-manager.sh, learn-manager.sh
|
|
38
38
|
# @entrypoints Called by hooks, slash commands, personality-manager.sh, and all TTS features
|
|
39
39
|
# @patterns Provider pattern - delegates to provider-specific implementations, auto-detects provider from voice name
|
|
40
|
-
# @related provider-manager.sh, play-tts-elevenlabs.sh, play-tts-piper.sh, learn-manager.sh
|
|
40
|
+
# @related provider-manager.sh, play-tts-elevenlabs.sh, play-tts-piper.sh, learn-manager.sh, translate-manager.sh
|
|
41
41
|
#
|
|
42
42
|
|
|
43
43
|
# Fix locale warnings
|
|
@@ -98,10 +98,133 @@ if [[ -n "$VOICE_OVERRIDE" ]]; then
|
|
|
98
98
|
fi
|
|
99
99
|
fi
|
|
100
100
|
|
|
101
|
+
# @function speak_text
|
|
102
|
+
# @intent Route text to appropriate TTS provider
|
|
103
|
+
# @why Reusable function for speaking, used by both single and learning modes
|
|
104
|
+
# @param $1 text to speak
|
|
105
|
+
# @param $2 voice override (optional)
|
|
106
|
+
# @param $3 provider override (optional)
|
|
107
|
+
speak_text() {
|
|
108
|
+
local text="$1"
|
|
109
|
+
local voice="${2:-}"
|
|
110
|
+
local provider="${3:-$ACTIVE_PROVIDER}"
|
|
111
|
+
|
|
112
|
+
case "$provider" in
|
|
113
|
+
elevenlabs)
|
|
114
|
+
"$SCRIPT_DIR/play-tts-elevenlabs.sh" "$text" "$voice"
|
|
115
|
+
;;
|
|
116
|
+
piper)
|
|
117
|
+
"$SCRIPT_DIR/play-tts-piper.sh" "$text" "$voice"
|
|
118
|
+
;;
|
|
119
|
+
*)
|
|
120
|
+
echo "❌ Unknown provider: $provider" >&2
|
|
121
|
+
return 1
|
|
122
|
+
;;
|
|
123
|
+
esac
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
# Note: learn-manager.sh and translate-manager.sh are sourced inside their
|
|
127
|
+
# respective handler functions to avoid triggering their main handlers
|
|
128
|
+
|
|
129
|
+
# @function handle_learning_mode
|
|
130
|
+
# @intent Speak in both main language and target language for learning
|
|
131
|
+
# @why Issue #51 - Auto-translate and speak twice for immersive language learning
|
|
132
|
+
# @returns 0 if learning mode handled, 1 if not in learning mode
|
|
133
|
+
handle_learning_mode() {
|
|
134
|
+
# Source learn-manager for learning mode functions
|
|
135
|
+
source "$SCRIPT_DIR/learn-manager.sh" 2>/dev/null || return 1
|
|
136
|
+
|
|
137
|
+
# Check if learning mode is enabled
|
|
138
|
+
if ! is_learn_mode_enabled 2>/dev/null; then
|
|
139
|
+
return 1
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
local target_lang
|
|
143
|
+
target_lang=$(get_target_language 2>/dev/null || echo "")
|
|
144
|
+
local target_voice
|
|
145
|
+
target_voice=$(get_target_voice 2>/dev/null || echo "")
|
|
146
|
+
|
|
147
|
+
# Need both target language and voice for learning mode
|
|
148
|
+
if [[ -z "$target_lang" ]] || [[ -z "$target_voice" ]]; then
|
|
149
|
+
return 1
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
# 1. Speak in main language (current voice)
|
|
153
|
+
speak_text "$TEXT" "$VOICE_OVERRIDE" "$ACTIVE_PROVIDER"
|
|
154
|
+
|
|
155
|
+
# 2. Auto-translate to target language
|
|
156
|
+
local translated
|
|
157
|
+
translated=$(python3 "$SCRIPT_DIR/translator.py" "$TEXT" "$target_lang" 2>/dev/null) || translated="$TEXT"
|
|
158
|
+
|
|
159
|
+
# Small pause between languages
|
|
160
|
+
sleep 0.5
|
|
161
|
+
|
|
162
|
+
# 3. Speak translated text with target voice
|
|
163
|
+
local target_provider
|
|
164
|
+
target_provider=$(detect_voice_provider "$target_voice")
|
|
165
|
+
speak_text "$translated" "$target_voice" "$target_provider"
|
|
166
|
+
|
|
167
|
+
return 0
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
# @function handle_translation_mode
|
|
171
|
+
# @intent Translate and speak in target language (non-learning mode)
|
|
172
|
+
# @why Issue #50 - BMAD multi-language TTS support
|
|
173
|
+
# @returns 0 if translation handled, 1 if not translating
|
|
174
|
+
handle_translation_mode() {
|
|
175
|
+
# Source translate-manager to get translation settings
|
|
176
|
+
source "$SCRIPT_DIR/translate-manager.sh" 2>/dev/null || return 1
|
|
177
|
+
|
|
178
|
+
# Check if translation is enabled
|
|
179
|
+
if ! is_translation_enabled 2>/dev/null; then
|
|
180
|
+
return 1
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
local translate_to
|
|
184
|
+
translate_to=$(get_translate_to 2>/dev/null || echo "")
|
|
185
|
+
|
|
186
|
+
if [[ -z "$translate_to" ]] || [[ "$translate_to" == "english" ]]; then
|
|
187
|
+
return 1
|
|
188
|
+
fi
|
|
189
|
+
|
|
190
|
+
# Translate text
|
|
191
|
+
local translated
|
|
192
|
+
translated=$(python3 "$SCRIPT_DIR/translator.py" "$TEXT" "$translate_to" 2>/dev/null) || translated="$TEXT"
|
|
193
|
+
|
|
194
|
+
# Get voice for target language if no override specified
|
|
195
|
+
local voice_to_use="$VOICE_OVERRIDE"
|
|
196
|
+
if [[ -z "$voice_to_use" ]]; then
|
|
197
|
+
source "$SCRIPT_DIR/language-manager.sh" 2>/dev/null || true
|
|
198
|
+
voice_to_use=$(get_voice_for_language "$translate_to" "$ACTIVE_PROVIDER" 2>/dev/null || echo "")
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
# Update provider if voice indicates different provider
|
|
202
|
+
local provider_to_use="$ACTIVE_PROVIDER"
|
|
203
|
+
if [[ -n "$voice_to_use" ]]; then
|
|
204
|
+
provider_to_use=$(detect_voice_provider "$voice_to_use")
|
|
205
|
+
fi
|
|
206
|
+
|
|
207
|
+
# Speak translated text
|
|
208
|
+
speak_text "$translated" "$voice_to_use" "$provider_to_use"
|
|
209
|
+
return 0
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
# Mode priority:
|
|
213
|
+
# 1. Learning mode (speaks twice: main + translated)
|
|
214
|
+
# 2. Translation mode (speaks translated only)
|
|
215
|
+
# 3. Normal mode (speaks as-is)
|
|
216
|
+
|
|
217
|
+
# Try learning mode first (Issue #51)
|
|
218
|
+
if handle_learning_mode; then
|
|
219
|
+
exit 0
|
|
220
|
+
fi
|
|
221
|
+
|
|
222
|
+
# Try translation mode (Issue #50)
|
|
223
|
+
if handle_translation_mode; then
|
|
224
|
+
exit 0
|
|
225
|
+
fi
|
|
226
|
+
|
|
101
227
|
# Normal single-language mode - route to appropriate provider implementation
|
|
102
|
-
# Note: For learning mode, the output style will call this script TWICE:
|
|
103
|
-
# 1. First call with main language text and current voice
|
|
104
|
-
# 2. Second call with translated text and target voice
|
|
105
228
|
case "$ACTIVE_PROVIDER" in
|
|
106
229
|
elevenlabs)
|
|
107
230
|
exec "$SCRIPT_DIR/play-tts-elevenlabs.sh" "$TEXT" "$VOICE_OVERRIDE"
|
|
@@ -141,31 +141,120 @@ set_active_provider() {
|
|
|
141
141
|
voice_file="$HOME/.claude/tts-voice.txt"
|
|
142
142
|
fi
|
|
143
143
|
|
|
144
|
-
#
|
|
145
|
-
local
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
# Unknown provider - remove voice file
|
|
157
|
-
if [[ -f "$voice_file" ]]; then
|
|
158
|
-
rm -f "$voice_file"
|
|
159
|
-
fi
|
|
160
|
-
echo "✓ Active provider set to: $provider (voice reset)"
|
|
161
|
-
return 0
|
|
162
|
-
;;
|
|
163
|
-
esac
|
|
144
|
+
# Migrate voice to equivalent in new provider
|
|
145
|
+
local current_voice=""
|
|
146
|
+
if [[ -f "$voice_file" ]]; then
|
|
147
|
+
# Strip only leading/trailing whitespace and newlines, preserve internal spaces
|
|
148
|
+
current_voice=$(cat "$voice_file" | tr -d '\n\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
local new_voice
|
|
152
|
+
new_voice=$(migrate_voice_to_provider "$current_voice" "$provider")
|
|
153
|
+
|
|
154
|
+
# Write new voice to file
|
|
155
|
+
echo "$new_voice" > "$voice_file"
|
|
164
156
|
|
|
165
|
-
|
|
166
|
-
|
|
157
|
+
if [[ -n "$current_voice" ]] && [[ "$current_voice" != "$new_voice" ]]; then
|
|
158
|
+
echo "✓ Active provider set to: $provider"
|
|
159
|
+
echo "🔄 Voice migrated: $current_voice → $new_voice"
|
|
160
|
+
else
|
|
161
|
+
echo "✓ Active provider set to: $provider (voice: $new_voice)"
|
|
162
|
+
fi
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# @function migrate_voice_to_provider
|
|
166
|
+
# @intent Migrate a voice from one provider to an equivalent in the target provider
|
|
167
|
+
# @why Users shouldn't have to manually reconfigure voices when switching providers
|
|
168
|
+
# @param $1 {string} current_voice - Current voice name (may be from any provider)
|
|
169
|
+
# @param $2 {string} target_provider - Target provider to migrate to
|
|
170
|
+
# @returns Echoes equivalent voice name for target provider
|
|
171
|
+
# @exitcode 0=always succeeds (returns default if no mapping found)
|
|
172
|
+
# @sideeffects None
|
|
173
|
+
# @edgecases Returns provider default if voice not found in mapping table
|
|
174
|
+
migrate_voice_to_provider() {
|
|
175
|
+
local current_voice="$1"
|
|
176
|
+
local target_provider="$2"
|
|
177
|
+
|
|
178
|
+
# Voice mapping table: ElevenLabs <-> Piper equivalents
|
|
179
|
+
# Format: "elevenlabs_voice:piper_voice"
|
|
180
|
+
local voice_mappings=(
|
|
181
|
+
"Amy:en_US-amy-medium"
|
|
182
|
+
"Aria:en_US-amy-medium"
|
|
183
|
+
"Matthew Schmitz:en_US-ryan-high"
|
|
184
|
+
"Michael:en_GB-alan-medium"
|
|
185
|
+
"Jessica Anne Bogart:en_US-kristin-medium"
|
|
186
|
+
"Ms. Walker:en_US-lessac-medium"
|
|
187
|
+
"Cowboy Bob:en_US-joe-medium"
|
|
188
|
+
"Ralf Eisend:en_US-arctic-medium"
|
|
189
|
+
"Northern Terry:en_GB-alan-medium"
|
|
190
|
+
"Lutz Laugh:en_US-joe-medium"
|
|
191
|
+
"Dr. Von Fusion:en_US-danny-low"
|
|
192
|
+
"Demon Monster:en_US-danny-low"
|
|
193
|
+
"Drill Sergeant:en_US-ryan-high"
|
|
194
|
+
"Grandpa Spuds Oxley:en_US-joe-medium"
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
# Default voices by provider
|
|
198
|
+
local elevenlabs_default="Amy"
|
|
199
|
+
local piper_default="en_US-lessac-medium"
|
|
200
|
+
|
|
201
|
+
# If no current voice, return default for target provider
|
|
202
|
+
if [[ -z "$current_voice" ]]; then
|
|
203
|
+
case "$target_provider" in
|
|
204
|
+
piper) echo "$piper_default" ;;
|
|
205
|
+
elevenlabs) echo "$elevenlabs_default" ;;
|
|
206
|
+
*) echo "$piper_default" ;;
|
|
207
|
+
esac
|
|
208
|
+
return 0
|
|
209
|
+
fi
|
|
167
210
|
|
|
168
|
-
|
|
211
|
+
# Convert to lowercase for case-insensitive comparison (portable)
|
|
212
|
+
local current_voice_lower
|
|
213
|
+
current_voice_lower=$(echo "$current_voice" | tr '[:upper:]' '[:lower:]')
|
|
214
|
+
|
|
215
|
+
# Search for mapping
|
|
216
|
+
for mapping in "${voice_mappings[@]}"; do
|
|
217
|
+
local el_voice="${mapping%%:*}"
|
|
218
|
+
local piper_voice="${mapping#*:}"
|
|
219
|
+
local el_voice_lower
|
|
220
|
+
local piper_voice_lower
|
|
221
|
+
el_voice_lower=$(echo "$el_voice" | tr '[:upper:]' '[:lower:]')
|
|
222
|
+
piper_voice_lower=$(echo "$piper_voice" | tr '[:upper:]' '[:lower:]')
|
|
223
|
+
|
|
224
|
+
case "$target_provider" in
|
|
225
|
+
piper)
|
|
226
|
+
# Switching to Piper: look for ElevenLabs voice match
|
|
227
|
+
if [[ "$current_voice_lower" == "$el_voice_lower" ]]; then
|
|
228
|
+
echo "$piper_voice"
|
|
229
|
+
return 0
|
|
230
|
+
fi
|
|
231
|
+
# Already a Piper voice? Keep it if valid format
|
|
232
|
+
if [[ "$current_voice" =~ ^en_ ]]; then
|
|
233
|
+
echo "$current_voice"
|
|
234
|
+
return 0
|
|
235
|
+
fi
|
|
236
|
+
;;
|
|
237
|
+
elevenlabs)
|
|
238
|
+
# Switching to ElevenLabs: look for Piper voice match
|
|
239
|
+
if [[ "$current_voice_lower" == "$piper_voice_lower" ]]; then
|
|
240
|
+
echo "$el_voice"
|
|
241
|
+
return 0
|
|
242
|
+
fi
|
|
243
|
+
# Already an ElevenLabs voice? Keep it
|
|
244
|
+
if [[ ! "$current_voice" =~ ^en_ ]]; then
|
|
245
|
+
echo "$current_voice"
|
|
246
|
+
return 0
|
|
247
|
+
fi
|
|
248
|
+
;;
|
|
249
|
+
esac
|
|
250
|
+
done
|
|
251
|
+
|
|
252
|
+
# No mapping found - return default for target provider
|
|
253
|
+
case "$target_provider" in
|
|
254
|
+
piper) echo "$piper_default" ;;
|
|
255
|
+
elevenlabs) echo "$elevenlabs_default" ;;
|
|
256
|
+
*) echo "$piper_default" ;;
|
|
257
|
+
esac
|
|
169
258
|
}
|
|
170
259
|
|
|
171
260
|
# @function list_providers
|