- TTSBridge.synthesize wraps stdin/stdout request-response in asyncio.Lock;
concurrent calls previously caused 'readuntil() called while another
coroutine is already waiting' and ENOENT (temp dir deleted while bridge
still reading input.txt)
- TTSService.start/stop guarded by lifecycle lock to prevent race from
rapid Start clicks creating duplicate bridge/worker/danmaku connections
- Verified: 20 concurrent synthesize calls all succeed with correct WAV
- Rebuild portable zip (83.8 MB)
- Add phonemize (MIT) rule-based G2P as second fallback tier
- IPA phoneme -> katakana CV-syllable transcription table
- proposal -> プラポーザル, bilibili -> バイリーバイリー (no more letter-spelling)
- Letter spelling kept as last resort only
- Bridge logs SYNTH IN/OUT to stderr; tts.py drains stderr to Python logs
so conversion results are visible in the server log
- Rebuild portable zip (83.8 MB)