Inspiration

「聲跡日記」源自一個很簡單的問題:日常中稍縱即逝的想法,能不能在不打斷當下的情況下被留下來?

傳統筆記需要停下工作、開啟應用程式並整理文字;一般錄音工具雖然保存了聲音,卻常留下大量難以搜尋與回顧的音檔。我們因此想打造一款介於錄音機與日記之間的工具:讓它在背景安靜工作,將語音轉成可閱讀、可修正、可長期保存的文字,同時不把私人錄音或轉錄內容送上雲端。

視覺上,我們也不希望它像一套充滿表格、儀表與狀態燈的工程工具。聲音是流動且帶有情緒的,因此介面被設計成一本打開的日誌:月份化為紙本聲景,片段依時間落在頁面上,讓使用者感受到的不是「操作轉錄系統」,而是「留下生活的痕跡」。

What It Does

「聲跡日記」是一款為 Windows 設計的本機語音日誌:

  • 440×190 精簡浮窗常駐桌面上方,顯示錄音狀態、即時文字與主要控制。
  • 今日工作台以預設 1180×820 展開,依「小時章節+語音片段」呈現當天紀錄。
  • Sherpa-ONNX 即時預覽先提供低延遲文字回饋。
  • Faster-Whisper 最終轉錄在背景產生較準確的定稿。
  • SQLite 保存權威狀態,Markdown 則是依小時重建的可閱讀輸出。
  • 文字可直接在時間軸中修正;人工修正會被鎖定,避免遭到較晚返回的模型結果覆蓋。
  • 日常錄音與轉錄在模型安裝完成後可完全離線執行。
  • 設定頁會掃描本機 TTF/OTF 字體,支援 14–26px 字級與個人化日記排版。
  • 真正發生的設定變更會以 before/after、異動欄位與 UTC 時間寫入耐久 JSONL 紀錄。
  • 十二個月份各有八種狀態,形成 96 個邏輯場景;每個場景分別製作精簡窗與工作台版本,共 192 張離線 WebP

How We Built It

專案以 Python 3.11、PySide6 與 Qt Quick/QML 開發。錄音、辨識、資料庫、匯出與復原邏輯保留在 Python 核心層;QML 只透過 JournalViewModel 和不可變 read-model 取得資料,不直接操作 SQLite。

整體流程為:

  1. 從 Windows WASAPI 接收麥克風音訊。
  2. 透過 VAD 辨識有效語音區段。
  3. 先將音訊安全寫入 FLAC spool。
  4. 將耐久片段加入 SQLite 與今日時間軸。
  5. 即時辨識引擎提供預覽文字。
  6. Finalizer 在獨立 worker 中完成最終轉錄。
  7. 依台北時間重建對應小時的 Markdown。
  8. 確認資料與輸出安全落地後,才清理暫存音訊。

核心耐久條件可以表示為:

[ \text{DeleteAudio} \iff \text{FinalTranscriptPersisted} \land \text{MarkdownAtomicWriteSucceeded} ]

顯示文字則遵循:

[ T_{\text{display}}= \begin{cases} T_{\text{final}}, & \text{最終轉錄可用時}\ T_{\text{preview}}, & \text{仍在處理時} \end{cases} ]

時間軸採虛擬化 ListView,並使用 timeline_revision 判斷耐久資料是否真正改變,避免在高頻 UI polling 中反覆查詢整天的 SQLite 紀錄。使用者正在回看或編輯時,背景刷新不會覆蓋草稿或強制改變捲動位置。

視覺資產先透過 ImageGen 建立一致的月份與狀態場景,再轉為經 manifest 管理的離線成品。每張圖都記錄版型、提示詞、尺寸與 SHA-256;執行時不產生圖片,也不需要連接 ImageGen。介面利用紙張色調、羽化區域與低細節閱讀空間讓插畫成為頁面材料,而不是一張被硬塞進框架的圖片。

Challenges We Faced

Balancing responsiveness and accuracy

即時文字必須夠快,才能讓使用者確認系統正在工作;但預覽結果又不能被誤認為永久定稿。我們將即時預覽與最終辨識拆成不同 worker,讓介面可以快速回應,同時維持較高品質的最終內容。

Making voice capture durable

錄音過程可能遇到程式崩潰、worker 中斷、磁碟空間不足、Windows 檔案占用或佇列壅塞。即時 UI 可以降級,但使用者的記憶不能因此消失。因此片段必須先安全落地,SQLite 必須能恢復中間狀態,Markdown 也必須以暫存檔、同步寫入及原子替換完成。

Preventing live updates from disrupting reading

時間軸會持續收到新片段,但使用者可能正在回看較早內容或編輯文字。我們需要區分「正在跟隨最新內容」和「使用者主動離開底部」,並在後者顯示新片段提示,而不是搶走捲動位置。

Integrating generated artwork into the product

早期介面中的直式插圖有明顯外框,看起來像後來才鑲進去的海報。我們重新設計場景構圖,取消卡片與裱框感,讓顏料、紙張與月份色調延伸到整個內容區,同時在文字附近保留足夠留白與呼吸感。

Combining expressive fonts with readable layouts

日記體與書寫字體的字面高度、行距及中英文 family name 差異很大。固定寬度按鈕在放大字級後容易截斷,因此我們加入本機字體掃描、名稱正規化、動態內容寬度及最大字級版面測試。授權字體只存在使用者本機,不進入 wheel 或公開發行內容。

Shipping safely on Windows

應用程式不只要能在開發環境執行,還要處理非 ASCII 路徑、CUDA/CPU 差異、登入排程、單例 mutex、既有 SQLite/WAL 與更新失敗回滾。安裝流程因此被視為產品的一部分,而不是最後才補上的腳本。

What We Learned

  • 即時回饋可以是 best-effort,但使用者資料必須 durable。
  • SQLite 與 Markdown 應扮演不同角色。 前者負責真實狀態與復原,後者負責閱讀、備份與攜出。
  • 生成式圖片需要完整的資產工程。 一致的藝術指引、版型重構、manifest、雜湊與離線驗證,和生成圖片本身同樣重要。
  • 安靜的介面更依賴細節。 當外框與卡片被移除後,留白、行高、字級及操作區安全距離會直接決定品質。
  • 離線是一項端到端承諾。 它包含模型、錄音、圖片、字體、設定、安裝與故障復原,而不只是「沒有呼叫雲端 API」。

What’s Next

下一階段會繼續深化既有核心,而不是急著加入搜尋、摘要或跨日瀏覽:

  • 改善不同 DPI、多螢幕與長時間執行下的視窗行為。
  • 擴充大量片段時的時間軸效能與編輯穩定性測試。
  • 強化 worker 中斷、占用檔案與安裝回滾的故障驗證。
  • 持續檢查 192 張場景的構圖一致性、可讀性與狀態辨識。
  • 優化不同字體及字級下的即時排版。
  • 以更長時間的真實使用驗證錄音、轉錄、修正、匯出與復原流程。

「聲跡日記」最終想完成的,不是一個更華麗的錄音介面,而是一種更自然的記憶方式:讓聲音在背景被可靠保存,等到需要回看時,它已經成為一本安靜、可讀、屬於自己的日誌。

Built With

  • ctranslate2
  • cuda
  • faster-whisper
  • local-ai
  • onnx
  • openai-whisper
  • opencc
  • pyside6
  • python
  • qml
  • qt
  • sherpa-onnx
  • sounddevice
  • speech-to-text
  • sqlite
  • voice-activity-detection
  • wasapi
Share this project:

Updates