我想在我的家庭自动化应用程序中加入连续(免提)语音命令识别

2023-12-30

我创建了一个简单的 Android 应用程序来控制连接到我的 Raspberry Pi 的继电器。我使用按钮以及基本的语音识别来触发这些按钮并打开/关闭相应的中继通道。

到目前为止,语音识别部分是由 RecognizerIntent 处理的,其中我需要按下应用程序上的按钮来打开 Google 语音提示,该提示会监听我的语音命令并激活/停用控制继电器开关的相应按钮。

我想做同样的事情连续语音识别允许应用程序连续听取我的命令,而无需用户按下应用程序上的按钮,从而允许免提操作。

这是我现有的代码,一种非常简单的语音识别方法,它允许我打开和关闭连接到继电器的各种设备的按钮:

public void micclick(View view) {
        if(view.getId()==R.id.mic)
        {promptSpeechInput();}
}

private void promptSpeechInput() {
    Intent i= new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
    i.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
    i.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault());
    i.putExtra(RecognizerIntent.EXTRA_PROMPT,"Speak!");
    try{
        startActivityForResult(i,100);

    }
    catch (ActivityNotFoundException a)
    {
        Toast.makeText(MainActivity.this,"Sorry your device doesn't support",Toast.LENGTH_SHORT).show();
    }
}
public void onActivityResult(int requestCode, int resultCode, Intent i) {
    super.onActivityResult(requestCode, resultCode, i);
    String voicetxt;
    switch (requestCode) {
        case 100:
            if (resultCode == RESULT_OK && i != null) {
                ArrayList<String> result2 = i.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
                voicetxt = result2.get(0);
                if (voicetxt.equals("fan on")) {
                    StringBuffer result=new StringBuffer();
                    toggleButton1.setChecked(true);
                    result.append("Fan: ").append(toggleButton1.getText());
                    sc.onRelayNumber="a";
                    new Thread(sc).start();
                    Toast.makeText(MainActivity.this, result.toString(),Toast.LENGTH_SHORT).show();
                }
                if (voicetxt.equals("fan of")) {
                    StringBuffer result=new StringBuffer();
                    toggleButton1.setChecked(false);
                    result.append("Fan: ").append(toggleButton1.getText());
                    sc.onRelayNumber = "a_off";
                    new Thread(sc).start();
                    Toast.makeText(MainActivity.this, result.toString(),Toast.LENGTH_SHORT).show();
                }
                if (voicetxt.equals("light on")) {
                    StringBuffer result=new StringBuffer();
                    toggleButton2.setChecked(true);
                    result.append("Light: ").append(toggleButton2.getText());
                    sc.onRelayNumber = "b";
                    new Thread(sc).start();
                    Toast.makeText(MainActivity.this, result.toString(),Toast.LENGTH_SHORT).show();
                }
                if (voicetxt.equals("light off")) {
                    StringBuffer result=new StringBuffer();
                    toggleButton2.setChecked(false);
                    result.append("Light: ").append(toggleButton2.getText());
                    sc.onRelayNumber = "b_off";
                    new Thread(sc).start();
                    Toast.makeText(MainActivity.this, result.toString(),Toast.LENGTH_SHORT).show();
                }
                if (voicetxt.equals("air conditioner on")) {
                    StringBuffer result=new StringBuffer();
                    toggleButton3.setChecked(true);
                    result.append("AC: ").append(toggleButton3.getText());
                    sc.onRelayNumber = "c";
                    new Thread(sc).start();
                    Toast.makeText(MainActivity.this, result.toString(),Toast.LENGTH_SHORT).show();
                }
                if (voicetxt.equals("air conditioner of")) {
                    StringBuffer result=new StringBuffer();
                    toggleButton3.setChecked(false);
                    result.append("AC: ").append(toggleButton3.getText());
                    sc.onRelayNumber = "c_off";
                    new Thread(sc).start();
                    Toast.makeText(MainActivity.this, result.toString(),Toast.LENGTH_SHORT).show();
                }
                if (voicetxt.equals("heater on")) {
                    StringBuffer result=new StringBuffer();
                    toggleButton4.setChecked(true);
                    result.append("Heater: ").append(toggleButton4.getText());
                    sc.onRelayNumber = "d";
                    new Thread(sc).start();
                    Toast.makeText(MainActivity.this, result.toString(),Toast.LENGTH_SHORT).show();
                }
                if (voicetxt.equals("heater off")) {
                    StringBuffer result=new StringBuffer();
                    toggleButton4.setChecked(false);
                    result.append("Heater: ").append(toggleButton4.getText());
                    sc.onRelayNumber = "d_off";
                    new Thread(sc).start();
                    Toast.makeText(MainActivity.this, result.toString(),Toast.LENGTH_SHORT).show();
                }
            }
            break;
    }
}

