首页/新闻资讯/正文详情

科大讯飞语音链路 Demo:从唤醒到播报的完整抽离

发布时间:2026/9/24 15:42:51 来源:云帆数科 栏目:资讯中心
科大讯飞语音链路 Demo:从唤醒到播报的完整抽离
在一个桌面类 Android 项目里语音助手经常会越写越“粘”首页要显示状态悬浮窗要同步状态业务指令要启动系统设置通话页面又要暂停麦克风。最后真正想复用的那条链路反而被 Launcher 业务包住了。一句话结论这次抽离的关键不是重写讯飞 SDK而是把“唤醒、实时转写、星火回复、TTS 播报”放进一个独立页面和独立状态机让 Demo 只表达语音链路本身。本文基于当前工程代码说明Android 配置为minSdk 30、targetSdk 37、compileSdk 37语音能力来自本地AIKit.aar和SparkChain.aar星火对话回复走讯飞 OpenAI 兼容 HTTP 接口。具体 SDK 返回码和授权策略可能随讯飞版本变化本文只解释当前项目里的实现边界。1. 大背景这篇文章解释新增的独立 Demo 页面如何工作以及它和原有语音助手服务的关系。本文会讲MainActivity如何串起完整语音链路。VoicePipelineDemoStateMachine为什么单独存在。sdk目录下的语音能力封装如何被复用。首页入口为什么先停止常驻语音服务。星火模型返回和 TTS 播报之间的数据关系。本文不展开讯飞 SDK 内部源码也不讨论默认桌面、天气、环信通话、悬浮窗等业务模块。它们属于产品壳层不是这次 Demo 的重点。2. 全局地图一条链路四个能力独立 Demo 的主流程可以看成四段播报完成继续对话用户说“小飞小飞”1. 离线唤醒WakeWordEngine2. 实时转写SpeechRecognizer3. 模型回复SparkHttpClient4. 语音播报SpeechSynthesizer唤醒词只负责开启第一轮对话。进入连续对话后TTS 每次播完都会直接回到实时转写只有用户退出对话或页面重新进入待命状态才需要再次说唤醒词。对应到文件职责代码位置职责MainActivity.kt独立 Demo 页面和语音链路编排VoicePipelineDemoStateMachine.ktDemo 专属 UI 状态不依赖 Android SDKsdk/WakeWordEngine.ktAIKit 离线唤醒“小飞小飞”命中后回调sdk/SpeechRecognizer.ktSparkChain 在线实时转写输出中间结果和最终结果llm/SparkHttpClient.kt调用星火 OpenAI 兼容 HTTP 接口拿到模型回复sdk/SpeechSynthesizer.ktAIKit Aisound 离线 TTS生成 PCM 并用AudioTrack播放这也是这次抽离的边界SDK 封装继续复用Launcher 业务不进入 Demo。3. 为什么先抽一个状态机页面上需要展示当前阶段、识别文本和模型回复。如果直接在 Activity 里到处改TextView很快会变成“某个回调里改一个标题另一个回调里忘了清空文本”的状态。所以新增了VoicePipelineDemoStateMachine。它不关心麦克风、Activity 生命周期也不调用 SDK只负责把语音链路推进成 UI 状态enumclassVoicePipelineDemoPhase{STOPPED,INSTALLING,INITIALIZING,STANDBY,AWAKENED,LISTENING,THINKING,SPEAKING,ERROR,}例如一轮用户提问会经历LISTENING - 正在实时转写保存 transcript THINKING - 带着 transcript 请求星火模型 SPEAKING - 保存 modelReply交给 TTS 播报 LISTENING - 播报完成后继续听下一轮这层状态机的好处是测试容易写。当前新增的 JVM 单测覆盖了两个关键行为识别文本会被带到星火思考和 TTS 播报阶段回到待唤醒时会清理上一轮文本。它测的是页面规则不需要启动 Android 设备也不需要真实麦克风。4. 独立工程只保留一条语音链路这次最终交付不是在robot工程里新增页面而是在同级目录voice-pipeline-demo下新建一个独立 Android 工程。它有自己的settings.gradle.kts、app/build.gradle.kts、AndroidManifest.xml、AAR、assets 和入口 Activity。这个拆法有两个好处。第一Demo 不再依赖 Launcher 的生命周期。它启动后只处理自己的权限、资源复制、SDK 初始化、唤醒监听、实时转写、星火请求和 TTS 播报。第二AIKit 和 SparkChain 这类进程级运行时只由 Demo 自己管理。页面不可见或销毁时MainActivity会释放唤醒、ASR、SparkChain、TTS 和 AIKit 资源避免麦克风或全局 SDK 实例残留。因此robot工程里的常驻VoiceAssistantService不再参与这个 Demo。原项目可以继续保持自己的桌面语音助手新工程负责提供最小可运行、便于讲解和二次迁移的语音链路样板。5. Demo 初始化资源、AIKit、SparkChainMainActivity.startDemo()做三件事。第一步释放 APK 里的离线资源valinstallerResourceInstaller(this)installer.installIfNeeded{progress-publish(stateMachine.installing(progress))}ResourceInstaller会把assets/iflytek/ivw和assets/iflytek/aisound复制到应用私有目录。唤醒和 TTS 都依赖这些本地模型。第二步初始化 AIKitvalnewRuntimeAIKitRuntime(applicationContext,installer.workDir)newRuntime.initialize{result-...}AIKit 负责离线唤醒和离线 TTS。密钥不写在仓库里而是从local.properties注入到BuildConfig。第三步初始化 SparkChainvalnewSparkRuntimeSparkChainRuntime(applicationContext)valsparkResultnewSparkRuntime.initialize()SparkChain 在当前项目里只用于中文大模型实时转写。真正的星火“模型回复”不是这个 AAR 里的 LLM 页面而是SparkHttpClient通过 HTTP 请求拿到。初始化完成后Activity 创建三个引擎wakeEngineWakeWordEngine(...)recognizerSpeechRecognizer(...)synthesizerSpeechSynthesizer(...)然后进入STANDBY等待“小飞小飞”。6. 唤醒从待命到第一句 TTS待唤醒阶段由WakeWordEngine.start()开启。它内部使用PcmRecorder持续采集16k/16bit/monoPCM然后通过 AIKit IVW 能力写入唤醒引擎。当 SDK 回调里出现func_wake_up或func_pre_wakeup时WakeWordEngine触发onWake()。Demo 页面收到后执行wakeEngine?.stop()conversationActivetruepublish(stateMachine.awakened(WakeWordEngine.WAKE_WORD))speak(我在请讲){startListening()}这里有一个重要细节唤醒命中后先停止唤醒录音再进入 TTS。否则同一个页面内也会出现唤醒录音和识别录音互相争用的问题。“我在请讲”播完以后才进入实时转写。7. 实时转写中间结果用于页面反馈最终结果用于请求星火SpeechRecognizer来自原项目对 SparkChain ASR 的封装。它做的事情很明确start()创建一轮 ASR 会话。PcmRecorder把麦克风 PCM 持续写给 SparkChain。onPartial返回中间识别文本用来实时刷新页面。onFinal返回最终文本用来进入星火请求。Demo 里的处理逻辑是privatefunhandlePartial(text:String){if(!conversationActive||text.isBlank())returnpublish(stateMachine.listening(text))}中间结果只更新 UI不触发模型请求。最终结果才进入handleRecognized()valtextrawText.trim()if(text.isBlank()){startListening()return}if(isDemoExit(text)){conversationActivefalsespeak(好的语音 Demo 已回到待唤醒){enterStandby()}return}askSpark(text)这样页面可以实时看到用户说了什么但不会因为每个中间字词都去请求模型。8. 星火回复HTTP 模型返回适合 TTS 的短文本当前项目里的星火对话客户端是SparkHttpClient。它调用的是https://spark-api-open.xf-yun.com/v1/chat/completions鉴权使用Authorization: Bearer APIPassword配置项是local.properties里的iflytek.sparkApiPassword。请求体由SparkHttpRequestBuilder构造默认非流式返回方便后续 TTS 一次性播报完整句子。Demo 里没有再经过IntentRouter因为这个页面不演示“打开设置”这类本地业务指令。它直接请求星火valclientSparkHttpClient()valsnapshotsynchronized(history){history.toList()}client.chat(text,snapshot).onSuccess{rememberTurn(text,it)}.getOrElse{星火大模型暂时没有返回成功结果请稍后再试}history只保留最近 8 条消息目的是让连续对话有一点上下文同时避免请求体无限增长。模型回复拿到后Activity 进入SPEAKING状态。如果没有配置iflytek.sparkApiPassword页面不会崩溃而是直接给出可播报的提示语。这是 Demo 很重要的体验边界配置缺失是可诊断状态不应该变成空白页面。9. TTS 播报合成结束不等于播放结束SpeechSynthesizer封装的是 AIKit Aisound 离线 TTS。它不是简单地“合成完就回调完成”而是做了两层处理。第一层SDK 回调里的 PCM 不直接在回调线程播放而是放入独立播放线程playbackExecutor.execute{writePcmOnPlaybackThread(speechContext,bytes)}这样可以避免AudioTrack.write()阻塞 SDK 回调线程。第二层SDK 通知合成结束后还要等AudioTrack队列里的 PCM 真正播完delayMsremainingPlaybackMsLocked(track)PLAYBACK_DRAIN_MARGIN_MS main.postDelayed(runnable,delayMs)这个细节很容易被忽略。对用户来说“播报完成”是耳朵听完不是 SDK 不再产出 PCM。如果提前释放AudioTrack短句尾部可能被吞掉。Demo 页面调用 TTS 的方式很轻privatefunspeak(text:String,onComplete:()-Unit){publish(stateMachine.speaking(text))synthesizer?.speak(text){main.post(onComplete)}?.onFailure{handleEngineError(it.message.orEmpty())}}星火回复播完后onComplete继续调用startListening()形成连续对话。10. 易混点SparkChain ASR 和星火 HTTP 不是同一层这次抽离里最容易混的概念是“SparkChain”和“星火模型返回”。在当前项目中名称当前用途输入输出SparkChainRuntime初始化 SparkChain SDKAppId、ApiKey、ApiSecret可用的 ASR 运行时SpeechRecognizer实时转写麦克风 PCM中文识别文本SparkHttpClient星火对话回复用户文本和历史消息适合播报的回复文本也就是说SparkChain AAR 负责“把声音变成文字”SparkHttpClient负责“把文字交给大模型生成回复”。这两个步骤都和讯飞有关但在代码里是两条不同通道。把这层边界说清楚以后Demo 的结构就很直观ASR 返回transcriptHTTP 返回modelReplyTTS 播放modelReply。11. 实践意义这个 Demo 的价值不只是多了一个页面而是把语音能力变成了可以单独验证、单独讲解、单独演示的模块。对开发来说它减少了排查成本。语音链路出问题时可以先进入 Demo 页面确认是资源复制失败、AIKit 鉴权失败、SparkChain ASR 没结果、星火 HTTP 没配置还是 TTS 播放异常。对产品来说它把 Launcher 的业务判断拿掉了。Demo 不会打开设置不会显示天气不会处理环信通话只回答一个问题从唤醒到播报这条链路是否跑通。对后续重构来说它也给了一个清晰方向底层 SDK 封装可以被单独搬迁到新工程业务态服务和演示态页面各自维护自己的状态机。共享能力不共享页面状态。12. 总结回到最开始的问题如何把“语音唤醒 实时转写 星火模型返回 TTS 播报”从桌面助手里抽出来答案不是复制一份大服务而是分三层底层能力复用WakeWordEngine、SpeechRecognizer、SparkHttpClient、SpeechSynthesizer。Demo 编排独立MainActivity只管理语音链路。UI 状态独立VoicePipelineDemoStateMachine只描述 Demo 页面该显示什么。这次抽离的核心是让 Demo 只保留语音链路本身唤醒负责开始ASR 负责听懂星火负责回答TTS 负责说出来。项目源代码地址

