在这里插入图片描述

每日一句正能量

不要看见别人发光就觉得自己黯淡。
世界是多元的舞台,别人发光不代表你的光就不存在。每个人的时区、赛道、光源都不同。你只需发出自己的光,无论强弱。

摘要

摘要: 语音交互正在重塑人机交互范式。HarmonyOS 6.1 提供了完整的智慧语音服务框架,支持应用内语音唤醒、连续对话、语义理解和语音合成。本文从架构原理到代码实现,完整演示如何在应用中集成语音唤醒、指令识别、意图分发和 TTS 反馈的闭环系统,帮助开发者构建"听得懂、说得出"的智能应用体验。


一、智慧语音服务架构概述

1.1 为什么应用需要语音交互?

在以下场景中,语音交互相比触屏操作具有不可替代的优势:

场景 痛点 语音方案
驾驶导航 双手握方向盘,无法触屏 “导航到最近的加油站”
厨房烹饪 双手沾水/油污 “定时 30 分钟”
运动健身 手机装在臂包中 “下一首歌”
长辈用户 界面复杂、字体小 “给张三打电话”
视觉障碍 无法看见屏幕内容 全程语音操作

HarmonyOS 6.1 的智慧语音服务(Intelligent Voice Service)将语音能力下沉为系统级服务,开发者无需自建 ASR/NLP/TTS 模型,即可快速接入专业级的语音交互能力。

1.2 框架四层架构

在这里插入图片描述

上图展示了 HarmonyOS 6.1 智慧语音服务的完整交互流程。从用户说话到应用响应,经过唤醒、识别、理解、执行、反馈五个阶段,全程在 500ms-2s 内完成。

智慧语音服务框架可分为四层:

层级 模块 职责
信号层 Audio Capturer、VAD 音频采集、语音端点检测、降噪
识别层 ASR Engine 语音转文字、方言适配、个性化热词
理解层 NLP Engine、Intent Parser 语义理解、实体抽取、意图分类
交互层 TTS Engine、Voice Synthesizer 语音合成、情感音色、语速控制

二、语音唤醒:打开交互之门

2.1 唤醒方式对比

HarmonyOS 6.1 支持两种唤醒模式:

唤醒方式 触发条件 功耗 适用场景
关键词唤醒 说出预设唤醒词(如"小艺小艺") 低(芯片级 DSP) 常驻后台、随时待命
按钮唤醒 点击界面麦克风按钮 无额外功耗 应用内主动发起

2.2 应用内按钮唤醒实战

在应用内集成语音唤醒按钮是最轻量的接入方式:

// components/VoiceButton.ets
import { speechRecognizer } from '@kit.SpeechKit';

@Component
export struct VoiceButton {
  @State isListening: boolean = false;
  @State rippleScale: number = 1.0;
  private recognizer: speechRecognizer.SpeechRecognizer | null = null;

  aboutToAppear(): void {
    // 初始化语音识别器
    this.recognizer = speechRecognizer.createEngine({
      language: 'zh-CN',
      online: true,           // 使用在线识别,精度更高
      partialResults: true    // 返回中间结果,提升响应速度
    });
  }

  private async startListening(): Promise<void> {
    if (!this.recognizer) return;

    this.isListening = true;
    this.startRippleAnimation();

    try {
      const result = await this.recognizer.startListening({
        // 最长录音 60 秒
        maxDuration: 60000,
        // 静音超时 3 秒自动停止
        silenceTimeout: 3000
      });

      // 识别结果通过回调返回
      this.recognizer.on('result', (event) => {
        if (event.isLast) {
          this.handleVoiceResult(event.result);
        } else {
          // 中间结果可用于实时展示
          this.showInterimResult(event.result);
        }
      });

      this.recognizer.on('error', (err) => {
        console.error('[Voice] 识别错误:', err.message);
        this.isListening = false;
      });

    } catch (err) {
      console.error('[Voice] 启动识别失败:', err);
      this.isListening = false;
    }
  }

  private stopListening(): void {
    this.recognizer?.stopListening();
    this.isListening = false;
  }

  private handleVoiceResult(text: string): void {
    console.info(`[Voice] 识别结果: ${text}`);
    this.isListening = false;
    // 将识别文本交给指令分发器
    CommandDispatcher.dispatch(text);
  }