我想无需按下按钮即可实现相同的功能。请注意,我是 Android 应用程序开发新手。如果可能的话,请描述外部库的使用情况(如果需要),因为我认为 Google 的 RecognizerIntent 无法实现连续识别。我推测我可能需要包含类似的库CMUS狮身人面像,但我不知道该怎么做。


对于连续识别/听写模式,您可以执行多种操作。您可以使用Android本身的谷歌语音识别功能,不建议连续识别(如上所述https://developer.android.com/reference/android/speech/SpeechRecognizer.html https://developer.android.com/reference/android/speech/SpeechRecognizer.html)

该 API 的实现可能会将音频流传输到远程 服务器来执行语音识别。因此这个 API 不是 旨在用于连续识别,这将消耗 大量的电池和带宽。

但如果您确实需要它,可以通过创建自己的类并继承 IRecognitionListener 来解决。 (我在xamarin-android上写的,语法与原生android非常相似)

public class CustomRecognizer : Java.Lang.Object, IRecognitionListener, TextToSpeech.IOnInitListener
{
    private SpeechRecognizer _speech;

    private Intent _speechIntent;


    public string Words;


    public CustomRecognizer(Context _context)
    {
        this._context = _context;
        Words = "";
        _speech = SpeechRecognizer.CreateSpeechRecognizer(this._context);
        _speech.SetRecognitionListener(this);
        _speechIntent = new Intent(RecognizerIntent.ActionRecognizeSpeech);
        _speechIntent.PutExtra(RecognizerIntent.ExtraLanguageModel, RecognizerIntent.LanguageModelFreeForm);
        _speechIntent.PutExtra(RecognizerIntent.ActionRecognizeSpeech, RecognizerIntent.ExtraPreferOffline);
        _speechIntent.PutExtra(RecognizerIntent.ExtraSpeechInputCompleteSilenceLengthMillis, 1000); 
        _speechIntent.PutExtra(RecognizerIntent.ExtraSpeechInputPossiblyCompleteSilenceLengthMillis, 1000);
        _speechIntent.PutExtra(RecognizerIntent.ExtraSpeechInputMinimumLengthMillis, 1500);
    }

    void startover()
    {
        _speech.Destroy();
        _speech = SpeechRecognizer.CreateSpeechRecognizer(this._context);
        _speech.SetRecognitionListener(this);
        _speechIntent = new Intent(RecognizerIntent.ActionRecognizeSpeech);
        _speechIntent.PutExtra(RecognizerIntent.ExtraSpeechInputCompleteSilenceLengthMillis, 1000);
        _speechIntent.PutExtra(RecognizerIntent.ExtraSpeechInputPossiblyCompleteSilenceLengthMillis, 1000);
        _speechIntent.PutExtra(RecognizerIntent.ExtraSpeechInputMinimumLengthMillis, 1500);
    StartListening();
    }
    public void StartListening()
    {
        _speech.StartListening(_speechIntent);
    }

    public void StopListening()
    {
        _speech.StopListening();
    }

    public void OnBeginningOfSpeech()
    {

    }

    public void OnBufferReceived(byte[] buffer)
    {
    }

    public void OnEndOfSpeech()
    {

    }

    public void OnError([GeneratedEnum] SpeechRecognizerError error)
    {
        Words = error.ToString();
        startover();
    }

    public void OnEvent(int eventType, Bundle @params)
    {
    }

    public void OnPartialResults(Bundle partialResults)
    {
    }

    public void OnReadyForSpeech(Bundle @params)
    {
    }

    public void OnResults(Bundle results)
    {

        var matches = results.GetStringArrayList(SpeechRecognizer.ResultsRecognition);
        if (matches == null)
            Words = "Null";
        else
            if (matches.Count != 0)
            Words = matches[0];
        else
            Words = "";

        //do anything you want for the result
        }
        startover();
    }

    public void OnRmsChanged(float rmsdB)
    {

    }

    public void OnInit([GeneratedEnum] OperationResult status)
    {
        if (status == OperationResult.Error)
            txtspeech.SetLanguage(Java.Util.Locale.Default);
    }


}