相关推荐

2026年杭州亨得利腕表质保政策与官方维保受理门店地址、电话(官方更新版)
2026年杭州亨得利腕表质保政策与官方维保受理门店地址、电话(官方更新版)

随着腕表维保需求持续增长,广大表主对于腕表质保权益、官方维保受理渠道的关注度不断提升,2026年亨得利更新杭州区域腕表质保相关政策,同步公示杭州亨得利服务中心受理门店地址、联络电话,帮助杭州及周边地区表主清晰了解自身腕表… · 2026/9/24 15:42:51

CAN总线调试工具选型指南:CANTest、ZCANPro、USB-CAN Tool对比
CAN总线调试工具选型指南:CANTest、ZCANPro、USB-CAN Tool对比

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 15:42:50

STM32CubeIDE与ST-LINK下载程序全攻略:从接线到固件更新排查
STM32CubeIDE与ST-LINK下载程序全攻略:从接线到固件更新排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 15:42:44

Swift-OPA 实践指南:在 Swift 应用中原生评估 OPA IR 策略计划
Swift-OPA 实践指南:在 Swift 应用中原生评估 OPA IR 策略计划

后端认证鉴权云原生 【免费下载链接】opa Open Policy Agent (OPA) is an open source, general-purpose policy engine. 项目地址: https://gitcode.com/gh_mirrors/op/opa 点击查看 免费下载 Swift-OPA 是由 Apple 发起并托管于 OPA 官方组织的 Swift 软件包&… · 2026/9/24 16:10:08

