Skip to content

Releases: Tianqi-Bu/Doppelvoice

Doppelvoice v0.3.3 — defense-in-depth crash hardening

Choose a tag to compare

@Tianqi-Bu Tianqi-Bu released this 26 Apr 17:01

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 fallback print(file=sys.stderr) was
    vulnerable to the same sys.stderr is None failure that took out
    v0.3.0/v0.3.1. A user with a malformed/missing .env would have triggered it.

Added

  • __main__.py: installed sys.excepthook = _last_resort_handle. Any uncaught
    exception now (1) writes a timestamped traceback to
    %APPDATA%\Doppelvoice\logs\crash.log and (2) shows a Qt
    QMessageBox.critical with 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.log produced
  • Doppelvoice.exe --help → 9-language description string
  • Doppelvoice.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

Choose a tag to compare

@Tianqi-Bu Tianqi-Bu released this 26 Apr 16:50

🚨 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 the sys.stderr console sink when sys.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 fallback sys.stderr.write(...).
  • __main__.py: explicit if stream is None: continue before 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)

Choose a tag to compare

@Tianqi-Bu Tianqi-Bu released this 26 Apr 14:58

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 header v0.2.1v0.3.1; scenario table no longer assumes Chinese-as-source / English-as-target; acceptance checklist references Doppelvoice.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.py argparse description: was Chinese-only "中→英实时同传…", now an English summary listing all 9 languages. Affects Doppelvoice.exe --help output.

Bumped

  • pyproject.toml / __init__.py 0.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

Choose a tag to compare

@Tianqi-Bu Tianqi-Bu released this 26 Apr 13:22

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

  1. Install VB-Audio Virtual Cable → run as admin → reboot. (If you forget, the app pops a download button.)
  2. Unzip the release. You get one .exe + 4 reference files.
  3. Double-click Doppelvoice.exe (first launch takes 7-10 s while it unpacks).
  4. Settings dialog opens automatically — paste your Doubao App Key + Access Token from Volcengine Console, Save.
  5. 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 single Doppelvoice.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 .env and log files live there now. Backward-compat: if you used v0.2.x and have a .env next to Doppelvoice.exe, it keeps working from there.
  • No manual .env editing required. First launch opens the Settings dialog automatically; saving creates the .env via atomic write. The shipped .env.example is 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 .exe replaces the Doppelvoice/ 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

Choose a tag to compare

@Tianqi-Bu Tianqi-Bu released this 26 Apr 13:07

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

  • Credentials is now frozen=True — was the one mutable sub-config that escaped the v0.2.0 frozen sweep. Three sites mutated it in-place; because AppConfig.snapshot() is a shallow copy, those writes silently leaked into already-running orchestrator sessions. All callers now use replace() and the type system enforces it.
  • SubtitleView clamps each block to 4096 chars. QTextDocument layout is O(n²) on a single huge run; a malicious or buggy server sending a 4 MB text field would freeze the GUI thread.
  • gui/env_io.py _dequote now matches python-dotenv exactly. The old .strip().strip('"').strip("'") chain ate unmatched quotes ("'weird'"weird instead of 'weird') and exposed inner padding (" spaced " spaced instead of spaced). The GUI display path now agrees with what Credentials.from_env actually loads at runtime.
  • utils/log.py _patcher no longer silently swallows arg-rewrite failures — falls through to sys.stderr (cannot use loguru without recursing into the patcher itself).

Tests (86 → 93)

  • OggOpusDecoder.MAX_BUF_BYTES guard (sentence_end-never-fires DoS).
  • _dequote parity with python-dotenv (matched / unmatched / inner padding).
  • read_env UTF-8 BOM round-trip.
  • Credentials FrozenInstanceError + replace-path.

Full changelog: CHANGELOG.md

Doppelvoice v0.2.2 — 20-agent adversarial fix sweep

Choose a tag to compare

@Tianqi-Bu Tianqi-Bu released this 26 Apr 06:09

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.py referenced _HAS_SOXR without importing it — NameError on every sample-rate-mismatch path, silently breaking capture when device's native rate ≠ 16 kHz.
  • audio/opus_decoder.py had unbounded _chunks growth if the server emitted TTSSentenceStart without TTSSentenceEnd (malicious or stuck server) → OOM. Now hard-capped at 64 MB; size() is O(1).
  • gui/env_io.py parsed .env as 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_KEY while runtime got nothing. Now utf-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 SessionStarted is acknowledged.
  • closeEvent didn't stop _metrics_timer first — 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 Orchestrator holds an immutable snapshot).
  • cli.py --jitter-ms help text said "default 240"; actually 120 since v0.2.0.

Added

  • __post_init__ validation on AudioConfig + NetworkConfig — clear ValueError instead 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.example with 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.py adds _validate_ws_url; test_config.py adds AppConfig.load env-var matrix.

Cleanup

  • requirements.txt pinned upper bounds (numpy<3, websockets<17, protobuf<7, etc.); grpcio-tools dropped from runtime (lives in [dev] extras).
  • 11 dead i18n keys removed (wizard.* never wired; config.mode etc. never displayed).
  • Unused bus.metrics and bus.usage signals removed.

Full changelog: CHANGELOG.md

Doppelvoice v0.2.1 — metadata + CI cleanup

Choose a tag to compare

@Tianqi-Bu Tianqi-Bu released this 26 Apr 05:33

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

  1. Install VB-Audio Virtual Cable and reboot
  2. Get Doubao credentials from Volcengine Console
  3. Unzip → copy .env.example to .env → fill in DOUBAO_APP_KEY / DOUBAO_ACCESS_KEY
  4. Double-click Doppelvoice.exe
  5. 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.toml was unbuildable — missing PySide6, protobuf, qasync, soundfile dependencies. pip install . now produces a runnable install. Heavy dev-only deps (grpcio-tools, pyinstaller) moved to [project.optional-dependencies].dev.
  • Version drift: pyproject.toml and __init__.py both still said 0.1.0. Bumped to 0.2.1.
  • LICENSE copyright line said "tongchuan contributors" (pre-rename). Now "Doppelvoice contributors".
  • pyproject.toml description 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.toml now 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

Choose a tag to compare

@Tianqi-Bu Tianqi-Bu released this 26 Apr 05:11

First binary release — no Python install needed, just unzip and run.

Download

Doppelvoice-v0.2.0-win64.zip (61 MB)

```
SHA256: 4F5634587E150342F1FCDC0EE0F844D79C37C1D41C974EA7C5B15AA67EE85A28
```

Quick start

  1. Install VB-Audio Virtual Cable and reboot
  2. Get Doubao credentials from Volcengine Console
  3. Unzip → copy `.env.example` to `.env` → fill in `DOUBAO_APP_KEY` / `DOUBAO_ACCESS_KEY`
  4. Double-click `Doppelvoice.exe`
  5. 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

v0.1.0

Choose a tag to compare

@Tianqi-Bu Tianqi-Bu released this 25 Apr 20:14

First public release.

End-to-end Chinese↔English speech-to-speech translation with zero-shot voice cloning, powered by ByteDance Doubao Seed LiveInterpret 2.0.

See README for setup.