草庐IT

华为智慧屏分布式语音音乐软件,可见即可说

龙眼lychee 2023-08-16 原文

[本文正在参加星光计划3.0-夏日挑战赛]

在HarmonyOS官方文档中,有这样一项功能是只能在智慧屏上使用的,那就是可见即可说。恰好在很久之前参照官方分布式音乐播放器定制了一款自己的播放器,今天将其改造成智慧屏应用,并添加可见即可说功能。待真机演示,有设备的小伙伴可以测试一下!

0. 效果演示

  • 各设备
  • 手机平板,智慧屏


  • 真机待测…(to do 7月7号)

1. 可见即可说功能

 按照官方文档的介绍,可见即可说就是将一些热词与Component关联,达到监听语音热词,来执行一些相应操作。例如,浏览图片的时候,说出图片的名字或者角标序号,从而实现打开图片的效果。
 那么相应的,我们就能将分布式音乐播放器改造成语音控制的,比如将"播放",“暂停”,"上一首"等热词绑定到对应组件上,监测到热词的时候执行功能即可。

2. 可见即可说开发

2.1 热词注册

  • 创建Component.VoiceEvent对象,设置相应的热词,英文和中文都是可以的。

可见即可说的功能的核心就是,Component.VoiceEvent对象,一个对象对应一个事件。

//  比如说设置一个播放事件
Component.VoiceEvent eventplay = new Component.VoiceEvent("播放");
  • 一个Component.VoiceEvent对象,可以绑定多个热词
eventplay.addSynonyms("play");
  • 绑定完热词后,哪个组件需要这个语音事件,哪个组件就需要进行注册。
//比如分布式音乐播放器里面的播放按钮,对该语音事件进行注册。
musicPlayButton.subscribeVoiceEvents(eventplay);
  • 如果组件有多个语音事件要响应,我们就的创建多个Component.VoiceEvent对象,并且都进行注册。一个对象对应一个事件。

2.2 事件开发

 在前面,我们设置了语音事件,并且将一个播放按钮对其进行了注册。但也仅仅是注册,然后呢?然后就没然后了,因为我们还没有进行事件开发,按钮要在事件发生时做出响应。

2.2.1 实现SpeechEventListener接口

private Component.SpeechEventListener speechEventListener = new Component.SpeechEventListener(){
    @Override
    public boolean onSpeechEvent(Component v, SpeechEvent event) {
        if (event.getActionProperty().equals("播放")) {
            ... // 检测注册的热词,进行相应的处理
             playOrpause();
        }
        return false;
  };
}

2.2.2 通过setSpeechEventListener方法实现回调注册

musicplayButton.setSpeechEventListener(speechEventListener);

至此,我们对可见即可说的功能已经了解了,那么下面是对分布式音乐播放器案例的改造,感兴趣的读者往下看。

3. 案例编写

3.1 工程结构

3.2 UI设计

3.3 架构简析

这里简单剖析一下架构,详情见附件工程文件。

  • PlayerManager.java
      封装好的播放器类,设置音乐路径,播放暂停,上一首下一首的功能
   /**
     *  首先播放之前要准备好媒体资源
     */
 public void prepareMusic(){...}
 /**
     *  准备好音频路径 准备媒体资源
     * @param Uri
     */
    public void setResource(String Uri){...}
/**
     *  播放
     */
    public void play(){...}
/**
     * 暂停
     */
    public void pause(){...}
/**
     * 定时事件通知更新进度条
     * DELAY_TIME 延迟1s
     * PERIOD 两个事件间隔1s
     */
    private void startTimetask(){...}
//.....
  • StateListener
      播放器状态监听接口,监听播放器状态进而进行一些事件通知
package com.yzj.musicplayer.Player;

public interface StateListener {
    void onPlaySuccess(int totalTime);

    void onPauseSuccess();

    void onPositionChange(int currentTime);

    void onMusicFinished();

    void onUriSet(String name);
}

  • CommonProvider,ViewProvidor

 用来生成dialog,显示可分布式流转的设备列表, 对此不赘述,用JAVA做UI体验不是很好

  • MainAbilitySlice

主页面

3.4 绑定可见即可说事件

这里我们有播放,暂停,上一首,下一首,拖动进度条,分布式流转等操作。
我们逐一为其添加语音事件。