  private showInterimResult(text: string): void {
    // 更新 UI 展示中间识别结果
    AppStorage.setOrCreate('voiceInterimText', text);
  }

  private startRippleAnimation(): void {
    // 录音时的波纹动画
    const timer = setInterval(() => {
      if (!this.isListening) {
        clearInterval(timer);
        return;
      }
      animateTo({ duration: 800, curve: Curve.EaseInOut }, () => {
        this.rippleScale = this.rippleScale === 1.0 ? 1.3 : 1.0;
      });
    }, 800);
  }

  build() {
    Stack() {
      // 外圈波纹
      if (this.isListening) {
        Circle()
          .width(80)
          .height(80)
          .fill('rgba(52,152,219,0.2)')
          .scale({ x: this.rippleScale, y: this.rippleScale })
          .animation({ duration: 800, curve: Curve.EaseInOut });
      }

      // 麦克风按钮
      Button() {
        Image(this.isListening ? $r('app.media.ic_stop') : $r('app.media.ic_mic'))
          .width(28)
          .height(28)
          .fillColor(this.isListening ? '#e74c3c' : '#3498db');
      }
      .width(56)
      .height(56)
      .backgroundColor(this.isListening ? '#fdf2f2' : '#f0f3ff')
      .borderRadius(28)
      .onClick(() => {
        if (this.isListening) {
          this.stopListening();
        } else {
          this.startListening();
        }
      });
    }
    .width(100)
    .height(100);
  }
}

2.3 系统级关键词唤醒

如果应用需要在后台响应语音指令,可申请接入系统级关键词唤醒:

// 在 EntryAbility 中注册唤醒词
import { wakeWord } from '@kit.SpeechKit';

onCreate(): void {
  // 注册应用自定义唤醒词
  wakeWord.register({
    wakeWords: ['打开外卖', '查订单'],
    callback: (word: string) => {
      console.info(`[WakeWord] 唤醒词命中: ${word}`);
      // 唤起应用到前台
      this.context.startAbility({
        bundleName: 'com.example.food',
        abilityName: 'EntryAbility'
      });
      
      // 根据唤醒词执行对应操作
      if (word === '查订单') {
        CommandDispatcher.dispatch('查看最近订单');
      }
    }
  });
}

三、语义理解与指令分发

3.1 为什么需要指令分发?

语音识别的结果是纯文本(如"帮我查一下昨天的外卖订单"),应用需要理解这句话的意图并执行对应操作。这个过程就是语义理解 + 指令分发

3.2 指令分发器实现

// services/CommandDispatcher.ets
import { nlp } from '@kit.IntelligenceKit';
import { router } from '@kit.ArkUI';

export class CommandDispatcher {
  // 指令路由表
  private static readonly ROUTE_TABLE: RouteItem[] = [
    {
      pattern: /查(看|一下)?.*订单/,
      intent: 'query_order',
      handler: (entities) => CommandHandlers.handleQueryOrder(entities)
    },
    {
      pattern: /(打开|进入).*首页/,
      intent: 'navigate_home',
      handler: () => CommandHandlers.handleNavigateHome()
    },
    {
      pattern: /(搜索|找|查).*商家/,
      intent: 'search_shop',
      handler: (entities) => CommandHandlers.handleSearchShop(entities)
    },
    {
      pattern: /(联系|呼叫).*骑手/,
      intent: 'contact_rider',
      handler: (entities) => CommandHandlers.handleContactRider(entities)
    },
    {
      pattern: /(播放|放).*音乐/,
      intent: 'play_music',
      handler: (entities) => CommandHandlers.handlePlayMusic(entities)
    },
    {
      pattern: /(定时|倒计时).*分钟/,
      intent: 'set_timer',
      handler: (entities) => CommandHandlers.handleSetTimer(entities)
    }
  ];

  static async dispatch(text: string): Promise<void> {
    console.info(`[Dispatcher] 接收指令: ${text}`);

    // 方式一:使用系统 NLP 服务进行意图识别(推荐)
    try {
      const nlpResult = await nlp.understandIntent({
        text: text,
        domain: 'food_delivery'  // 指定领域,提升识别精度
      });

      if (nlpResult.intent && nlpResult.confidence > 0.7) {
        await this.executeByNlpResult(nlpResult);
        return;
      }
    } catch (err) {
      console.warn('[Dispatcher] NLP 服务调用失败,降级到本地规则:', err);
    }

    // 方式二:本地正则规则匹配(兜底方案)
    await this.executeByLocalRule(text);
  }