在 Accelerate 中使用 torch.compile 与区域编译(Regional Compilation):从配置到冷启动优化实战
在 Accelerate 中使用 torch.compile 与区域编译(Regional Compilation):从配置到冷启动优化实战

人工智能深度学习分布式训练 【免费下载链接】accelerate 🚀 A simple way to launch, train, and use PyTorch models on almost any device and distributed configuration, automatic mixed precision (including fp8), and easy-to-configure FSDP and DeepSpee… · 2026/9/24 16:10:08

SDR++ 完整指南:免费软件定义无线电,5 分钟听清 FM 与航空波段
SDR++ 完整指南:免费软件定义无线电,5 分钟听清 FM 与航空波段

SDR 完整指南:免费软件定义无线电,5 分钟听清 FM 与航空波段 【免费下载链接】SDRPlusPlus Cross-Platform SDR Software 项目地址: https://gitcode.com/GitHub_Trending/sd/SDRPlusPlus SDR 是一款免费、开源、跨平台的软件定义无线电&#xff… · 2026/9/24 16:10:08

【Coze】【视频】天涯实事工作流
【Coze】【视频】天涯实事工作流

今天给大家演示一个 天涯实事 Coze 视频工作流,它将大模型的新闻锐评、音频合成、字幕生成与图片排版有机结合,最终实现短视频的自动化生成。通过这个流程,可以把复杂的时事热点快速转化为带有声音、画面和字幕的完整视频,让用户直观地感受到新闻解读与创意表达的结合。 文… · 2026/9/24 16:10:01

【Coze】【视频】情感类国风水墨风格视频片段工作流
【Coze】【视频】情感类国风水墨风格视频片段工作流

今天给大家演示一个 情感类国风水墨风格视频片段工作流。该工作流能够将文本转化为极具东方意境的视频作品,结合大模型生成标题与画面提示词,配合图像生成、语音合成与剪映插件实现自动化排版与音画结合,最终形成带有国风水墨质感的视频成品。通过这一流程,用户只需提供简单… · 2026/9/24 16:10:01

下载卡在 99%、Peer 只剩个位数?trackerslist 公共 BitTorrent Tracker 列表 3 步配置完整避坑指南
下载卡在 99%、Peer 只剩个位数?trackerslist 公共 BitTorrent Tracker 列表 3 步配置完整避坑指南

下载卡在 99%、Peer 只剩个位数?trackerslist 公共 BitTorrent Tracker 列表 3 步配置完整避坑指南 【免费下载链接】trackerslist Updated list of public BitTorrent trackers 项目地址: https://gitcode.com/GitHub_Trending/tr/trackerslist 种子卡在 99… · 2026/9/24 16:09:55

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13

1D-CNN时间序列建模实战:从Conv1d原理到工业落地
1D-CNN时间序列建模实战:从Conv1d原理到工业落地

简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26

柔软的L:汉语语流中被忽视的舌肌张力控制
柔软的L:汉语语流中被忽视的舌肌张力控制

1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码