//测试
    //播放
    private Component.VoiceEvent eventplay;
    //暂停
    private Component.VoiceEvent eventpause;
    //下一首
    private Component.VoiceEvent eventnext;
    //前一首
    private Component.VoiceEvent eventpre;
    //流转
    private Component.VoiceEvent eventremote;
   //流转的语音相应事件
    private Component.SpeechEventListener speech_mShowDeviceListListener = new Component.SpeechEventListener() {
        @Override
        public boolean onSpeechEvent(Component component, SpeechEvent speechEvent) {
            if(speechEvent.getActionProperty().equals("流转")){
                // 显示选择设备列表
                continuationRegisterManager.showDeviceList(abilityToken, null, null);
            }
            return false;
        }
    };

  void initview(){
          //绑定热词
        eventplay = new Component.VoiceEvent("播放");
        eventpause = new Component.VoiceEvent("暂停");
        eventnext = new Component.VoiceEvent("下一首");
        eventpre = new Component.VoiceEvent("上一首");
        eventremote = new Component.VoiceEvent("流转");
         //播放按钮注册热词
        musicPlayButton.subscribeVoiceEvents(eventplay);
        musicPlayButton.subscribeVoiceEvents(eventpause);
        //播放按钮设置响应事件
        musicPlayButton.setSpeechEventListener(new Component.SpeechEventListener() {
            @Override
            public boolean onSpeechEvent(Component component, SpeechEvent speechEvent) {
                if(speechEvent.getActionProperty().equals("播放")){
                    if(playerManager.isPlaying()){
                        Log.info(TAG,"正在播放");
                    }
                    else{
                        playOrPause();
                    }
                    return true;

                }
                else if(speechEvent.getActionProperty().equals("暂停")){
                    if(!playerManager.isPlaying()){
                        Log.info(TAG,"已经暂停了");

                    }
                    else{
                        playOrPause();
                    }
                    return true;
                }
                return false;
            };
        });
         //下一首注册热词
        playnextButton.subscribeVoiceEvents(eventnext);
        //下一首设置响应事件
        playnextButton.setSpeechEventListener(new Component.SpeechEventListener() {
            @Override
            public boolean onSpeechEvent(Component component, SpeechEvent speechEvent) {
                if(speechEvent.getActionProperty().equals("下一首")){
                    nextMusic(component);
                    return true;
                }
                return false;
            }
        });
        //上一首注册热词
        playpreButton.subscribeVoiceEvents(eventpre);
       //上一首设置响应事件
        playpreButton.setSpeechEventListener(new Component.SpeechEventListener() {
            @Override
            public boolean onSpeechEvent(Component component, SpeechEvent speechEvent) {
                if(speechEvent.getActionProperty().equals("上一首")){
                    prevMusic(component);
                    return  true;
                }
                return false;
            }
        });
       
        remotePlay.setClickedListener(mShowDeviceListListener);
        //流转按钮注册热词
        remotePlay.subscribeVoiceEvents(eventremote);
        //流转按钮设置流转弹窗事件
        remotePlay.setSpeechEventListener(speech_mShowDeviceListListener);

}

这里只展示了核心部分的代码,具体含义看名称即可知,详情参见附件。

4. 关于分布式流转

  关于流转的部分,这里简单复习一下。
在本案例里,任何动态变化的数据都是迁移和恢复的内容。

5. 关于旋转动画

  • 创建一个属性动画
    /* 属性动画 */
    private AnimatorProperty animatorProperty;

  • 初始化一个属性对象
          //初始化属性动画对象 musicPosters是一个Image组件
        animatorProperty = musicPosters.createAnimatorProperty();
        animatorProperty.setCurveType(Animator.CurveType.LINEAR);
  • 启动
//让他一直循环转下去
animatorProperty.rotate(360+musicPosters.getRotation()).setDuration(100000).setLoopedCount(-1).start();
  • 暂停,重置
animatorProperty.stop();
animatorProperty.reset();

各种操作放在合适的位置执行就可以了。

5. 结语

  本次主要在分布式音乐播放器案例中加入了智慧屏特有的可见即可说的功能,和一些简单的优化和动画。在手机,平板上也能有类似的操作,可参考分布式语音照相机,但相比之下还是觉得可见即可说的功能更加清楚和好用。