  private static async executeByNlpResult(result: nlp.IntentResult): Promise<void> {
    const { intent, entities } = result;
    
    switch (intent) {
      case 'query_order':
        await CommandHandlers.handleQueryOrder(entities);
        break;
      case 'search_shop':
        await CommandHandlers.handleSearchShop(entities);
        break;
      case 'contact_rider':
        await CommandHandlers.handleContactRider(entities);
        break;
      default:
        await this.fallbackResponse(`收到指令: ${intent},正在为您处理`);
    }
  }

  private static async executeByLocalRule(text: string): Promise<void> {
    for (const route of this.ROUTE_TABLE) {
      const match = text.match(route.pattern);
      if (match) {
        console.info(`[Dispatcher] 本地规则命中: ${route.intent}`);
        await route.handler(match);
        return;
      }
    }

    // 未匹配到任何指令
    await this.fallbackResponse('抱歉,我没听懂,您可以试试说"查订单"或"联系骑手"');
  }

  private static async fallbackResponse(text: string): Promise<void> {
    // 播放 TTS 反馈
    await TTSHelper.speak(text);
    
    // 同时展示文字提示
    promptAction.showToast({ message: text, duration: 3000 });
  }
}

interface RouteItem {
  pattern: RegExp;
  intent: string;
  handler: (match: RegExpMatchArray) => Promise<void>;
}

3.3 指令处理器实现

// services/CommandHandlers.ets
import { router } from '@kit.ArkUI';
import { call } from '@kit.TelephonyKit';

export class CommandHandlers {
  // 查询订单
  static async handleQueryOrder(entities?: Record<string, string>): Promise<void> {
    const orderId = entities?.['orderId'];
    const timeRange = entities?.['timeRange'];  // 如 "昨天"

    if (orderId) {
      router.pushUrl({ url: 'pages/OrderDetail', params: { orderId } });
      await TTSHelper.speak(`正在为您打开订单 ${orderId}`);
    } else if (timeRange === '昨天') {
      router.pushUrl({ url: 'pages/OrderList', params: { filter: 'yesterday' } });
      await TTSHelper.speak('正在为您查看昨天的订单');
    } else {
      router.pushUrl({ url: 'pages/OrderList', params: { filter: 'latest' } });
      await TTSHelper.speak('正在为您查看最近的订单');
    }
  }

  // 导航到首页
  static async handleNavigateHome(): Promise<void> {
    router.clear();
    router.pushUrl({ url: 'pages/Index' });
    await TTSHelper.speak('已回到首页');
  }

  // 搜索商家
  static async handleSearchShop(entities?: Record<string, string>): Promise<void> {
    const keyword = entities?.['shopName'] ?? entities?.['keyword'];
    router.pushUrl({ url: 'pages/SearchResult', params: { keyword } });
    await TTSHelper.speak(`正在为您搜索 ${keyword}`);
  }

  // 联系骑手
  static async handleContactRider(entities?: Record<string, string>): Promise<void> {
    const orderId = entities?.['orderId'];
    const riderPhone = await OrderService.getRiderPhone(orderId);
    
    if (riderPhone) {
      call.makeCall(riderPhone);
      await TTSHelper.speak('正在为您呼叫骑手');
    } else {
      await TTSHelper.speak('抱歉,未找到骑手联系方式');
    }
  }

  // 播放音乐
  static async handlePlayMusic(entities?: Record<string, string>): Promise<void> {
    const songName = entities?.['songName'];
    // 调用音乐播放服务
    await MusicService.play(songName);
    await TTSHelper.speak(songName ? `正在播放 ${songName}` : '正在为您播放音乐');
  }

  // 设置定时器
  static async handleSetTimer(entities?: Record<string, string>): Promise<void> {
    const minutes = parseInt(entities?.['duration'] ?? '0');
    if (minutes > 0) {
      TimerService.start(minutes * 60 * 1000);
      await TTSHelper.speak(`已设置 ${minutes} 分钟定时器`);
    }
  }
}

四、TTS 语音反馈:让应用会说话

4.1 TTS 基础接入

// services/TTSHelper.ets
import { textToSpeech } from '@kit.SpeechKit';

