Skip to content

Repository files navigation

Shadowing

Shadowing App Icon

macOS 本地英语跟读练习应用。打开 MP3,选一段循环听,同步录音,在同一时间轴上对比原音与多条 Take。

flowchart LR
A[打开 MP3] --> B[选择并循环片段]
B --> C[同步录音]
C --> D[多 Take 对比]
D --> E[本地恢复]
Loading

数据默认只保存在本机:无账号、无上传、无网络依赖。

界面预览

Library

拖入或选择 MP3,在统一的 Projects 列表里继续练习(含 Take 数量与最近活动时间)。右上角齿轮打开 Settings。

Library:打开 MP3 与 Projects 列表

Practice

Original 与多条 Take 纵向对齐;选区循环、变速、录音与删除 Take 都在同一页完成。

Practice:Original 与多条 Take 对齐

功能概览

  • Library:拖入或选择 MP3;统一 Projects 列表(有录音时显示 Take 数量)
  • 练习页:完整波形 Overview、选区缩放 / 平移、循环播放、变速与音量
  • 多轨跟读:Original + 多条 Take 纵向对齐;Take 可独立播放与选区循环
  • 覆盖重录:选中 Take 后继续录音,按原音时间轴合并(空隙补静音)
  • Take 排序:新 Take 默认在 Original 下方;可拖拽调整顺序
  • Settings:窗口右上角齿轮打开浮窗(麦克风、倒计时、录音时是否播放原音等)

技术栈

选择
UISwiftUI · Swift 6 · macOS 15+
音频AVFoundation / Core Audio
元数据GRDB / SQLite
录音与波形缓存本地文件系统
工程XcodeGen(Shadowing/project.yml 为事实来源)

MVP 引入 Rust、UniFFI、cargo-swift、网络服务或 AI 评分。持久化通过 Swift 协议注入,后续若评估 UniFFI,见 ADR-0010

架构

依赖方向见 ADR-0003:上层只依赖 Domain 协议,具体音频与存储实现由 App 组装后注入,不向 View / Domain 泄漏 AVFoundation 或 GRDB。

flowchart TB
subgraph UI["界面层"]
Views["SwiftUI Views<br/>Library · Practice · Settings"]
VM["ViewModels @MainActor<br/>状态 · intent · 取消"]
end
subgraph Domain["领域层"]
Models["值对象与规则<br/>Project · Region · Take"]
Protocols["协议边界<br/>Repository · AudioClient · Store"]
end
subgraph Adapters["适配层(由 App 注入)"]
Audio["Audio<br/>播放 / 循环 / 录音<br/>render-time 时钟"]
Persist["Persistence<br/>SQLite 元数据<br/>录音与波形文件"]
Services["Services<br/>书签 · 权限 · Session"]
end
Views -->|"用户操作"| VM
VM -->|"调用协议"| Protocols
Models --- Protocols
Audio --> Protocols
Persist --> Protocols
Services --> Protocols
Loading
分层做什么不做什么
Views显示状态、发送 intent不碰 AVFoundation、GRDB、文件 I/O
ViewModels协调练习流程、异步任务与取消不持有数据库具体类型
Domain模型、不变量、状态规则与协议不导入 SwiftUI / AVFoundation / GRDB
Audio选区循环、同步录音、波形采样;循环与录音边界用 sample/render time不在实时 callback 里访问数据库或阻塞主线程
PersistenceProject / Take 元数据(SQLite)与录音文件;Take 提交顺序为临时写入 → 校验 → 原子移动 → 元数据事务不向外泄漏 GRDB record 类型
Servicessecurity-scoped bookmark、麦克风权限、打开/恢复会话不承载 UI 状态

AppDependencies.live() 是唯一组装点:把 PracticeAudioEngine、GRDB repository、RecordingFileStore、bookmark 等接到协议上,再交给 Features。更多决策见 ADR 索引

环境要求

  • macOS 15 或更高
  • Xcode(含 macOS SDK)
  • Homebrew

开发

make setup # 安装工具、hooks,并生成 Xcode 工程
make build # Debug 构建(无签名)
make upgrade # 重新构建并启动 Debug app
make test# 单元测试
make check # format + lint + build + test
make dmg # Release 无签名 DMG(build/Shadowing-<version>.dmg)

make setup 会按 Brewfile 安装依赖、安装 pre-commit hooks,并生成 Shadowing/Shadowing.xcodeproj。生成的工程不要提交,请改 Shadowing/project.yml

运行 make help 可查看全部命令。本地与 CI 共用 make check 作为质量门禁。

源码结构

Shadowing/
├── App/ 入口与依赖组装
├── Domain/ 模型、规则与持久化协议
├── Features/ 功能 View / ViewModel
├── Audio/ 播放、录音与波形
├── Persistence/ GRDB 与文件存储
├── Services/ 权限、书签等平台能力
└── Tests/ 单元 / 契约 / migration 测试

文档

LICENSE

本项目采用 Apache License 2.0

About

Local-first macOS English shadowing app — loop clips, record in sync, compare takes. No account, no upload.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages