更新记录
1.0.0(2026-10-07)
- 支持 Android 麦克风实时语音转文字、停止继续、累计计时和音量波纹示例。
- 支持保存及导出 WAV 录音和 TXT 文字,长录音按小时切片。
- 支持自动重连、未送达音频区间记录和本地草稿恢复。
- 支持导入录音、流式转换、分段上传、任务进度、取消和继续查询。
- 提供 Android 原生依赖配置、完整使用说明和 uni-app Vue 3 示例工程。
平台兼容性
uni-app(4.25)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | 7.0 | - | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
讯飞长时间语音转文字
iflytek-asr · 版本 1.0.0 · Android UTS API 插件
为普通 uni-app Vue 3 的 Android 应用提供实时语音转文字、停止和继续、录音保存导出、累计计时、音量事件、长时间连续转写,以及录音文件导入转写。示例页面已提供对应按钮、音量波纹、实时文字、保存历史和文件任务进度。
使用前,使用者须自行前往讯飞开放平台申请开通“实时语音转写大模型”和“录音文件转写大模型”,并配置自己的 AppID、APIKey、APISecret。申请入口及步骤见下文“使用的讯飞语音模型与申请”。插件不附带讯飞服务授权或调用额度。
配套演示动图展示实时转写页面、开始录音、录音计时、停止收尾和识别文字。
当前配置要求 HBuilderX 4.27.0+、Android 5.0(API 21)+。需要制作包含本插件的 Android 自定义基座或正式安装包。uni-app x、Vue 2 和 nvue 尚未作为当前发布版本的验证范围;iOS、鸿蒙、Web、小程序及 uniCloud 没有对应实现。
功能
| 功能 | 行为 |
|---|---|
| 实时转写 | 点击开始后申请麦克风权限,以 16kHz / 16bit / 单声道 PCM 发送音频并持续返回文字 |
| 停止与继续 | 停止采集并收取尾句,状态进入 paused 后可继续同一会话;暂停期间不累计录音时间 |
| 保存当前对话 | 保存 WAV、TXT 和会话信息;录音过程中可保存快照,不会结束当前录音 |
| 计时与音量 | 返回累计 durationMs 和 0–1 的相对音量,示例用它们显示计时和波纹 |
| 长时间录音 | 本地 WAV 每小时切片,实时连接在 7 小时 45 分收尾后建立新连接 |
| 断线恢复 | 自动重连,持续保存本地音频,记录已知未送达区间,支持保存后导入补转 |
| 后台与草稿 | Android 麦克风前台服务和通知停止按钮;定期保存草稿,重新打开后可手动继续 |
| 导入录音 | 系统选择文件,流式转换为标准 WAV,长文件按小时顺序提交 |
| 任务取消与恢复 | 取消本地处理或查询,恢复时沿用已保存订单;支持复制和导出转写文字 |
本插件提供语音转文字。AI 回复、语音合成、语音唤醒、离线识别和客户热词分组不在当前接口范围内。
安装
- 从插件市场导入,或把
iflytek-asr.zip解压到项目的uni_modules/iflytek-asr/。该目录下应直接包含package.json、readme.md、changelog.md和utssdk/。 - 使用 HBuilderX 为 Android 制作包含本插件的自定义基座,并安装到设备。修改 UTS、Kotlin、原生清单或 Maven 依赖后需重新制作。
- 本地完整示例位于项目根目录的
IflytekASR-demo。下载iflytek-asr-example.zip后,解压并使用 HBuilderX 打开其中的IflytekASR-demo工程。发布示例的manifest.json已清空应用标识和 SDK 配置,运行或打包前填写使用者自己的 DCloud AppID,并按需配置所用 SDK。
插件通过 WebSocket / HTTPS 直接调用讯飞服务,无需下载 SparkChain.aar。Android 网络依赖已在 utssdk/app-android/config.json 声明:
{
"minSdkVersion": 21,
"dependencies": [
"com.squareup.okhttp3:okhttp:3.12.12",
"com.squareup.okio:okio:1.15.0"
]
}
导入插件时保留全部 Kotlin 文件和 AndroidManifest.xml。只复制 index.uts 不能完整运行。
使用的讯飞语音模型与申请
本插件实际对接以下两项讯飞语音大模型服务。请按表中的官方服务名称申请开通;完整使用实时转写和导入录音功能时,同一讯飞应用需要同时具备两项服务的有效权限和额度。
| 插件功能 | 使用的讯飞语音模型 / 服务 | 当前识别模式 | 官网申请入口 | 官方对接文档 |
|---|---|---|---|---|
| 麦克风实时转文字、停止继续、长时间连续转写 | 实时语音转写大模型 | lang=autodialect |
实时转写服务申请 · 产品页 | 实时语音转写大模型文档 |
| 导入录音转文字、保存录音后的文件补转 | 录音文件转写大模型 | language=autodialect |
录音文件转写服务申请 · 产品页 | 录音文件转写大模型文档 |
两项服务当前均使用 autodialect 中英及中文方言免切识别模式,范围以各自官方文档及应用权限为准。当前插件接口未提供识别模式或模型版本切换参数。
使用者自行申请步骤
- 前往讯飞开放平台官网注册、登录自己的账号,并进入控制台。
- 创建或选择自己的讯飞应用,在上表两个服务申请入口中,为同一应用分别申请开通实时语音转写大模型和录音文件转写大模型。申请条件、试用额度和正式套餐以官网当前页面为准。
- 在对应服务的控制台中取得该应用的 AppID、APIKey、APISecret,确认两项服务权限有效、额度可用。只创建应用或取得凭证,还需完成对应服务的开通。
- 将自己的三个凭证填写到示例工程的
config/credentials.local.js;自行接入时,传给initialize({ appId, apiKey, apiSecret })。apiKey对应请求中的accessKeyId,apiSecret用于生成请求签名。 - 分别测试实时录音和导入录音功能。遇到权限或额度错误时,到对应服务的控制台检查应用、授权和剩余额度。
凭证配置
发布包不包含作者的实际讯飞 AppID、APIKey、APISecret、请求签名、SDK 授权文件或 SDK 二进制。示例 ZIP 中 config/credentials.local.js 和 config/credentials.example.js 均使用空白模板,运行前填写使用者自己的有效凭证:
export default {
appId: 'YOUR_APPID',
apiKey: 'YOUR_API_KEY',
apiSecret: 'YOUR_API_SECRET'
}
讯飞 AppID 与 manifest.json 的 DCloud AppID 各自使用,不要互相替换。signature 由插件按实际请求参数和时间自动生成,无需手动传入或提前注册。
先注册 onAsrEvent,再调用 initialize。初始化只检查配置是否齐全并恢复本地状态;实际连接、上传及查询时,讯飞才校验凭证和服务权限。客户端直连会让凭证进入安装包,生产项目可将签名改由自己的服务端提供。
插件包目前按免费版本配置;讯飞云服务的额度、计费和并发限制由讯飞单独管理。
页面接入示例
下面演示普通 uni-app Vue 3 页面的生命周期和实时文字更新。合并到现有页面时保留原有生命周期逻辑,按钮调用放在用户主动操作中。完整界面、文件转写和保存导出见随附工程的 pages/index/index.vue。
<script>
// #ifdef APP-PLUS
import * as asr from '@/uni_modules/iflytek-asr'
import credentials from '@/config/credentials.local.js'
// #endif
export default {
data() {
return {
supported: false,
state: 'idle',
durationMs: 0,
volume: 0,
segments: [],
savedRecording: null,
error: ''
}
},
computed: {
transcriptText() {
return this.segments.map(item => item.text).join('\n')
}
},
onLoad() {
// #ifdef APP-PLUS
this.supported = uni.getSystemInfoSync().platform === 'android'
if (!this.supported) return
asr.onAsrEvent(this.receiveAsrEvent)
asr.initialize(credentials)
// #endif
},
onShow() {
// #ifdef APP-PLUS
if (this.supported) asr.getSnapshot()
// #endif
},
onUnload() {
// #ifdef APP-PLUS
if (this.supported) {
asr.dispose()
asr.offAsrEvent()
}
// #endif
},
methods: {
receiveAsrEvent(raw) {
const event = JSON.parse(raw)
if (event.type === 'state' || event.type === 'snapshot') {
this.state = event.state
this.durationMs = event.durationMs || 0
if (event.type === 'snapshot') this.segments = event.segments || []
} else if (event.type === 'meter') {
this.volume = event.volume || 0
this.durationMs = event.durationMs || 0
} else if (event.type === 'transcript') {
const removed = event.removedIds || []
this.segments = this.segments.filter(item => !removed.includes(item.id))
if (event.segment) {
const index = this.segments.findIndex(item => item.id === event.segment.id)
if (index >= 0) this.segments.splice(index, 1, event.segment)
else this.segments.push(event.segment)
this.segments.sort((a, b) => a.beginMs - b.beginMs)
}
} else if (event.type === 'saved') {
this.savedRecording = event.recording
} else if (event.type === 'error') {
this.error = event.message || '转写失败'
}
},
start() {
// #ifdef APP-PLUS
if (this.supported && ['idle', 'paused'].includes(this.state)) {
this.segments = []
this.savedRecording = null
this.error = ''
asr.startRecording()
}
// #endif
},
stop() {
// #ifdef APP-PLUS
if (this.supported) asr.stopRecording()
// #endif
},
resume() {
// #ifdef APP-PLUS
if (this.supported && this.state === 'paused') asr.resumeRecording()
// #endif
},
save() {
// #ifdef APP-PLUS
if (this.supported) asr.saveRecording()
// #endif
}
}
}
</script>
按钮分别绑定 start、stop、resume 和 save,文字绑定 transcriptText。所有命令异步执行,结果通过事件返回;startRecording() 等方法没有同步成功返回值。中间文字会修订,必须按 segment.id 替换并删除 removedIds,不要直接把每次事件文字追加到全文。
每次 startRecording() 建立新会话,resumeRecording() 才会继续旧会话。在已有页面上开始新会话时,清空该页面的旧 segments,以免把两场对话显示在一起。事件监听目前只保留一个,多个页面需统一管理。
常用接口
从 @/uni_modules/iflytek-asr 导入,仅在 Android App 端调用。类型声明见 utssdk/interface.uts。
| 接口 | 用途 |
|---|---|
initialize({ appId, apiKey, apiSecret }) |
配置凭证、恢复本地草稿与文件任务;不会自行录音 |
onAsrEvent(listener) / offAsrEvent() |
注册或移除持续监听,回调参数为 JSON 字符串 |
startRecording() |
开始新会话;录音中不能重复开启 |
stopRecording() |
停止采集并等待尾句,完成后进入 paused |
resumeRecording() |
在 paused 状态继续当前会话 |
saveRecording() |
保存当前录音和文字快照,成功返回 saved 事件 |
getSnapshot() |
返回当前状态、文字、缺口、保存历史和文件任务 |
chooseAudioFile() |
系统选择录音,复制到应用私有目录后返回 fileSelected |
transcribeFile(path) |
转写可读取的本地录音,同一时间运行一个文件任务 |
cancelFileTranscription() |
取消设备上的处理或查询 |
retryFileTranscription() |
恢复最近取消、失败或中断任务,沿用已保存订单 |
exportRecording(path) |
系统选择保存位置并导出本插件保存的 WAV/TXT |
dispose() |
停止录音、取消文件任务并移除监听;已落盘草稿保留 |
事件字段
type |
主要字段 |
|---|---|
state |
state:idle / starting / recording / stopping / paused;network:idle / connecting / online / reconnecting;durationMs、sessionId、saving、lastError |
snapshot |
状态字段及 segments、gaps、history、fileTask |
meter |
volume:0–1;durationMs:累计录音毫秒数 |
transcript |
segment:id / text / beginMs / endMs / final;removedIds:应移除的临时结果 ID |
gap |
gap.beginMs、gap.endMs:已知未送达的音频区间 |
saved |
recording.audioPaths、textPath、directory、durationMs、savedAt、segments、gaps |
fileSelected |
path、name、size |
fileTask |
task.phase、progress、partIndex、partCount、message、uploadUncertain;订单保存在 parts 中 |
fileResult |
segments、complete;全部完成时返回 textPath |
pickerCancelled / exported |
文件选择取消 / 导出完成 |
warning / error |
message;错误还提供 code |
音量是 PCM RMS 计算出的相对值,不表示识别准确率。文件任务阶段为 preparing / uploading / processing / complete / cancelled / interrupted / failed。
保存与文件导入
调用 saveRecording() 后,在 saved 事件取得路径。recording.audioPaths 包含按小时切片的 WAV,recording.textPath 指向 TXT。分别传给 exportRecording(path) 即可导出;导出会由系统文件选择器让用户选择目的位置。
录音中保存的文字可能包含未确定子句。需要收齐尾句时,先调用 stopRecording(),等到 paused 后再保存。
导入流程为:chooseAudioFile() → fileSelected.path → transcribeFile(path) → fileTask 进度 → fileResult 结果。
- WAV 支持普通 RIFF PCM 8/16/24/32bit 和 Float32,转为 16kHz 单声道 WAV。
- MP3、M4A、AAC、FLAC、OGG 等使用 Android 系统音轨解码器,实际支持取决于设备和文件编码。
- 裸 PCM 必须是 16kHz / 16bit / 单声道 / 小端格式。
- 长文件先流式转码并按小时切片,再顺序上传和查询。取消后调用
retryFileTranscription()继续已保存任务。 - 取消不会撤回讯飞已创建的订单,也不会返还已产生的服务费用。上传响应丢失时可能无法确认是否已建单,应先核对订单,避免重复上传。
长时间与后台使用
本地每小时 PCM 约 115.2MB(约 109.9MiB),保存快照和导入转码会另外占用空间。剩余空间少于 8MiB 时会停止录音。应用私有目录为 files/iflytek-asr,包含草稿、保存录音、导入源文件和文件任务。卸载或清除应用数据会删除这些文件,需保留的内容请先导出。
请从 App 前台、经用户主动操作开始录音。Android 前台服务通知提供停止按钮,WakeLock 用于录音期间维持 CPU 工作。后台运行仍受系统及设备厂商策略影响;本插件没有开机自启动或进程被结束后自动重启麦克风的逻辑。
断网期间本地音频继续保存,实时文字可能缺失;已发送但尚未确认的最后文字也可能不完整。保存后可用对应录音补转。长连接切换不承诺云端文字无缝衔接,识别准确率与运行流畅度需用实际设备、语音和网络验收。
隐私与权限
无广告。用户主动开始录音后采集麦克风音频,主动选择文件后读取对应录音。实时转写及文件转写会将音频发送至讯飞,用于识别;同时发送应用身份参数、请求时间、随机标识和请求签名,并查询云端订单。请求地址:
wss://office-api-ast-dx.iflyaisol.com/ast/communicate/v1
https://office-api-ist-dx.iflyaisol.com/v2/upload
https://office-api-ist-dx.iflyaisol.com/v2/getResult
本地保存录音、文字、会话草稿、用户选中的录音副本和任务状态,供恢复及导出。请在应用的隐私说明中披露上述用途,并在获得用户授权后启动采集或上传。讯飞云端的数据处理规则以其服务协议为准。
插件原生清单声明以下权限:
| 权限 | 用途 |
|---|---|
android.permission.INTERNET |
连接讯飞并上传、查询转写 |
android.permission.ACCESS_NETWORK_STATE |
网络状态相关权限声明 |
android.permission.RECORD_AUDIO |
麦克风录音,开始和继续时检查并按需申请 |
android.permission.FOREGROUND_SERVICE |
录音前台服务 |
android.permission.FOREGROUND_SERVICE_MICROPHONE |
麦克风类型前台服务 |
android.permission.WAKE_LOCK |
录音期间保持 CPU 工作 |
文件选择和导出使用 Android SAF,不申请全盘存储权限。清单声明不代表已获得用户授权。
验证与常见问题
已完成无界面 Vue 编译、页面事件 mock、UTS → Kotlin 转译、原生与桥接 Kotlin 编译,以及签名、文字修订、WAV、切片、解码和文件任务恢复的核心检查。2026-10-06 使用本地合成的短中文语音通过了真实实时转写和文件转写链路。
当前发布资料只承诺上述验证范围。最终 APK / 自定义基座、真实 Android 麦克风与后台行为、实际讲话准确率、设备解码器和超过 60 分钟的连续运行仍需真机验收;仓库已有 APK 不作为本次重新制作和验收的发布安装包。
- 提示尚未初始化或配置缺失: 核对三个凭证是否填写,并先注册事件、调用
initialize。 - 接口权限或额度错误: 确认两个大模型转写服务均已开通、凭证来自对应应用且额度有效。
Unresolved reference 'okhttp3'/'okio': 核对当前插件 Androidconfig.json的 Maven 依赖,重新制作自定义基座。- 插件接口不存在: 核对
uni_modules/iflytek-asr与实际安装的自定义基座是否匹配。 - 暂停后仍有少量文字更新: 停止后正在收取尾句;进入
paused后再继续或保存最终结果。 - 文字重复或尾句被覆盖: 按 ID 替换临时结果并处理
removedIds,不要逐事件累加字符串。
项目源码与问题记录:UTSIflytekASR Git 仓库。

收藏人数:
购买源码授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 0
赞赏 0
下载 12658913
赞赏 1955
赞赏
京公网安备:11010802035340号