export class TTSHelper {
  private static engine: textToSpeech.TextToSpeechEngine | null = null;
  private static isSpeaking: boolean = false;

  // 初始化 TTS 引擎
  static async init(): Promise<void> {
    this.engine = await textToSpeech.createEngine({
      language: 'zh-CN',
      person: textToSpeech.VoicePerson.MALE,  // 男声
      speed: 1.0,   // 语速:正常
      volume: 1.0,  // 音量:最大
      pitch: 1.0    // 音调:正常
    });

    this.engine.on('finish', () => {
      this.isSpeaking = false;
    });
  }

  // 播放语音
  static async speak(text: string): Promise<void> {
    if (!this.engine) {
      await this.init();
    }

    if (this.isSpeaking) {
      // 如果正在播放,先停止
      await this.engine?.stop();
    }

    this.isSpeaking = true;
    
    try {
      await this.engine?.speak(text);
    } catch (err) {
      console.error('[TTS] 播放失败:', err);
      this.isSpeaking = false;
    }
  }

  // 停止播放
  static async stop(): Promise<void> {
    await this.engine?.stop();
    this.isSpeaking = false;
  }

  // 设置语速(0.5 - 2.0)
  static setSpeed(speed: number): void {
    this.engine?.setSpeed(Math.max(0.5, Math.min(2.0, speed)));
  }
}

4.2 场景化语音反馈设计

不同场景下,TTS 反馈的内容和语气应有所区别:

// services/VoiceFeedback.ets
export class VoiceFeedback {
  // 成功反馈
  static async success(action: string): Promise<void> {
    const templates = [
      `${action}成功`,
      `已为您${action}`,
      `${action}完成,还有什么可以帮您?`
    ];
    const text = templates[Math.floor(Math.random() * templates.length)];
    await TTSHelper.speak(text);
  }

  // 失败反馈
  static async error(reason: string): Promise<void> {
    const templates = [
      `抱歉,${reason},请稍后再试`,
      `操作失败了,${reason}`,
      `遇到问题:${reason},您可以尝试其他方式`
    ];
    const text = templates[Math.floor(Math.random() * templates.length)];
    await TTSHelper.speak(text);
  }

  // 确认反馈
  static async confirm(question: string): Promise<void> {
    await TTSHelper.speak(`${question},请说"确认"或"取消"`);
  }

  // 引导反馈
  static async guide(tip: string): Promise<void> {
    await TTSHelper.speak(`您可以试着说:${tip}`);
  }

  // 进度反馈
  static async progress(percent: number): Promise<void> {
    if (percent % 25 === 0) {
      await TTSHelper.speak(`已完成 ${percent}%`);
    }
  }
}

五、完整实战:语音点餐助手

在这里插入图片描述

以下是一个完整的语音点餐助手实现,整合了前文所有模块:

// pages/VoiceOrder.ets
import { speechRecognizer } from '@kit.SpeechKit';
import { CommandDispatcher } from '../services/CommandDispatcher';
import { TTSHelper } from '../services/TTSHelper';
import { VoiceFeedback } from '../services/VoiceFeedback';

@Entry
@Component
struct VoiceOrder {
  @State voiceText: string = '点击麦克风开始说话...';
  @State isListening: boolean = false;
  @State commandHistory: string[] = [];
  private recognizer: speechRecognizer.SpeechRecognizer | null = null;

  aboutToAppear(): void {
    TTSHelper.init();
    this.initRecognizer();
  }

  private initRecognizer(): void {
    this.recognizer = speechRecognizer.createEngine({
      language: 'zh-CN',
      online: true,
      partialResults: true
    });

    this.recognizer.on('result', (event) => {
      this.voiceText = event.result;
      if (event.isLast) {
        this.isListening = false;
        this.commandHistory.unshift(event.result);
        CommandDispatcher.dispatch(event.result);
      }
    });

    this.recognizer.on('error', (err) => {
      this.isListening = false;
      this.voiceText = '识别失败,请重试';
      VoiceFeedback.error('语音识别出错');
    });
  }

  private toggleListening(): void {
    if (this.isListening) {
      this.recognizer?.stopListening();
      this.isListening = false;
    } else {
      this.voiceText = '正在聆听...';
      this.isListening = true;
      this.recognizer?.startListening({ maxDuration: 30000, silenceTimeout: 2000 });
    }
  }