在活动中调用它:

void StartRecording()
    {
        string rec = PackageManager.FeatureMicrophone;

        if (rec != "android.hardware.microphone")
        {
            // no microphone, no recording. Disable the button and output an alert
            Toast.MakeText(this, "NO MICROPHONE", ToastLength.Short);
        }
        else
        {

            //you can pass any object you want to connect to your recognizer here (I am passing the activity)
            CustomRecognizer voice = new CustomRecognizer(this);
            voice.StartListening();

        }
    }

不要忘记请求使用麦克风的许可!

解释 :

- 这将消除烦人的“点击开始录制”

-这将始终记录您调用 StartListening() 的那一刻,并且永远不会停止,因为每次完成录制时我总是调用 startover() 或 StartListening()

-这是一个非常糟糕的解决方法,因为在处理录音的那一刻,录音机在调用 StartListening() 之前不会获得任何声音输入(对此没有解决方法)

-谷歌识别对于语音命令来说并不是很好,因为语言模型是“[lang]句子”,所以你不能限制单词,谷歌总是会尝试做出一个“好句子”。

为了更好的结果和用户体验,我真的建议你使用Google Cloud API(但它必须是在线的,而且成本很高),第二个建议是CMUSphinx / PocketSphinx,它是开源的,可以做离线模式,但你必须做所有的事情手动

PocketSphinx 的优势:

  1. 您可以创建自己的词典
  2. 兼容离线模式

  3. 您可以自行训练声学模型(语音等),因此您可以根据您的环境和发音进行配置

  4. 您可以通过访问“PartialResult”获取实时结果

