侧耳倾听:一款实时语音识别APP(含源码)


缘起
有一次和老妈聊天,但是老妈耳朵不好,我要用很大声才能听清,我就想开发个软件可以将我说的话转为文字显示出来,并且要实时的,文字还要大。
功能介绍
- 实时语音识别;支持中文、英文;无需网络连接;仅需录音及浮窗权限
- 支持浮窗显示;大小字体显示;手动保存;定长保存(超过300字)
- 内置WeNet开源模型
- 视频演示



开发将近两个月,界面的交互效果还不理想,也还能扩展一下,比如说英汉互译、加载本地录音文件进行识别等,但是耗时太长了,严重影响了我的主线任务,所以暂时放弃。
WeNet简介
和DeepSeek聊天,了解到国内有开源模型可以使用,我选择了WeNet,特点:端到端模型,工业级落地,支持流式识别。
中文模型:wenet_aishell1
魔搭社区地址: https://modelscope.cn/models/wenet/aishell
Aishell1 数据集训练,209MB;必需文件:final.zip,train.yaml,units.txt。
英文模型:wenet_librispeech
魔搭社区地址: https://modelscope.cn/models/wenet/librispeech
Librispeech 数据集训练,213MB;必需文件:final.zip,train.yaml,units.txt。
WeNet 语音识别工具包(3.1.0版本)
仓库地址:https://github.com/wenet-e2e/wenet
1、 主要目录:
- .github/: 包含GitHub工作流和issue模板
- docs/: 项目文档和图片资源
- examples/: 各种语音数据集的训练示例(aishell, librispeech等)
- runtime/: 运行时组件
- wenet/: 核心源代码
- tools/: 辅助工具
我们只需要runtime/android目录下的文件。
2、android目录结构:
\wenet-3.1.0\runtime\android
├── .gitignore
├── .gradle\
│ ├── 7.5\
│ │ ├── checksums\
│ │ │ └── checksums.lock
│ │ ├── dependencies-accessors\
│ │ │ ├── dependencies-accessors.lock
│ │ │ └── gc.properties
│ │ ├── fileChanges\
│ │ │ └── last-build.bin
│ │ ├── fileHashes\
│ │ │ └── fileHashes.lock
│ │ ├── gc.properties
│ │ └── vcsMetadata\
├── buildOutputCleanup\
│ │ ├── buildOutputCleanup.lock
│ │ └── cache.properties
│ └── vcs-1\
│ └── gc.properties
├── README.md
├── app\
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ ├── src\
│ │ ├── androidTest\
│ │ │ └── java\
│ │ ├── main\
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── assets\
│ │ │ ├── cpp\
│ │ │ ├── java\
│ │ │ └── res\
│ │ └── test\
│ │ └── java\
│ └── wenet.keystore
├── build.gradle
├── gradle\
│ └── wrapper\
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
android\app\src\main\cpp文件夹中只有CMakeLists.txt和wenet.cc,以及8个快捷方式,
你需要将此8个快捷方式指向的文件夹考进来,他们在wenet-3.1.0\runtime\core目录下,分别是:
bin、cmake、decoder、frontend、kaldi、patch、post_processor和utils。
3、 build提示缺少WeTextProcessing:
WeTextProcessing 是一款专注于 文本标准化(Text Normalization, TN) 和 逆向文本标准化(Inverse Text Normalization, ITN) 的开源工具包,由 wenet-e2e 团队开发并维护。它主要用于自然语言处理(NLP)任务中的文本预处理和后处理,广泛应用于语音识别(ASR)、机器翻译、信息检索、聊天机器人等场景。
文本标准化(TN):将非标准文本(如口语化表达)转换为标准书面形式。
示例:
输入:“二点五平方电线” → 输出:“2.5平方电线”
输入:“你好 WeTextProcessing 1.0 船新版本儿” → 输出:“你好 WeTextProcessing 1.0 全新版本”。逆向文本标准化(ITN):将标准化文本还原为自然语言表达。
示例:
输入:“2.5平方电线” → 输出:“二点五平方电线”
输入:“1:05” → 输出:“一点零五分”。你需要引入此库文件,或者直接下载解压到目录\main\cpp
仓库地址:https://github.com/wenet-e2e/WeTextProcessing
APP源码目录结构
包含两个模块:
1、主模块:app
主模块包含了APP的界面和逻辑,主要包括:
(1) MainActivity.kt:
语音识别功能:
使用RecognizerService进行语音识别;管理音频录制(AudioRecord)和识别状态;支持中英文切换识别界面管理功能:
主界面和悬浮窗(FloatingWindowManager)的切换;字体大小调整;文本显示区域管理文件操作功能:
识别结果保存到文件;自动保存选项;清空当前结果权限管理:
处理悬浮窗权限请求;音频录制权限检查
(2) FloatingWindowManager.kt:
- 悬浮窗管理功能:
创建和管理悬浮窗口界面;处理悬浮窗的显示、隐藏和移除;管理悬浮窗的布局参数 - 界面交互功能:
实现悬浮窗的拖动功能;处理点击事件显示/隐藏按钮栏;提供关闭按钮返回主界面 - 字体大小控制:
切换大/小字体显示;同步主界面和悬浮窗的字体大小设置 - 屏幕方向锁定:
提供方向锁定/解锁功能;处理横竖屏切换时的布局调整 - 状态同步:
与主界面(MainActivity)保持状态同步;共享文本显示框引用;同步字体大小设置 - 布局管理:
动态调整悬浮窗布局;处理不同屏幕方向下的布局变化;计算和更新控件尺寸
2、语音识别模块:wenetmodel
包含模型文件,Recognize.java、VoiceRectView.java、RecognizerService.java和cpp文件夹;
(1) assets目录:
- wenetmodels_zn/:中文模型
- wenetmodels_en/:英文模型
(2) Recognize.java:
这个类是语音识别功能的核心桥梁,将 Java 层的调用转发到底层的 C++ 实现。主要功能是作为 Java 本地接口(JNI)的封装类,用于连接 Java 层与底层的 WeNet 语音识别引擎。具体功能包括:
本地库加载:
静态代码块负责加载名为 wenet 的本地库;包含加载成功/失败的日志记录;加载失败时会抛出运行时异常JNI 方法声明:
init(): 初始化模型和解码器;reset(): 重置解码器状态;acceptWaveform(): 接收音频波形数据;setInputFinished(): 设置输入结束标志;getFinished(): 检查识别是否完成;startDecode(): 启动解码线程;getResult(): 获取识别结果;clearResultAtEndpoint(): 清空端点累积结果;getState(): 获取当前解码状态
(3) VoiceRectView.java:用来绘制波形,我没用到。
(4) RecognizerService.java:
语音识别服务管理:
作为后台服务运行语音识别功能;处理服务的生命周期(onCreate, onStartCommand, onDestroy);提供前台服务通知模型管理:
管理中文和英文语音识别模型;从assets目录解压模型文件到设备存储;提供模型初始化功能语音识别处理:
启动/停止语音识别过程;处理音频数据队列;调用底层识别引擎(Recognize类)进行语音识别回调机制:
提供初始化回调(InitializationCallback);提供识别结果回调(RecognitionCallback)多线程处理:
使用独立线程进行模型初始化和语音识别;使用BlockingQueue处理音频数据;使用AtomicBoolean管理识别状态通知管理:
创建和管理前台服务通知;处理通知点击返回主界面
(5) cpp目录:
包含wenet.cc文件,这是C++层的实现代码,负责加载模型、初始化识别器、处理音频数据、调用识别引擎和返回识别结果。
(6) 另外:
文件夹wenetmodel\build中的pytorch_android-1.13.0.aar和wenet-openfst-android-1.0.2.aar重新建构时不要删除。
3、调用逻辑:
1)、初始化阶段
// MainActivity中初始化
recognizer = new RecognizerService(context) // 构造服务
↓
recognizer.initialize(callback) // 触发初始化流程
↓
extractAssets() // 从APK包中解压模型文件到手机存储:data/user/0/com.lhh.voiceAssistant.debug/files/wenet_models/
↓
Recognize.init(modelDir) // 初始化识别引擎
↓
Recognize.reset() // 重置引擎状态
2)、语音识别阶段
recognizer.startRecognition(callback) // 开始识别
↓
Recognize.startDecode() // 启动解码器
↓
// 循环处理音频队列
while (isProcessing) {
audioQueue.poll() // 获取音频数据
↓
Recognize.acceptWaveform(audioData) // 送入引擎
↓
Recognize.getResult() // 获取识别结果
↓
callback.onResult() // 回调结果
}
3)、辅助方法调用
// 音频输入
recognizer.feedAudioData(short[]) // 填充音频数据到队列
↓
audioQueue.offer() // 存入队列
// 停止识别
recognizer.stopRecognition()
↓
Recognize.setInputFinished() // 通知输入结束
4)、关键调用时序图
MainActivity.onCreate()
→ RecognizerService.initialize()
→ extractAssets()
→ Recognize.init()
→ startRecognition()
→ 循环处理 audioQueue
→ stopRecognition()
经验教训:
依赖库的版本错配是所有问题的根源:建议项目初期一定要和AI商量好最合适的版本配置。
CMAKE配置:
由于模型使用的C语言,就要构建系统的配置文件CMakeLists.txt用来跨平台编译。遇到最多的问题就是各种库的链接和依赖顺序。 这在我的源码中,或者相应文件夹下通过文件STUDYME.md进行了记录。
下载地址
app地址(百度网盘) 提取码:mvbr,大约400多兆
源码地址(百度网盘) 提取码:btqy,也是400多兆,我把建构的过程文件删了,你需要重新建构。
请支持我坚持下去,不限额捐赠!!!

微信捐赠

支付宝捐赠