  build() {
    Column({ space: 20 }) {
      // 顶部标题
      Text('语音点餐助手')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1a1a2e')
        .margin({ top: 40 });

      Text('说"查订单"、"联系骑手"、"搜索肯德基"试试')
        .fontSize(13)
        .fontColor('#888');

      // 语音波形动画
      VoiceWaveAnimation({ isActive: this.isListening });

      // 识别文字展示
      Text(this.voiceText)
        .fontSize(18)
        .fontColor(this.isListening ? '#3498db' : '#333')
        .textAlign(TextAlign.Center)
        .padding(20)
        .backgroundColor('#f8f9fa')
        .borderRadius(12)
        .width('90%')
        .animation({ duration: 300 });

      // 麦克风按钮
      VoiceButton({
        isListening: this.isListening,
        onToggle: () => this.toggleListening()
      });

      // 指令历史
      if (this.commandHistory.length > 0) {
        Column({ space: 8 }) {
          Text('最近指令')
            .fontSize(14)
            .fontWeight(FontWeight.Bold)
            .fontColor('#555')
            .width('90%');

          ForEach(this.commandHistory.slice(0, 5), (cmd: string, index: number) => {
            Row() {
              Text(`"${cmd}"`)
                .fontSize(13)
                .fontColor('#666')
                .layoutWeight(1);
            }
            .width('90%')
            .padding(10)
            .backgroundColor('#f0f0f0')
            .borderRadius(8);
          });
        }
        .margin({ top: 20 });
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#ffffff');
  }
}

六、唤醒效果与交互界面

在这里插入图片描述

上图展示了语音唤醒时的交互界面。包括:麦克风波纹动画、实时文字转写、指令历史记录。整个界面简洁明了,弱化了视觉元素,强化了语音交互的主体地位。


七、性能优化与最佳实践

7.1 性能优化

优化项 方案
识别延迟 开启 partialResults,边录边识别,缩短首字响应时间
TTS 预加载 常用话术提前合成缓存,避免重复调用引擎
音频焦点 录音前请求音频焦点,避免与音乐播放冲突
降噪处理 启用 VAD + AEC,过滤环境噪音和回声

7.2 体验设计原则

原则 说明
一听就懂 TTS 反馈避免技术术语,使用自然口语
一呼就应 语音唤醒响应时间 < 500ms,超时需有提示
一错就帮 识别失败时,给出示例指令引导用户
一多就简 搜索结果过多时,语音播报前 3 条,其余展示在屏幕

7.3 隐私与权限

// module.json5 权限声明
{
  "module": {
    "requestPermissions": [
      { "name": "ohos.permission.MICROPHONE" },
      { "name": "ohos.permission.RECORD_AUDIO" },
      { "name": "ohos.permission.INTERNET" }
    ]
  }
}

八、总结

本文从唤醒、识别、理解、执行到反馈,完整演示了 HarmonyOS 6.1 智慧语音服务的接入全链路:

  1. 语音唤醒:通过按钮或关键词唤醒语音识别引擎;
  2. 语音识别:使用 SpeechKit 将语音转为文字,支持中间结果返回;
  3. 语义理解:通过系统 NLP 服务或本地规则匹配,解析用户意图;
  4. 指令分发:基于路由表将意图映射到具体业务处理函数;
  5. TTS 反馈:使用 TextToSpeech 引擎将执行结果播报给用户。

开发者行动清单:

  • module.json5 中申请麦克风与录音权限;
  • 封装 SpeechRecognizer 管理器,统一处理识别生命周期;
  • 设计指令路由表,覆盖应用的核心高频操作;
  • 接入系统 NLP 服务提升意图识别精度,本地规则兜底;
  • 初始化 TTS 引擎,设计场景化的语音反馈话术;
  • 实现录音时的波纹动画与中间结果实时展示;
  • 处理音频焦点冲突,避免与系统音乐/通话互相干扰;
  • 为语音交互界面提供文字兜底,满足多模态使用需求。

语音交互不是对触屏的替代,而是对触屏的补充。在双手被占用、视力受限或追求效率的场景中,语音是最自然、最高效的交互方式。HarmonyOS 6.1 的智慧语音服务让这一切变得触手可及。


转载自:https://blog.csdn.net/u014727709/article/details/162938629
欢迎 👍点赞✍评论⭐收藏,欢迎指正

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