有关华为智慧屏分布式语音音乐软件,可见即可说的更多相关文章

  1. ruby - 即时确定方法的可见性 - 2

    我正在编写一个方法,它将在一个类中定义一个实例方法;类似于attr_accessor:classFoocustom_method(:foo)end我通过将custom_method函数添加到Module模块并使用define_method定义方法来实现它,效果很好。但我无法弄清楚如何考虑类(class)的可见性属性。例如,在下面的类中classFoocustom_method(:foo)privatecustom_method(:bar)end第一个生成的方法(foo)必须是公共(public)的,第二个(bar)必须是私有(private)的。我怎么做?或者,如何找到调用我的cust

  2. ruby - 分布式事务和队列,ruby,erlang,scala - 2

    我有一个涉及多台机器、消息队列和事务的问题。因此,例如用户点击网页,点击将消息发送到另一台机器,该机器将付款添加到用户的帐户。每秒可能有数千次点击。事务的所有方面都应该是容错的。我以前从未遇到过这样的事情,但一些阅读表明这是一个众所周知的问题。所以我的问题。我假设安全的方法是使用两阶段提交,但协议(protocol)是阻塞的,所以我不会获得所需的性能,我是否正确?我通常写Ruby,但似乎Redis之类的数据库和Rescue、RabbitMQ等消息队列系统对我的帮助不大——即使我实现某种两阶段提交,如果Redis崩溃,数据也会丢失,因为它本质上只是内存。所有这些让我开始关注erlang和

  3. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  4. 华为常用命令 - 2

    system-view进入系统视图quit退到系统视图sysname交换机命名vlan20创建vlan(进入vlan20)displayvlan显示vlanundovlan20删除vlan20displayvlan20显示vlan里的端口20Interfacee1/0/24进入端口24portlink-typeaccessvlan20把当前端口放入vlan20undoporte1/0/10删除当前VLAN端口10displaycurrent-configuration显示当前配置02配置交换机支持TELNETinterfacevlan1进入VLAN1ipaddress192.168.3.100

  5. 华为OD机试真题 C++ 实现【带传送阵的矩阵游离】【2023 Q2 | 200分】 - 2

            所有题目均有五种语言实现。C实现目录、C++实现目录、Python实现目录、Java实现目录、JavaScript实现目录题目n行m列的矩阵,每个位置上有一个元素你可以上下左右行走,代价是前后两个位置元素值差的绝对值.另外,你最多可以使用一次传送阵(只能从一个数跳到另外一个相同的数)求从走上角走到右下角最少需要多少时间。输入描述:第一行两个整数n,m,分别代表矩阵的行和列。后面n行,每行m个整数,分别代表矩阵中的元素。输出描述:一个整数,表示最少需要多少时间。

  6. 西安华为OD面试体验 - 2

    西安华为OD面试体验开始投简历技术面试进展工作进展开始投简历去年一整年一直在考研和工作之间纠结,感觉自己的状态好像当时的疫情一样差劲。之前刚毕业的时候投了个大厂的简历,结果一面写算法的时候太拉跨了,虽然知道时dfs但是代码熟练度不够,放在平时给足时间自己可以调试通过,但是熟练度不够那面试当时就写不出来被刷了。说真的算法学到后期我感觉最重要的是熟练度和背板子(对于我这种普通玩家来说),面试题如果一上来短时间内想不出思路就完蛋了。然后由于当时找的工作不是很理想就又想考研了。但是考研是有风险的,我自我感觉自己可能冲不上那个学校,而找工作一个没成可以继续找嘛。本着抱着试试看的态度在boss上投了简历,

  7. ruby - 是否可以动态检查方法可见性范围(私有(private)/公共(public)/ protected )? - 2

    如thisanswer中所述,在Ruby2.1或更高版本中,此代码:classSimpleTestprivatedefine_method:foodo42endend将定义foo作为SimpleTest的私有(private)方法实例。(在Ruby2.0和更早版本中,它不会是私有(private)的。)但是,我希望做一些不那么琐碎的事情。我想定义一个类可以扩展的DSL,并希望DSL在内部定义的方法尊重调用上下文的私有(private)/protected可见性。这可能不是很清楚,所以这里有一个例子:moduleDsldefhas_a(name)define_methodnamedo42

  8. ruby - 停止分布式 Ruby 服务 - 2

    我有一个启动DRb服务的脚本,然后生成处理程序对象并通过DRb.thread.join等待。我希望脚本一直运行直到被明确杀死,所以我添加了trap"INT"doDRb.stop_serviceend在Ruby1.8下成功停止DRb服务并退出,但在1.9下似乎死锁(在OSX10.6.7上)。对该进程进行采样显示在semaphore_wait_signal_trap中有几个线程在旋转。我假设我在调用stop_service时做错了什么,但我不确定是什么。谁能给我任何关于如何正确处理它的指示? 最佳答案 好的,我想我已经找到了解决方案。如

  9. 华为ensp详细安装包、安装教程及所遇问题 - 2

    目录一、安装包链接二、安装详细步骤1.安装Wireshark和WinPcap2.安装OracleVMVirtualBox3.安装ensp三、安装后注册四、启动路由器出现40错误怎么解决一、安装包链接二、安装详细步骤链接:https://pan.baidu.com/s/1QbUUYMOMIV2oeIKHWP1SpA?pwd=xftx提取码:xftx1.安装Wireshark和WinPcap找到Wireshark安装包所在文件夹,双击它,按照以下步骤安装。2.安装OracleVMVirtualBox找到OracleVMVirtualBox安装包所在文件夹,双击它,按照以下步骤安装。注:可自定义安装

  10. 中润光学在科创板IPO过会:拟募资4亿元,张平华为实际控制人 - 2

    近日,上海证券交易所科创板披露的信息显示,嘉兴中润光学科技股份有限公司(下称“中润光学”)获得上市委会议通过。这意味着,中润光学的上市之路获得实质性进展,接下来将提交注册。据贝多财经了解,中润光学的招股书于2022年5月20日获得科创板受理,5个月后便获得上市委会议通过,进度不可谓不快。本次冲刺科创板上市,中润光学拟募资4.05亿元,计划用于高端光学镜头智能制造项目、高端光学镜头研发中心升级项目等。天眼查信息显示,中润光学成立于2012年8月,是一家以从事非金属矿物制品业为主的企业。当前,该公司的注册资本为6600万元,法定代表人为张平华。穿透股权可知,张平华也是该公司的实际控制人。据招股书介

随机推荐