基于微信小程序与SenseVoice-Small的实时语音笔记应用开发
基于微信小程序与SenseVoice-Small的实时语音笔记应用开发
你有没有过这样的经历?开会时奋笔疾书,结果会后一看,笔记记得乱七八糟,关键信息可能还漏了。或者突然有个灵感,想用手机记下来,但打字速度跟不上思维的速度,灵感转瞬即逝。
现在,我们可以用技术来解决这个问题。今天要聊的,就是怎么自己动手做一个微信小程序,让它能实时录音,然后“秒变”成文字笔记。核心就是用上了开源的语音识别模型SenseVoice-Small,它体积小、速度快,特别适合放在云端服务里,让小程序来调用。
整个过程听起来有点技术,但别担心,我会用最直白的方式,带你走一遍从想法到实现的全过程。你会发现,把语音变成文字,并变成一个可用的工具,并没有想象中那么复杂。
1. 这个应用能解决什么问题?
简单说,我们想做的是一个“语音速记本”。它的核心工作流程是这样的:你在微信小程序里按下录音键,开始说话;小程序一边录音,一边把音频数据打包,悄悄传给后端的服务器;服务器上跑着的SenseVoice-Small模型会立刻开始工作,把语音转换成文字;这些文字又实时地传回小程序,显示在屏幕上。等你录完音,一篇结构清晰的文字笔记初稿就已经在那儿了,你只需要稍微修改和润色就行。
这个工具特别适合几个场景:
- 会议记录:再也不用手忙脚乱地打字,专注听讲,语音自动转成文字纪要。
- 课堂笔记:学生上课时可以用来记录重点,复习时看文字比听录音更高效。
- 灵感捕捉:创作者、思考者随时记录脑中闪现的想法,用说的总比打的快。
- 访谈整理:媒体人或研究者进行访谈时,可以快速获得文字素材。
传统的解决方案要么需要依赖特定的App,要么转写服务收费不菲。我们自己搭建一个,成本可控,而且数据和流程完全掌握在自己手里,用起来更放心。
2. 技术方案与整体设计
要把这件事做成,我们需要“三驾马车”协同工作:微信小程序、后端服务器和语音识别模型。
微信小程序负责和用户交互。它的任务包括:调用手机的麦克风录音、把录好的声音压缩成合适的格式、通过网络把音频数据发送给服务器,并且把服务器返回的文字漂亮地展示出来,让用户能编辑和保存。
后端服务器是我们的“中间人”和“大脑”。它用Python的Web框架(比如Flask或FastAPI)搭建一个简单的服务。这个服务有两个核心功能:一是接收小程序发来的音频数据;二是调用我们部署好的SenseVoice-Small模型,让模型去识别音频,然后把识别出的文字结果再送回给小程序。
SenseVoice-Small模型是核心的“翻译官”。它是一个开源、轻量级的语音识别模型,在保证不错准确率的同时,对计算资源的要求相对友好,非常适合我们部署在云服务器上,提供API服务。
它们三者的关系,我画了一个简单的图帮你理解:
用户说话 ↓ [微信小程序] 录音、压缩、发送 ↓ (通过网络HTTP/WebSocket) [后端服务器] 接收音频、调用模型 ↓ [SenseVoice-Small] 语音转文字 ↓ [后端服务器] 返回文字结果 ↓ (通过网络) [微信小程序] 接收、显示、编辑文字整个流程的关键在于“实时性”。我们当然可以等用户录完一整段再上传、识别,但体验最好的方式是“流式”处理,也就是录一点,传一点,识别一点,返显一点。这样用户能看到文字随着他的说话实时出现,心里有底,体验也更流畅。接下来,我们就分步看看具体怎么实现。
3. 微信小程序前端开发要点
小程序部分是我们的门面,重点在于把录音、通信和界面展示做好。
3.1 录音功能实现
微信小程序提供了强大的wx.getRecorderManager()API来管理录音。我们首先要在小程序的配置文件app.json里,声明需要用户授权录音的权限。
// app.json 部分配置 { "requiredPermissions": [ "scope.record" ], "permission": { "scope.record": { "desc": "需要您授权使用麦克风,以实现录音笔记功能" } } }然后,在页面的JavaScript逻辑里,我们可以这样创建和管理录音:
// pages/index/index.js Page({ data: { isRecording: false, recordedText: '', tempAudioPath: '' }, onLoad: function() { // 初始化录音管理器 this.recorderManager = wx.getRecorderManager(); // 监听录音开始事件 this.recorderManager.onStart(() => { console.log('录音开始'); this.setData({ isRecording: true }); }); // 监听录音结束事件,拿到临时文件路径 this.recorderManager.onStop((res) => { console.log('录音结束', res); this.setData({ isRecording: false, tempAudioPath: res.tempFilePath // 录音文件的临时路径 }); // 录音结束后,自动调用上传函数 this.uploadAudioFile(res.tempFilePath); }); // 监听录音错误事件 this.recorderManager.onError((err) => { console.error('录音失败:', err); wx.showToast({ title: '录音失败,请重试', icon: 'none' }); }); }, // 开始录音 startRecording: function() { const options = { duration: 60000, // 最长录音60秒,可根据需要调整 sampleRate: 16000, // 采样率,16kHz是语音识别的常用设置 numberOfChannels: 1, // 单声道 encodeBitRate: 48000, // 编码码率 format: 'aac' // 格式,AAC兼容性好,文件小 }; this.recorderManager.start(options); }, // 停止录音 stopRecording: function() { this.recorderManager.stop(); } })这里有几个参数需要注意:sampleRate(采样率)设置成16000Hz,是因为大多数语音识别模型在这个采样率下工作得最好;format(格式)选择aac,能在保证音质的同时有效压缩文件大小,节省流量和传输时间。
3.2 音频上传与实时通信
录音结束后,我们需要把音频文件发送到后端服务器。为了追求实时效果,我们可以采用分片上传或使用WebSocket。这里以简单的HTTP POST上传为例,演示如何将音频文件发送到后端。
// pages/index/index.js 中的 uploadAudioFile 方法 uploadAudioFile: function(tempFilePath) { wx.showLoading({ title: '转写中...' }); wx.uploadFile({ url: 'https://你的服务器地址/api/transcribe', // 你的后端API地址 filePath: tempFilePath, name: 'audio', formData: { 'format': 'aac', 'sample_rate': '16000' }, success: (res) => { wx.hideLoading(); if (res.statusCode === 200) { const result = JSON.parse(res.data); if (result.success) { // 将识别到的文字追加到现有文本中 const newText = this.data.recordedText + result.text + '\n'; this.setData({ recordedText: newText }); wx.showToast({ title: '转写完成', icon: 'success' }); } else { wx.showToast({ title: '识别失败:' + result.error, icon: 'none' }); } } else { wx.showToast({ title: '上传失败', icon: 'none' }); } }, fail: (err) => { wx.hideLoading(); wx.showToast({ title: '网络错误', icon: 'none' }); console.error('上传失败:', err); } }); }对于真正的“实时”流式传输,上述“录完一段传一段”的方式(称为“准实时”)已经能提供不错的体验。如果要求字字秒回,则需要使用WebSocket,在录音onFrameRecorded回调中不断发送音频数据帧,后端也进行流式识别。这会更复杂,但原理相通。
3.3 文本展示与编辑界面
收到识别文字后,我们需要一个友好的界面来展示和编辑。我们可以用一个可编辑的<textarea>来显示文本,并提供一些简单的编辑按钮。
<!-- pages/index/index.wxml --> <view class="container"> <view class="control-panel"> <button wx:if="{{!isRecording}}" type="primary" bindtap="startRecording">开始录音</button> <button wx:if="{{isRecording}}" type="warn" bindtap="stopRecording">停止录音</button> <text class="hint">{{isRecording ? '正在录音...' : '点击上方按钮开始'}}</text> </view> <view class="text-display"> <text class="title">语音笔记内容:</text> <textarea class="text-area" value="{{recordedText}}" placeholder="识别后的文字将显示在这里..." bindinput="onTextInput" auto-height maxlength="-1" ></textarea> </view> <view class="action-panel"> <button bindtap="clearText">清空文本</button> <button bindtap="saveNote">保存笔记</button> <button bindtap="copyText">复制文本</button> </view> </view>相应的样式和逻辑处理:
// pages/index/index.js 中补充方法 onTextInput: function(e) { this.setData({ recordedText: e.detail.value }); }, clearText: function() { wx.showModal({ title: '清空确认', content: '确定要清空所有文本吗?', success: (res) => { if (res.confirm) { this.setData({ recordedText: '' }); } } }); }, saveNote: function() { const text = this.data.recordedText; if (!text.trim()) { wx.showToast({ title: '内容为空', icon: 'none' }); return; } // 这里可以调用云函数或API将文本保存到云端或本地缓存 wx.setStorageSync('last_note', text); wx.showToast({ title: '已暂存', icon: 'success' }); }, copyText: function() { const text = this.data.recordedText; wx.setClipboardData({ data: text, success: () => wx.showToast({ title: '已复制', icon: 'success' }) }); }这样,一个具备基本录音、转写、显示和编辑功能的小程序前端就搭建好了。接下来,我们需要一个强大的后端来提供识别能力。
4. 后端服务与SenseVoice-Small部署
后端是我们的“动力车间”。我们需要做两件事:一是部署SenseVoice-Small模型,二是搭建一个能接收音频、调用模型并返回结果的Web服务。
4.1 搭建基础后端服务
我们使用Python的Flask框架,因为它轻量、简单。首先,确保你的服务器环境(比如一台云服务器)已经安装了Python3和pip。
创建一个项目目录,并安装必要的依赖:
mkdir voice_note_backend cd voice_note_backend python3 -m venv venv source venv/bin/activate # Windows系统用 venv\Scripts\activate pip install flask torch transformers soundfile librosa然后,我们创建一个最简单的Flask应用来测试接口是否通畅:
# app.py from flask import Flask, request, jsonify import os app = Flask(__name__) @app.route('/api/transcribe', methods=['POST']) def transcribe_audio(): # 1. 检查是否有文件上传 if 'audio' not in request.files: return jsonify({'success': False, 'error': 'No audio file provided'}), 400 audio_file = request.files['audio'] # 2. 保存上传的音频文件(临时) temp_path = f"temp_{os.urandom(8).hex()}.aac" audio_file.save(temp_path) # 3. 这里暂时模拟识别结果,下一步会替换成真正的模型调用 # 假设我们有一个函数叫 `transcribe_with_model(temp_path)` try: # transcribed_text = transcribe_with_model(temp_path) # 真实调用 transcribed_text = "这是模拟的语音识别结果。请确保SenseVoice-Small模型已正确部署。" # 4. 清理临时文件 os.remove(temp_path) # 5. 返回识别结果 return jsonify({ 'success': True, 'text': transcribed_text }) except Exception as e: # 如果出错,也尽量清理文件 if os.path.exists(temp_path): os.remove(temp_path) return jsonify({'success': False, 'error': str(e)}), 500 if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True)运行这个程序 (python app.py),你的后端服务就在本地的5000端口启动了。此时,它还不能真正识别语音,但已经可以接收文件并返回一个模拟响应。你可以用Postman等工具测试一下/api/transcribe这个接口。
4.2 集成SenseVoice-Small模型
现在,我们来接入核心——SenseVoice-Small模型。我们需要从Hugging Face模型库加载这个模型。首先,确保安装了transformers库(上面已安装)。
修改app.py,引入模型并实现真正的识别函数:
# app.py (更新版) from flask import Flask, request, jsonify import torch from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor import librosa import soundfile as sf import os import numpy as np app = Flask(__name__) # 全局加载模型和处理器,避免每次请求都重复加载 print("正在加载SenseVoice-Small模型,这可能需要几分钟...") model_id = "SenseTime/sensevoice-small" # SenseVoice-Small的模型ID # 加载处理器和模型 processor = AutoProcessor.from_pretrained(model_id) model = AutoModelForSpeechSeq2Seq.from_pretrained(model_id) # 将模型设置为评估模式,并移动到GPU(如果可用) device = "cuda:0" if torch.cuda.is_available() else "cpu" model.to(device) model.eval() print(f"模型加载完成,运行在: {device}") def transcribe_with_model(audio_path): """ 使用SenseVoice-Small模型转录音频文件 """ # 1. 加载音频文件 # SenseVoice模型期望16kHz采样率的输入 speech_array, sampling_rate = librosa.load(audio_path, sr=16000, mono=True) # 2. 使用处理器准备模型输入 inputs = processor( raw_speech=speech_array, sampling_rate=sampling_rate, return_tensors="pt", padding=True ) # 3. 将输入数据移动到与模型相同的设备(GPU/CPU) input_features = inputs.input_features.to(device) # 4. 执行推理(生成文字) with torch.no_grad(): # 禁用梯度计算,加快推理速度 generated_ids = model.generate(input_features) # 5. 将生成的token ID解码为文字 transcription = processor.batch_decode(generated_ids, skip_special_tokens=True)[0] return transcription @app.route('/api/transcribe', methods=['POST']) def transcribe_audio(): if 'audio' not in request.files: return jsonify({'success': False, 'error': '未提供音频文件'}), 400 audio_file = request.files['audio'] # 保存临时文件,使用更安全的唯一文件名 import uuid temp_filename = f"temp_{uuid.uuid4().hex}.aac" temp_path = os.path.join("/tmp", temp_filename) # Linux/Mac临时目录 # Windows系统可以改为: temp_path = os.path.join(os.environ['TEMP'], temp_filename) audio_file.save(temp_path) try: # 调用真正的模型进行识别 transcribed_text = transcribe_with_model(temp_path) # 清理临时文件 os.remove(temp_path) return jsonify({ 'success': True, 'text': transcribed_text }) except Exception as e: # 出错时也清理文件 if os.path.exists(temp_path): os.remove(temp_path) app.logger.error(f"识别失败: {str(e)}") return jsonify({'success': False, 'error': f'语音识别处理失败: {str(e)}'}), 500 if __name__ == '__main__': # 在生产环境中,应使用更稳定的WSGI服务器,如Gunicorn app.run(host='0.0.0.0', port=5000, debug=False) # 生产环境请设置debug=False几点重要的说明:
- 模型加载:第一次运行时会从网上下载SenseVoice-Small模型,可能需要一些时间和网络。下载后模型会缓存,下次启动就快了。
- 硬件要求:SenseVoice-Small虽然相对轻量,但在CPU上推理可能较慢。如果追求更好的实时性,建议使用带有GPU的云服务器。代码中已自动检测并使用CUDA GPU。
- 临时文件:我们使用系统的临时目录来存放上传的音频文件,并在处理完成后立即删除,避免磁盘空间被占满。
- 错误处理:代码中添加了基本的异常捕获,确保服务在出错时也能返回友好的错误信息,而不是直接崩溃。
4.3 部署与上线建议
本地测试没问题后,你需要将后端服务部署到公网服务器,这样小程序才能访问到。
- 购买云服务器:选择一家云服务商(如阿里云、腾讯云),购买一台至少2核4G的云服务器(如果使用GPU版本更好)。记得在安全组(防火墙)中开放你后端服务使用的端口(例如5000)。
- 上传代码:将你的
voice_note_backend项目文件夹上传到服务器。 - 安装依赖:在服务器上同样创建虚拟环境并安装所需Python包。
- 使用生产级服务器:Flask自带的开发服务器不适合生产环境。建议使用
Gunicorn或uWSGI配合Nginx来部署。pip install gunicorn # 在项目目录下运行 gunicorn -w 4 -b 0.0.0.0:5000 app:app - 配置HTTPS:微信小程序要求后端接口必须为HTTPS。你需要在Nginx中配置SSL证书,将HTTPS请求代理到Gunicorn服务。
- 修改小程序配置:将小程序代码中的
url替换为你服务器的HTTPS地址和正确端口。
5. 实际应用与效果体验
当小程序和后端都部署好后,整个应用就跑通了。你可以打开小程序,点击录音,说一段话。比如:“大家好,欢迎参加今天的项目例会。本次会议主要讨论三个议题:第一季度总结、下阶段规划以及资源分配。”
稍等片刻(取决于网络和服务器性能),屏幕上就会逐句或整段地出现识别出的文字。你会发现,对于清晰的普通话,SenseVoice-Small的识别准确率是相当不错的,完全能满足会议纪要、灵感记录这类场景的需求。
在实际使用中,你可能会遇到一些情况,并可以这样优化:
- 环境噪音:在嘈杂环境下录音,识别准确率会下降。可以在小程序端增加一个“环境检测”提示,建议用户在相对安静的环境下使用。
- 长语音处理:我们设定了最长60秒的录音片段。对于更长的会议,可以提示用户分段录音,或者在服务器端实现音频拼接和长语音识别(SenseVoice-Small支持长音频,但需注意服务器内存)。
- 识别结果编辑:提供便捷的编辑工具很重要。除了我们已有的清空、复制、保存,还可以考虑增加“标为重点”、“分段”、“查找替换”等功能,让笔记整理更高效。
- 多平台同步:将笔记保存后,可以通过云函数同步到其他平台(如Notion、语雀),构建个人知识管理体系。
这个项目的魅力在于,它不仅仅是一个工具,更是一个起点。基于这个“语音转文字”的核心能力,你可以扩展出很多功能,比如为识别内容自动添加标点、提取关键词、生成摘要,甚至进行情绪分析,让一个简单的笔记应用变得越来越智能。
6. 总结
走完这一趟,我们从零开始,把一个“语音实时转笔记”的想法变成了一个可用的微信小程序。前端利用小程序的能力处理录音和交互,后端用Python和SenseVoice-Small模型提供语音识别这个“硬核”功能,两者通过网络API连接起来。
整个过程涉及了小程序开发、后端API编写、深度学习模型部署等多个环节,算是一个比较完整的全栈小项目。虽然里面有些技术细节,但一步步拆解开来,每一步都有清晰的路径。最重要的是,你做出了一个真正能解决自己问题的工具,这种成就感是无可替代的。
SenseVoice-Small这样的开源模型,大大降低了语音AI的应用门槛。你可以根据自己的需求调整它,比如针对专业词汇进行微调,或者尝试其他更大型号的版本以获得更高的准确率。这个项目的代码和思路也是一个模板,你可以很容易地把它改造成一个“语音翻译机”、“实时字幕生成器”或者“访谈整理助手”。
技术最终是为了服务人与生活。希望这个案例不仅能给你提供一个可运行的程序,更能给你带来一种“用技术创造工具”的思维。动手试试看,从录制第一段语音,看到屏幕上出现第一行字开始,你会发现,很多有趣的事情,其实你都能自己做出来。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