PocketSphinx 的缺点:您必须手动完成所有操作,从设置声学模型、字典、语言模型、阈值等(如果您想要简单的东西就太过分了)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我想在我的家庭自动化应用程序中加入连续(免提)语音命令识别 的相关文章

  • Firebase 管理 SDK Android

    在 Android 中初始化 Firebase Admin SDK 的代码 Override protected void onCreate Bundle savedInstanceState super onCreate savedIns
  • BottomNavigationView - 如何获取选定的菜单项?

    我使用BottomNavigationView来切换片段 如何获取当前选定的菜单项 以防止重新打开片段 BottomNavigationView bottomNavigationView BottomNavigationView findV
  • Android 应用程序在后台运行时保存数据

    目前我正在开发 xmmp 客户端 当应用程序位于前台时 该客户端工作得很好 但由于事实上 当应用程序处于后台时 我在 Application 类中保存了大量数据 复杂的 ArrayList 字符串和布尔值作为公共静态 每个字段都被垃圾收集
  • 如何更新 Firebase 中的节点密钥?

    如何重命名14 04 2017 node 没有用于重命名节点的 API 您必须获取节点的值 使用新名称将其保存到数据库并删除旧节点
  • RxJava、Proguard 和 sun.misc.Unsafe

    我有以下问题RxJava 1 1 0 使用时Proguard 我没有更改 RxJava 版本或其 pro文件 但更新后OkHttp我无法编译使用Proguard因为我有关于sun misc Unsafe不在场 rxJava pro keep
  • 无法在自定义 AOSP 上安装 Google Play 中的某些应用程序:项目不可用。理由:9

    我在尝试从 Google Play 安装某些应用程序时收到以下错误 LibraryUtils isAvailable not available restriction 9 DocUtils getAvailabilityRestricti
  • 在 Google Analytics 中跟踪应用程序版本

    我正在使用谷歌分析模块 https marketplace appcelerator com apps 5081 2014113336 https marketplace appcelerator com apps 5081 2014113
  • Android 原理图内容提供程序库配置?

    Jake Wharton 在最近的一次演讲中提到了这个库 它看起来是避免大量样板文件的好方法 所以我尝试了一下 但没有任何成功 https github com SimonVT schematic https github com Simo
  • OnClick 事件中的 finish() 如何工作?

    我有一个Activity一键退出Activity 通过layout xml我必须设置OnClick事件至cmd exit调用 this finish 效果很好 public void cmd exit View editLayout thi
  • 在 Jetpack Compose 中启动动画矢量 Drawable

    我有一个动画矢量可绘制R drawable my anim 我想在 Jetpack Compose 中展示并开始 可绘制对象显示 渲染正确 但动画未启动 这是撰写视图 Composable fun SplashView Surface mo
  • 带有自定义阵列适配器的微调器不允许选择项目

    我使用自定义阵列适配器作为微调器 但是 当在下拉列表中选择一个项目时 下拉列表保留在那里 并且微调器不会更新 这是错误行为 与使用带有字符串的通用数组适配器相比 这是自定义类 我错过了什么吗 谢谢 public class Calendar
  • Flutter 深度链接

    据Flutter官方介绍深层链接页面 https flutter dev docs development ui navigation deep linking 我们不需要任何插件或本机 Android iOS 代码来处理深层链接 但它并没
  • 通过 ADB 拔出设备:“找不到服务”

    我必须测试我的应用程序在打瞌睡模式下的行为 根据文档 https developer android com training monitoring device state doze standby html testing doze 我
  • 应用程序关闭时的倒计时问题

    我制作了一个 CountDownTimer 代码 我希望 CountDownTimer 在完成时重新启动 即使应用程序已关闭 但它仅在应用程序正在运行或重新启动应用程序时重新启动 因此 如果我在倒计时为 00 10 分钟 秒 时关闭应用程序
  • Android中webview的截图方法

    我在 webview 中的 html5 canvas 上画了一些线 并尝试使用下面的代码截取 webview 的屏幕截图 WebView webView WebView findViewById R id webview webView s
  • 保护 APK 中的字符串

    我正在使用 Xamarin 的 Mono for Android 开发一个 Android 应用程序 我目前正在努力使用 Google Play API 添加应用内购买功能 为此 我需要从我的应用程序内向 Google 发送公共许可证密钥
  • Android:有没有办法以毫安为单位获取设备的电池容量?

    我想获取设备的电池容量来进行一些电池消耗计算 是否可以以某种方式获取它 例如 三星 Galaxy Note 2 的电池容量为 3100mAh 谢谢你的帮助 知道了 在 SDK 中无法直接找到任何内容 但可以使用反射来完成 这是工作代码 pu
  • 使用 Espresso 检查 EditText 的字体大小、高度和宽度

    如何使用 Espresso 检查 EditText 的字体大小 高度和宽度 目前要分割我使用的文本 onView withId R id editText1 perform clearText typeText Amr 并阅读文本 onVi
  • 在webview android中加载本地html文件

    我正在尝试在 android 的 webview 中加载 html 文件的内容 但是 它给了我 网页不可用错误 如果我尝试使用谷歌或雅虎等网站 它们就会起作用 html文件位于src gt main gt assests gt index
  • 找到 Android 浏览器中使用的 webkit 版本?

    有没有办法知道某些特定手机上的 Android 浏览器使用的是哪个版本的 webkit 软件 如果有一个您可以浏览以获取该信息的 URL 那就太好了 但任何其他方式也很好 如果你知道 webkit 版本 你就知道 html5 支持多少 至少

