Releases: Tianqi-Bu/Doppelvoice
Release list
Doppelvoice v0.3.3 — defense-in-depth crash hardening
Summary
Defense-in-depth follow-up to v0.3.2's hotfix. v0.3.2 fixed the specific
logger.add(None, ...) crash, but a static sweep found another identical-shape
vulnerability in cli.py, and the bundle still had no last-resort handler for
any unhandled exception escaping main(). v0.3.3 plugs both holes so a future
regression of the same family ("stdio is None in windowed bundle") still leaves
the user with a friendly Qt dialog and a crash log on disk — never the opaque
PyInstaller "Unhandled exception in script" box again.
No functional changes for users on v0.3.2 who never hit a crash — this is
purely belt-and-suspenders.
Fixed
cli.py main(): configuration-error fallbackprint(file=sys.stderr)was
vulnerable to the samesys.stderr is Nonefailure that took out
v0.3.0/v0.3.1. A user with a malformed/missing.envwould have triggered it.
Added
__main__.py: installedsys.excepthook = _last_resort_handle. Any uncaught
exception now (1) writes a timestamped traceback to
%APPDATA%\Doppelvoice\logs\crash.logand (2) shows a Qt
QMessageBox.criticalwith the trace tail and the log path. Falls back
gracefully if Qt itself is unavailable.
Verified
End-to-end smoke from a fresh extraction directory (not the project root):
- GUI double-click → launches clean, no
crash.logproduced Doppelvoice.exe --help→ 9-language description stringDoppelvoice.exe --check(no.env) → exit 2 + Chinese friendly error
Download
Doppelvoice-v0.3.3-win64.zip (60.5 MB)
SHA256: 7D259E46ACC0D4E3B4E57A3630B4F3D4E94F0B1CB3CFF7CA21E0A5A55FEFE193
Unzip, double-click Doppelvoice.exe, paste your Doubao AppID + AccessToken
into the Settings dialog. No .env editing required.
Doppelvoice v0.3.2 — HOTFIX: onefile startup crash
🚨 Critical hotfix. v0.3.0 and v0.3.1 .exe binaries crashed at startup with TypeError: Cannot log to objects of type 'NoneType' for all users who double-clicked the bundled .exe. v0.3.2 is the fixed binary.
Download
Doppelvoice-v0.3.2-win64.zip (60.5 MB)
SHA256: 4DCCA817B5031CCE2DB95E287C35D1C2E5FE5923B9DB2295A66B6DB48569047F
If you're on v0.3.0 or v0.3.1: download this and replace your Doppelvoice.exe. Your .env in %APPDATA%\Doppelvoice\ is preserved.
Root cause
PyInstaller's onefile + windowed bootloader (console=False) detaches stdio. Inside a bundled GUI app sys.stderr is None, but utils/log.py was doing logger.add(sys.stderr, ...) unconditionally. loguru rejects None sinks with TypeError, killing the .exe on every double-click.
This regression slipped past me because in dev mode (python -m doppelvoice) sys.stderr is always a real TTY, so the bug never surfaced until a real user fired up the bundled binary.
Fixed
utils/log.py setup_logging: skip thesys.stderrconsole sink whensys.stderr is None. The file sink under%APPDATA%\Doppelvoice\logs\keeps working — users still get logs.utils/log.py _patcher: same fix for the redact-failure fallbacksys.stderr.write(...).__main__.py: explicitif stream is None: continuebefore stdio reconfigure for clarity.
Tests
Added test_setup_logging_with_none_stderr: monkeypatches sys.stderr = None and asserts setup_logging does not raise + the file sink still produces output. Total tests now 94.
Verified
Locally retested: Doppelvoice.exe double-clicked from dist/ launches the GUI cleanly (no Tkinter exception dialog), Settings opens automatically when .env is missing, log written to %APPDATA%\Roaming\Doppelvoice\logs\doppelvoice_YYYYMMDD.log.
Lesson
v0.3.0's onefile switch was tested on the dev box where sys.stderr is a real TTY. The crash only surfaced for fresh .exe users via the windowed bootloader. Onefile + console=False deserves an explicit smoke test in CI — tracked for v0.4.
Full changelog: CHANGELOG.md
Doppelvoice v0.3.1 — doc-drift fixes (binary refresh)
Binary refresh of v0.3.0 with the post-release documentation cleanup baked in. Same install, same behavior — Doppelvoice.exe --help now reflects the multi-language description and __version__ reads 0.3.1.
Download
Doppelvoice-v0.3.1-win64.zip (60.5 MB)
SHA256: B5224E6B3C10DD16169FDAAAC25A75208ABCDA6ABCD50DB1202B528299FF4746
Same 5-step quick start as v0.3.0 — see README.txt inside the zip.
What changed since v0.3.0
No code behavior changes. Strictly description-drift cleanup that made the README, PRDs, SETUP guides, and --help output match the actual 9-language capability.
Documented
- README (en + zh): tagline + ASCII diagram now show
<source lang> → <target lang>and inline-list the 9 supported codes. The old "Chinese → English" framing left over from v0.1.x is gone. docs/{en,zh}/PRD.md: bumped Version headerv0.2.1→v0.3.1; scenario table no longer assumes Chinese-as-source / English-as-target; acceptance checklist referencesDoppelvoice.exe; roadmap fills in v0.2.1 / v0.2.2 / v0.2.3 / v0.3.0 / v0.3.1.docs/{en,zh}/SETUP.md: rewritten as Path A (binary, recommended) + Path B (source, dev). Path A walks through download → unzip → double-click → fill Settings dialog. Cross-language link added in both files.cli.pyargparsedescription: was Chinese-only "中→英实时同传…", now an English summary listing all 9 languages. AffectsDoppelvoice.exe --helpoutput.
Bumped
pyproject.toml/__init__.py0.3.0 → 0.3.1.
If you're already on v0.3.0 and don't care about --help text, you can stay there — there are no functional improvements in v0.3.1.
Full changelog: CHANGELOG.md
Doppelvoice v0.3.0 — single .exe, GUI-only setup
Open-box experience overhaul. Single-file .exe, no manual file editing required, GUI handles everything.
Download
Doppelvoice-v0.3.0-win64.zip (60.5 MB)
SHA256: B17661B74C0E6513FDEE835A74641D3D17AD08835A28BA9DF4006144C0DB064E
5-step quick start
- Install VB-Audio Virtual Cable → run as admin → reboot. (If you forget, the app pops a download button.)
- Unzip the release. You get one
.exe+ 4 reference files. - Double-click
Doppelvoice.exe(first launch takes 7-10 s while it unpacks). - Settings dialog opens automatically — paste your Doubao App Key + Access Token from Volcengine Console, Save.
- In your meeting app set the mic to
CABLE Output (VB-Audio Virtual Cable). Speak — the peer hears your voice translated.
That's it. No .env editing, no folder navigation, no Python install.
What's new in v0.3.0
Changed (UX)
- PyInstaller bundle is now
onefile— distribution is a singleDoppelvoice.exe(~63 MB extracted, 60 MB zipped) instead of a 154 MB folder. First launch takes 7-10 s while the bundled_internal/unpacks to%TEMP%; subsequent launches are roughly the same. - Data directory moved to
%APPDATA%\Doppelvoice\in bundled mode (~/Library/Application Support/Doppelvoice/on macOS,~/.local/share/doppelvoice/on Linux). Your.envand log files live there now. Backward-compat: if you used v0.2.x and have a.envnext toDoppelvoice.exe, it keeps working from there. - No manual
.envediting required. First launch opens the Settings dialog automatically; saving creates the.envvia atomic write. The shipped.env.exampleis reference-only.
Added
- "Download VB-Cable" button in the missing-virtual-audio-device dialog. One click opens the VB-Audio download page. Replaces the earlier text-only warning.
Migration from v0.2.x
- Single
.exereplaces theDoppelvoice/folder. Delete the old folder; nothing inside it is needed. - Your existing
.env(if it sat next to the old.exe) keeps working transparently.
Trade-off: first-launch is 7-10 s slower than v0.2.x onedir mode (PyInstaller's onefile bootloader unpacks _internal/ on every launch). Steady-state runtime is identical.
Full changelog: CHANGELOG.md
Doppelvoice v0.2.3 — review tier-1 follow-ups
Tier-1 follow-ups from the v0.2.2 20-agent adversarial review.
Tests 86 → 93. No behavior changes for normal usage.
Download
Doppelvoice-v0.2.3-win64.zip (61 MB)
SHA256: 789446E15607D535AA825BF253E3750829C8C36857A7F0B337B2DC79F36D3B0E
Same install as v0.2.2 — see README.txt inside the zip.
What changed since v0.2.2
Fixed
Credentialsis nowfrozen=True— was the one mutable sub-config that escaped the v0.2.0 frozen sweep. Three sites mutated it in-place; becauseAppConfig.snapshot()is a shallow copy, those writes silently leaked into already-running orchestrator sessions. All callers now usereplace()and the type system enforces it.SubtitleViewclamps each block to 4096 chars. QTextDocument layout is O(n²) on a single huge run; a malicious or buggy server sending a 4 MBtextfield would freeze the GUI thread.gui/env_io.py _dequotenow matches python-dotenv exactly. The old.strip().strip('"').strip("'")chain ate unmatched quotes ("'weird'"→weirdinstead of'weird') and exposed inner padding (" spaced "→spacedinstead ofspaced). The GUI display path now agrees with whatCredentials.from_envactually loads at runtime.utils/log.py _patcherno longer silently swallows arg-rewrite failures — falls through tosys.stderr(cannot use loguru without recursing into the patcher itself).
Tests (86 → 93)
OggOpusDecoder.MAX_BUF_BYTESguard (sentence_end-never-fires DoS)._dequoteparity with python-dotenv (matched / unmatched / inner padding).read_envUTF-8 BOM round-trip.CredentialsFrozenInstanceError+replace-path.
Full changelog: CHANGELOG.md
Doppelvoice v0.2.2 — 20-agent adversarial fix sweep
20 parallel sub-agent reviews (architect / security / performance / Python /
code / refactor / docs / tdd / first-install UX / 24h stability / cross-platform /
deps / concurrency / WS protocol / audio math / error UX / dist UX / i18n /
malicious-server / .env edges) drove this patch release.
Tests: 46 → 86. Coverage: 14% → 24%. No public API changes.
Download
Doppelvoice-v0.2.2-win64.zip (61 MB)
SHA256: 58F3CA7ED81BAB9B9A9005C91394ECE6FF48CEDFE6C3338F901E026F48DDB5B1
Extract → copy .env.example → .env → fill in DOUBAO_APP_KEY / DOUBAO_ACCESS_KEY from Volcengine Console → double-click Doppelvoice.exe.
What changed since v0.2.1
Fixed (CRITICAL)
audio/capture.pyreferenced_HAS_SOXRwithout importing it —NameErroron every sample-rate-mismatch path, silently breaking capture when device's native rate ≠ 16 kHz.audio/opus_decoder.pyhad unbounded_chunksgrowth if the server emittedTTSSentenceStartwithoutTTSSentenceEnd(malicious or stuck server) → OOM. Now hard-capped at 64 MB;size()is O(1).gui/env_io.pyparsed.envas utf-8 strict — Windows Notepad's default UTF-8 save adds a BOM, which made the GUI think it had a key called\ufeffDOUBAO_APP_KEYwhile runtime got nothing. Nowutf-8-sig.
Fixed (HIGH)
- Log redaction missed bare-keyword fields (
password=,secret=,token=) because the leading[A-Za-z]consumed the first character before the keyword could match. - Status badge displayed literal
"status.busy"after a language switch (i18n key reverse-lookup used the mapped state value instead of the original key). - Resample fallback returned the input array at the wrong sample rate when
n_out == 0. - Reconnect counter only reset after a complete session, so 5+ short network drops climbed the exponential backoff to the 30s cap (30 s of black audio per drop). Now resets as soon as
SessionStartedis acknowledged. closeEventdidn't stop_metrics_timerfirst — 150 ms window where the GUI could touch a stopped capture.- Settings menu (Ctrl+,) wasn't disabled while a session was running — user changes silently ignored (because
Orchestratorholds an immutable snapshot). cli.py --jitter-mshelp text said "default 240"; actually 120 since v0.2.0.
Added
__post_init__validation onAudioConfig+NetworkConfig— clearValueErrorinstead of cryptic PortAudio-9997.- VB-Cable startup pre-check in
gui/app.py— finally fires the i18n-translated dialog (keys existed since v0.1 but were never wired). THIRD_PARTY_LICENSES.md+ LICENSE in dist — LGPL compliance for bundled PySide6 (Qt 6), libsoxr, libsndfile.- Bilingual
.env.examplewith explicit "App Key vs Access Token" guidance — first-install UX testing confirmed users routinely swap the two. - UTF-8 stdio reconfigure on Windows — Chinese error messages no longer show as GBK mojibake in CMD.
Tests (46 → 86)
- New:
test_env_io.py,test_opus_decoder.py,test_orchestrator_units.py. - Expanded:
test_doubao_classify.pyadds_validate_ws_url;test_config.pyaddsAppConfig.loadenv-var matrix.
Cleanup
requirements.txtpinned upper bounds (numpy<3,websockets<17,protobuf<7, etc.);grpcio-toolsdropped from runtime (lives in[dev]extras).- 11 dead i18n keys removed (
wizard.*never wired;config.modeetc. never displayed). - Unused
bus.metricsandbus.usagesignals removed.
Full changelog: CHANGELOG.md
Doppelvoice v0.2.1 — metadata + CI cleanup
Patch release. Same binary behavior as v0.2.0, fixes packaging metadata so pip install . actually works, plus CI and an updated README.
Download
Doppelvoice-v0.2.1-win64.zip (61 MB)
SHA256: 1004D6111213A2A31F5A80E2D15E9D7FC50A0BD983979E43BABB065F7F934D5A
Quick start
- Install VB-Audio Virtual Cable and reboot
- Get Doubao credentials from Volcengine Console
- Unzip → copy
.env.exampleto.env→ fill inDOUBAO_APP_KEY/DOUBAO_ACCESS_KEY - Double-click
Doppelvoice.exe - In your meeting app, set the microphone to
CABLE Output (VB-Audio Virtual Cable)
See README.txt inside the zip.
What changed since v0.2.0
Audit-driven cleanup. No new features, no behavior changes.
Fixed
pyproject.tomlwas unbuildable — missingPySide6,protobuf,qasync,soundfiledependencies.pip install .now produces a runnable install. Heavy dev-only deps (grpcio-tools,pyinstaller) moved to[project.optional-dependencies].dev.- Version drift:
pyproject.tomland__init__.pyboth still said0.1.0. Bumped to0.2.1. - LICENSE copyright line said "tongchuan contributors" (pre-rename). Now "Doppelvoice contributors".
pyproject.tomldescription was Chinese-only and claimed "中→英"; replaced with English text that lists all 9 supported languages.
Added
- GitHub Actions CI: pytest on Windows / Python 3.10/3.11/3.12 with coverage report.
pyproject.tomlnow has proper PyPI classifiers, keywords, URLs, readme reference.- README (en + zh): two-path install (binary vs source); real CI badge replaces the static one; Release shield added.
Full changelog: CHANGELOG.md
Doppelvoice v0.2.0 — multi-language + Windows binary
First binary release — no Python install needed, just unzip and run.
Download
Doppelvoice-v0.2.0-win64.zip (61 MB)
```
SHA256: 4F5634587E150342F1FCDC0EE0F844D79C37C1D41C974EA7C5B15AA67EE85A28
```
Quick start
- Install VB-Audio Virtual Cable and reboot
- Get Doubao credentials from Volcengine Console
- Unzip → copy `.env.example` to `.env` → fill in `DOUBAO_APP_KEY` / `DOUBAO_ACCESS_KEY`
- Double-click `Doppelvoice.exe`
- In your meeting app, set the microphone to `CABLE Output (VB-Audio Virtual Cable)`
See `README.txt` inside the zip for the full walkthrough.
What's new in v0.2.0
A 24-file overhaul driven by 4 parallel code review agents (architecture / Python / security / performance). Tests grew 3 → 46.
Highlights
- 9 languages per Doubao spec: `zh / en / ja / id / es / pt / de / fr / zhen` (last is bilingual ZH⇄EN auto)
- Voice clone tuning: explicit `denoise=false` in TranslateRequest retains breath / resonance details
- Audio device dedup: one entry per physical device (MME 31-char name truncation handled)
- Subtitle memory cap (MAX_BLOCKS=2000) for long sessions
- Atomic .env write (tempfile + os.replace) — credentials safe on crash
- Log redaction patcher — API keys / Bearer tokens auto-scrubbed
- WebSocket URL allowlist + 4 MB frame guard
- AppConfig snapshot isolation — running session unaffected by mid-run settings changes
- First Windows binary via PyInstaller — no Python install required
Performance
- Mic level meter merged into capture stream (dropped second InputStream)
- TranslateRequest template cached (saves ~12 protobuf alloc/sec)
- silence_rms_threshold 0.010 → 0.0 (was clipping first-syllable consonants, +300-500 ms first-word latency)
- _receiver_loop split into 4 handler functions + _ReceiverContext
Known limits
- Use headphones, not external speakers. With speakers, the meeting audio gets re-captured by your mic and re-translated back to the peer (acoustic feedback loop). Every video conferencing tool hits this — they ship built-in AEC; we don't yet.
- End-to-end latency floor ≈ 2.5 s per the Seed LiveInterpret 2.0 paper.
- Public AST API is below the Volcengine Console demo (separate BFF endpoint with extra prosody).
Full changelog: CHANGELOG.md