文档版本:v2.0.0
最后更新:2026年9月7日
项目名称:ReadRecite
目标平台:Android(手机 + TV)
一、项目概述
1.1 项目背景
ReadRecite 是一款基于语音识别技术的智能朗读背书软件。用户提供文本内容后进行朗读背诵,应用通过语音识别实时比对用户朗读与原文,逐字标记正确(蓝色)与错误(红色),并基于朗读情感和内容准确度进行综合评分。
新增特性(v2.0):
-
AI 示范朗读(可选):集成 VoiceStudio 音色克隆与 TTS 能力,用户可录制自己声音克隆,由 AI 用克隆音色完美朗读指定文本,提供无错误、有感情的示范。
-
应用内升级检测:自动检测 cnb.cool 仓库 Release 中的新版本,支持用户一键升级。
1.2 核心功能
| 功能模块 | 描述 |
|---|---|
| 文本导入 | 支持用户输入或粘贴待背诵文本 |
| 实时语音识别 | 基于 ASR 技术将用户语音实时转文本 |
| 逐字比对与标记 | 读对文字变蓝色,读错文字变红色,遗漏保持黑色 |
| 情感识别 | 识别用户朗读语气与情感状态(本地或云端) |
| 智能评分 | 综合准确度、流利度、情感表达等多维度打分 |
| 结果回顾 | 全文展示标记结果,蓝色正确/红色错误/黑色遗漏 |
| AI 示范朗读(可选) | 通过 VoiceStudio 克隆用户音色,生成带情感的全文朗读音频,供用户跟读学习 |
| 应用内升级 | 检查 cnb.cool Release 版本,自动下载并安装新 APK |
1.3 技术选型
-
开发语言:Kotlin
-
最低 SDK:API 21(Android 5.0),以兼容 TV 设备
-
目标 SDK:API 34(Android 14)
-
架构模式:MVVM + Clean Architecture
-
语音识别:讯飞语音识别 SDK / Google Speech Recognition API(本地)
-
情感分析:audEERING sensAI / 讯飞情感分析(本地)
-
AI TTS 服务(可选):VoiceStudio(远程服务,需用户自行部署)
-
UI 框架:Jetpack Compose(自适应手机与 TV 布局)
-
依赖注入:Hilt
-
异步处理:Kotlin Coroutines + Flow
-
升级检测:使用 Retrofit + OkHttp 访问 cnb.cool API
二、开发规范
(本节内容与 v1.0 相同,为节省篇幅,此处略去详细内容,但在实际文档中应完整保留。以下仅列出标题,实际完整文档需包含全部规范。)
2.1 代码风格
(Kotlin 规范、Ktlint、Detekt、Lint 等)
2.2 架构规范
(MVVM + Clean Architecture,分层模块划分)
2.3 命名规范
(类、函数、变量、包名等命名规则)
三、功能模块详细设计
3.1 文本管理模块
(同 v1.0,略)
3.2 语音识别模块
(同 v1.0,略)
3.3 文本比对与标记模块
(同 v1.0,略)
3.4 情感识别模块
(同 v1.0,略)
3.5 评分模块
(同 v1.0,略)
3.6 AI 示范朗读模块(可选)
3.6.1 功能概述
该模块允许用户录制 3-15 秒的语音样本,通过 VoiceStudio 服务克隆用户音色,然后使用该音色将任意文本合成为流畅、富有情感的朗读音频,供用户跟读和模仿。
3.6.2 前置条件
-
用户必须自行部署 VoiceStudio 服务(本地或远程服务器),并获取服务端的访问地址(如
http://192.168.1.100:3900)。 -
该功能默认关闭,需用户在“设置”中主动开启并填入服务端 URL。
3.6.3 交互流程
text
- 用户进入“AI 示范朗读”入口
- 若未配置服务端地址,提示用户进入设置填写
- 若已配置但连接失败,提示错误并建议检查服务状态
- 首次使用:提示用户录制 3-15 秒朗读样本(建议安静环境)
- 点击“克隆音色” → 将音频上传到 VoiceStudio → 返回 voice_id
- 选择要示范的文本(可从当前背诵文本中选取)
- 点击“生成示范音频” → 调用 TTS 合成 → 返回音频文件
- 播放音频,用户跟读
- 跟读后进入原有的背诵比对流程
3.6.4 API 接口定义(Android 端)
服务接口定义:
kotlin
interface VoiceStudioApi { // 音色克隆 @Multipart @POST("/v1/audio/cloning") suspend fun cloneVoice( @Part audio: MultipartBody.Part, @Part("name") name: String, @Part("language") language: String = "zh" ): Response<CloneResponse> // TTS 合成(OpenAI 兼容接口) @POST("/v1/audio/speech") suspend fun synthesizeSpeech( @Body request: TtsRequest ): Response<ResponseBody> } data class CloneResponse(val voice_id: String, val status: String) data class TtsRequest( val model: String = "voicestudio-tts", val input: String, val voice: String, val response_format: String = "mp3", val instructions: String? = null // 用于情感控制 )
数据模型:
kotlin
data class VoiceCloneResult( val voiceId: String, val createdAt: Long ) data class ReciteAudio( val file: File, val durationMs: Long )
3.6.5 服务端地址管理
-
地址存储在
SharedPreferences中,加密存储(使用 Android Keystore)。 -
提供设置界面让用户输入和测试连接。
-
每次调用前检查地址是否为空,若为空则引导用户配置。
3.6.6 错误处理与降级
-
若 VoiceStudio 服务不可用,该功能按钮变灰并显示“服务未连接”。
-
若生成失败,提示用户检查服务状态或重新尝试。
-
所有网络请求设置超时(连接 10s,读取 30s)。
3.7 应用内升级模块
3.7.1 功能概述
自动检测 cnb.cool 仓库 Release 页面的最新版本,与当前应用版本比较,若有新版本则提示用户下载并安装。
3.7.2 升级检测流程
text
- 应用启动时(或用户手动点击“检查更新”)触发检测
- 请求 cnb.cool 仓库的 Release API(需知晓仓库地址)
- 解析返回的 JSON,获取最新版本号(tag_name)和 APK 下载地址
- 与当前 versionCode 或 versionName 比较
- 若有新版本,弹窗提示更新内容,用户点击“立即更新”
- 后台下载 APK(使用 DownloadManager)
- 下载完成后自动跳转安装界面(需申请 INSTALL_PACKAGES 权限)
3.7.3 API 接口
cnb.cool 的 Release API 遵循 GitHub Releases 风格,示例:
text
GET https://cnb.cool/{owner}/{repo}/-/releases/latest
返回示例:
json
{ "tag_name": "v2.0.1", "name": "Release 2.0.1", "body": "修复语音识别延迟问题", "assets": [ { "name": "app-release.apk", "browser_download_url": "https://cnb.cool/.../app-release.apk" } ] }
Android 端接口定义:
kotlin
interface CnCoolReleaseApi { @GET("/{owner}/{repo}/-/releases/latest") suspend fun getLatestRelease(): ReleaseInfo } data class ReleaseInfo( @SerializedName("tag_name") val tagName: String, val name: String, val body: String, val assets: List<Asset> ) data class Asset( val name: String, @SerializedName("browser_download_url") val downloadUrl: String )
3.7.4 版本比较逻辑
-
使用语义化版本比较(如
2.0.1>2.0.0)。 -
若无法解析语义化版本,则回退到字符串比较(但会发出警告)。
-
比较前先移除
v前缀。
kotlin
fun isNewerVersion(current: String, latest: String): Boolean { val cleanCurrent = current.removePrefix("v") val cleanLatest = latest.removePrefix("v") return try { val curParts = cleanCurrent.split('.').map { it.toInt() } val latParts = cleanLatest.split('.').map { it.toInt() } // 逐位比较,不足补0 for (i in 0 until maxOf(curParts.size, latParts.size)) { val c = curParts.getOrElse(i) { 0 } val l = latParts.getOrElse(i) { 0 } if (l > c) return true if (l < c) return false } false } catch (e: NumberFormatException) { // 非数字则字符串比较 cleanLatest > cleanCurrent } }
3.7.5 下载与安装
-
使用 Android
DownloadManager进行后台下载,避免 ANR。 -
下载完成后通过
DownloadManager的广播接收器触发安装。 -
对于 Android 8.0+,需申请
REQUEST_INSTALL_PACKAGES权限,并引导用户开启“允许安装未知来源”。
代码示例:
kotlin
fun downloadAndInstall(context: Context, url: String) { val request = DownloadManager.Request(Uri.parse(url)).apply { setTitle("下载更新") setDescription("正在下载新版本 APK") setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, "update.apk") } val dm = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager val id = dm.enqueue(request) // 注册广播接收器监听下载完成 context.registerReceiver(object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { val completeId = intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) ?: -1 if (completeId == id) { val file = File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "update.apk") installApk(context, file) } } }, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) } fun installApk(context: Context, file: File) { val uri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file) val intent = Intent(Intent.ACTION_VIEW).apply { setDataAndType(uri, "application/vnd.android.package-archive") addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) } context.startActivity(intent) }
3.7.6 升级提示策略
-
启动时静默检测,若有新版本则在主界面显示红点或弹窗(可配置为自动弹窗或手动检查)。
-
在“设置”页面提供“检查更新”按钮,用户主动触发。
-
更新内容(Release Body)以 Markdown 形式展示(简单解析)。
四、UI/UX 设计规范
4.1 手机端设计
(同 v1.0,略)
4.2 TV 端适配规范
(同 v1.0,略)
4.3 新增设置界面
在设置中增加:
-
“AI 示范朗读”开关(默认关闭)
-
“VoiceStudio 服务地址” 输入框(URL)
-
“测试连接” 按钮(验证服务是否可用)
-
“检查更新” 按钮(手动触发升级检测)
-
当前版本号显示
五、基准测试规范
(本节与 v1.0 基本相同,新增 VoiceStudio 相关测试项)
5.1 性能基准测试
(同 v1.0,Macrobenchmark 启动性能、帧率等)
5.2 微基准测试
(同 v1.0)
5.3 语音识别基准测试
(同 v1.0)
5.4 VoiceStudio 集成测试(新增)
| 测试项 | 指标 | 验收标准 |
|---|---|---|
| 音色克隆响应时间 | 从上传到返回 voice_id | ≤ 10s(网络良好) |
| TTS 合成响应时间 | 请求到返回音频 | 每 100 字 ≤ 5s |
| 音频播放流畅度 | 播放卡顿次数 | 零卡顿 |
| 服务不可用时的降级表现 | 界面反馈 | 3s 内提示错误,不崩溃 |
| 升级检测响应时间 | 从请求到结果显示 | ≤ 3s |
六、CI/CD 配置(cnb.cool)
(同 v1.0,但在 .cnb.yml 中增加构建后自动生成 Release 的步骤,以便升级模块获取最新版本。)
6.1 基本构建配置
(同 v1.0)
6.2 自动发布 Release(建议)
在 master 分支的 CI 中,当构建成功且为 tag 触发时,自动创建 Release 并上传 APK,这样升级模块就能通过 API 获取。
yaml
master: push: tags: - v* - docker: image: mobiledevops/android-sdk-image:34.0.1 volumes: - /root/.gradle:cow stages: - name: build-release script: ./gradlew assembleRelease - name: upload-artifacts script: | # 将 APK 作为 Release 资产上传(需使用 cnb.cool CLI) cnb release create ${CI_COMMIT_TAG} --assets ./app/build/outputs/apk/release/*.apk
(具体 CLI 命令需参考 cnb.cool 官方文档,此处为示例)
七、测试策略
(同 v1.0,需增加 VoiceStudio 集成测试和升级模块测试)
7.1 单元测试
(同 v1.0)
7.2 集成测试
(同 v1.0)
7.3 UI 测试
(同 v1.0)
7.4 TV 专项测试
(同 v1.0)
7.5 升级模块测试(新增)
-
模拟有更新和无更新场景
-
下载中断恢复(网络切换)
-
权限拒绝时的降级处理
-
安装流程验证
7.6 VoiceStudio 服务集成测试(新增)
-
服务地址为空时禁用功能
-
服务地址错误时的提示
-
克隆和合成流程的端到端测试
-
超时和重试机制
八、部署与运维
(同 v1.0,需增加 VoiceStudio 服务部署建议和升级服务说明)
8.1 版本号规范
(同 v1.0)
8.2 签名配置
(同 v1.0)
8.3 VoiceStudio 服务部署建议(新增)
-
最低配置:4 核 CPU + 8GB RAM(建议 16GB)+ 至少 10GB 存储
-
推荐配置:带有 NVIDIA GPU(CUDA 支持)的服务器,或者 Apple M 系列芯片 Mac
-
Docker 部署命令:
bash
docker run -d -p 3900:3900 -v omnivoice-data:/app/omnivoice_data --name voicestudio palashdeb/omnivoice-studio:stable
-
首次启动会自动下载模型,约需几分钟。
-
服务启动后,默认 API 地址为
http://<服务器IP>:3900,Android 端应填写此地址。
8.4 升级服务说明
-
确保 cnb.cool 仓库中的 Release 包含正确命名的 APK 文件(如
app-release.apk)。 -
Release 的
tag_name需与versionName对应,且遵循语义化版本。 -
升级模块依赖网络,建议在 Wi-Fi 环境下进行大文件下载。
九、附录
9.1 核心依赖(新增)
kotlin
dependencies { // ... 原有依赖
// 网络请求(用于 VoiceStudio 和升级检测)
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
// 音频播放(如果需要更精细控制)
implementation("androidx.media:media:1.7.0")
}
9.2 AndroidManifest 权限(新增)
xml
<!-- 网络访问 --> <uses-permission android:name="android.permission.INTERNET" /> <!-- 下载更新所需 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <!-- 文件共享 --> <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>