随机推荐

  • NodeJS 热代码推送

    我一直在尝试找出 Node js 上的 热代码推送 基本上 我的主文件 当您键入时运行node app js 由一些设置 配置和初始化组成 在该文件中 我有一个使用 chokidar 的文件观察器 添加文件后 我只需require文件 如果
  • Qt Creator 编译后不会运行应用程序

    我使用 SVN 将托管在 google code 上的 Qt 项目检出到本地文件夹 当我在 Qt Creator 上打开它时 它成功编译了该项目 但是当它尝试运行编译后的程序时 应用程序输出上出现了一条错误消息 该进程无法启动 怎么了 我通
  • 对 Firestore 文档进行排序

    有什么可能的方法可以根据特定字段值的数字对集合的 Cloud Firestore 文档进行排序 例如在集合中ids有一些文档 每个文档中都有一个名为idNumber 该 idNumber 的值是 0 然后在下一个文档中是 1 然后是 2 等
  • 将用户定义属性添加到域类

    我需要允许用户在系统实体之一中定义一些自定义字段 您有任何建议 模式 插件可以帮助我将此功能添加到我的应用程序中吗 thanks Meni 您可以将 Map 属性添加到域类并在其中存储任意数据 但它相当有限 它将生成一个包含 varchar
  • $window.focus() 不适用于 iOS Safari 中的现有选项卡

    当我打开一个新窗口时var win window open name 然后使用聚焦窗口win focus 这是首次在 iOS Safari 中运行 但是 当使用 window open 通过名称再次引用现有窗口时 我无法在 iOS Safa
  • 如何“完成”新行

    所以今晚我的 c windows 窗体应用程序遇到了一些麻烦 当当前选定的新行仍为其默认值时 是否可以将新行插入到 datagridview 中 或者 如果我想以编程方式更改值 如何模拟用户编辑文本框以完成该行 为了澄清 当一行是新行时 并
  • 如何从状态“False(MissingEndpoints)”启用 kube-system/metrics-server?

    我的指标服务器突然无法工作并得到以下信息 kubectl get apiservices egrep metrics v1beta1 metrics k8s io kube system metrics server False Missi
  • jQuery 类选择器性能(困惑)

    So is table selectable td capable input text 优于 table selectable td input text 换句话说 指定一个类会加速还是减慢选择速度 假设在这种情况下不是绝对需要的 我没有
  • 当我无法设置“DYLD_LIBRARY_PATH”时,如何在 macOS 上便携式安装 ImageMagick?

    我正在为 macOS Mojave 开发一个命令行实用程序 它使用 ImageMagick 来操作图像 我想将它作为一个独立的应用程序进行共享 以便其他人可以开箱即用 而无需安装任何额外的 dylib 或框架 ImageMagick 的 H
  • WPF Expander 在 Canvas 内部时不会向左扩展

    我有一个有 4 列的网格 第一列是一个 ZIndex 为 99 的 Canvas 里面有一个扩展器 展开方向设置为 RIGHT 当我单击标题时 扩展器会扩展至第 2 列的顶部 这正是我想要的 我试图在第 4 列内复制此内容 仅相反方向 以便
  • 如何将 Iterator 作为 Iterator<&str> 传递?

    fn my print lt a gt args impl Iterator
  • undefined 不是一个函数(评估 'decorator(target, property, desc)')

    我想整合mobx https mobx js org and mobx 坚持 https github com pinqy520 mobx persist with 反应导航 https reactnavigation org 我读过这些文
  • AJAX ToolKit TabContainer:我可以捕获“活动选项卡面板更改”事件吗

    我有一个AJAX 工具包 TabContainer控制几个TabPanels 我想验证当前活动的内容TabPanel防止用户在数据无效的情况下处理其他数据 如果您需要在服务器端执行 TabPanelChangingEvent 则需要通过更改
  • 递归 MySql 触发器不起作用

    我正在尝试设置一个 MySql 触发器 以便在 ODBC 中插入 更新数据后运行 我的触发器如下 CREATE TRIGGER myTrigger AFTER INSERT ON testTable FOR EACH ROW UPDATE
  • 有人用SvnMapper吗?

    首先我下载 SvnMapper svnmapper tigris org http svnmapper tigris org 并安装它 然后我启动SvnMapper GUI需要输入subversion的URL 我在这一步失败了 我有一个本地
  • 当一个操作“接近 O(1)”而不是“是 O(1)”时,这意味着什么?

    例如 考虑 NET Framework 4 5 的文档Dictionary
  • GitHub Desktop 中有强制推送选项吗?

    使用版本 1 0 4 我找不到任何强制推送选项 命令行是使用武力的唯一方法吗 推不动 https i stack imgur com ZjW5E jpg and 拉不动 https i stack imgur com Rbfoc jpg 2
  • 用于保护 S3 文档的 Rails 实施

    我想通过 Rails 应用程序保护我的 s3 文档 这样如果我转到 www myapp com attachment 5 应在显示 下载文档之前对用户进行身份验证 我在 stackoverflow 上读过类似的问题 但我不确定我是否看到了任
  • Swing 中精美的工具提示

    我有一个 JTable 我想为列中的特定单元格显示一个精美的工具提示 基本上是一个 JTextArea 我正在使用自定义单元格渲染器 因此如果我能够弄清楚当我将鼠标悬停在单元格渲染器的组件上时如何弹出一个窗口 那就很容易了 有没有关于如何执
  • 我想在我的家庭自动化应用程序中加入连续(免提)语音命令识别

    我创建了一个简单的 Android 应用程序来控制连接到我的 Raspberry Pi 的继电器 我使用按钮以及基本的语音识别来触发这些按钮并打开 关闭相应的中继通道 到目前为止 语音识别部分是由 RecognizerIntent 处理的