Android原生SpeechRecognizer(语音识别)

开篇先吐槽下,在Android 平台开发原生的SpeechRecognizer真是难受的,不像ios,无比轻松,平台统一。 由于Android 平台的碎片化问题比较严重,各个厂商都有自己的实现,尤其是语音助手出来以后,每家的语音服务肯定是不一样的。

目前Android原生的SpeechRecognizer做法应该有两种

  1. 默认调用原生SpeechRecognizer,并稍作修改
  2. 调用第三方,科大讯飞,百度等

这两种做法中

  • 1. 在Google原生系统是可以的,但是在国内的环境是需要修改,修改后能保证各个机型基本可以用,至于识别效果就要看各个机型自己实现的怎么样了
  • 2. 最简单省心省力,如果你的项目可以这么做,那么兄弟恭喜你,你是最幸福的

这里我们不讲第三方的,大家可以自己去集成第三方sdk,主要讨论原生的开发

​ 首先权限不要忘记(记得6.0以后动态请求权限)

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
复制代码

​ 在SpeechRecognizer.class有这样SpeechRecognizer .isRecognitionAvailable一个方法

    /**
     * Checks whether a speech recognition service is available on the system. If this method
     * returns {@code false}, {@link SpeechRecognizer#createSpeechRecognizer(Context)} will
     * fail.
     * 
     * @param context with which {@code SpeechRecognizer} will be created
     * @return {@code true} if recognition is available, {@code false} otherwise
     */
    public static boolean isRecognitionAvailable(final Context context) {
        final List<ResolveInfo> list = context.getPackageManager().queryIntentServices(
                new Intent(RecognitionService.SERVICE_INTERFACE), 0);
        return list != null && list.size() != 0;
    }
复制代码

​ 该方法在使用语音识别前建议要调用下,该方法是检查当前系统有没有语音识别服务,我相信绝大多数厂商都有这个服务,但是都有自己特别的实现,但是它至少有,有就可以用。但是,你像oppo的7.0以后机器,这个方法调用后就是false,这时候就是毫无办法了。oppo 7.0以后就是这样调用完后返回false,对于 oppo 这种情况,可以在手机上装一个**讯飞语音+**的app,语音识别就可以了,但是这种方法我估计没人会用,用户体验太差。

​ 如果该方法返回false在我们调用*SpeechRecognizer.startListening();*方法的时候会日志中发现这行log

no selected voice recognition service
复制代码

​ 该日志在SpeechRecognizer.startListening(final Intent recognizerIntent)方法中,大家可以进源码查看这里就不贴了。

​ 检查完如果语音识别可用,接下来有两种做法我们一个个来


  1. 直接创建实例启动服务
mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(context);
mSpeechRecognizer.setRecognitionListener(this);
复制代码

​ 创建识别实例,并添加监听,这里没有什么问题,在监听中我们可以拿到我们的想要的回调

/**
 * Used for receiving notifications from the SpeechRecognizer when the
 * recognition related events occur. All the callbacks are executed on the
 * Application main thread.
 * 值的注意的是,所有的回调都在主线程
 */
public interface RecognitionListener {
    
  	// 实例准备就绪 
    void onReadyForSpeech(Bundle params);

    // 开始语音识别
    void onBeginningOfSpeech();

   	// 聆听分贝值 可能会有负数哦
    void onRmsChanged(float rmsdB);

    void onBufferReceived(byte[] buffer);

    // 识别结束
    void onEndOfSpeech();

    // 错误码
    void onError(int error);

    // 识别的结果,在某些国产机上,这个结果会是空
    void onResults(Bundle results);

    // 识别的部分结果 有些过程机上 [onResults] 方法为空,可以在这里拿到结果
    void onPartialResults(Bundle partialResults);

    void onEvent(int eventType, Bundle params);
}

复制代码

​ 指的注意的是,如果 SpeechRecognizer .isRecognitionAvailable 方法返回false的话,即使注册了监听*mSpeechRecognizer.setRecognitionListener(this);*回调方法不会走的,因为没有该服务的

​ 下面就是样板代码了,都一样的

// 启动服务需要一个 Intent
mRecognitionIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
            mRecognitionIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
mRecognitionIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
mRecognitionIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 3);
// mLocale 是一个语音种类,可以根据自己的需求去设置
mRecognitionIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, mLocale);

// 开始语音识别 结果在 mSpeechRecognizer.setRecognitionListener(this);回调中
mSpeechRecognizer.startListening(mRecognitionIntent);

// 停止监听
mSpeechRecognizer.stopListening();

// 取消服务
mSpeechRecognizer.cancel();
复制代码

​ 在识别过程中,如果出错,错误码很有用

    // 错误码
    void onError(int error);
复制代码

​ 这里是错误码的原因,可以做参考去排查,错误码在SpeechRecognizer.class中,可以自行查阅

    /** Network operation timed out. */
    public static final int ERROR_NETWORK_TIMEOUT = 1;

    /** Other network related errors. */
    public static final int ERROR_NETWORK = 2;

    /** Audio recording error. */
    public static final int ERROR_AUDIO = 3;

    /** Server sends error status. */
    public static final int ERROR_SERVER = 4;

    /** Other client side errors. */
    public static final int ERROR_CLIENT = 5;

    /** No speech input */
    public static final int ERROR_SPEECH_TIMEOUT = 6;

    /** No recognition result matched. */
    public static final int ERROR_NO_MATCH = 7;

    /** RecognitionService busy. */
    public static final int ERROR_RECOGNIZER_BUSY = 8;

    /** Insufficient permissions */
    public static final int ERROR_INSUFFICIENT_PERMISSIONS = 9;
复制代码

​ 这里特别说明下,如果你所有操作都正常,可是在监听回调中一直出现 ERROR_INSUFFICIENT_PERMISSIONS 即错误码返回一直是 9,这时候可以尝试常看各个厂商的语音助手有没有在这里做处理,方法就是在说话的时候,这时打开语音助手,会有语音助手提示赋予应用权限,这种情况我在小米手机上遇到过,小爱同学需要打开权限,打开就好了。

​ 以上就是一般的做法,但是不一定有用,厂商会做什么事,我们是不知道滴,具体问题需要具体对待,下面我们来讨论另外一种实现


  1. 照旧在启动服务前需要检查服务是否存在 *SpeechRecognizer.isRecognitionAvailable(context);*如果返回false,要么歇菜(绝大多数不会出现),要么自己实现,我自己实现不了。

    如果返回true,说明有语音识别服务可以用,这时候我们需要记录下当前系统内置的是哪个服务

String serviceComponent = Settings.Secure.getString(context.getContentResolver(),
                                                            "voice_recognition_service");
复制代码

serviceComponent就是我们的服务名称,eg:华为手机返回"com.huawei.vassistant/com.huawei.ziri.service.FakeRecognitionService"从名字看就是 FakeRecognitionService伪造的语音识别服务,就是说这个是不用的。这里多说下,华为使用的讯飞的语音识别服务。

​ 组装成组件

// 当前系统内置语音识别服务
ComponentName component = ComponentName.unflattenFromString(serviceComponent);
复制代码

​ 组装成一个Component组件,后面我们需要用到


// 内置语音识别服务是否可用
boolean isRecognizerServiceValid = false;
ComponentName currentRecognitionCmp = null;
// 查找得到的 "可用的" 语音识别服务
List<ResolveInfo> list = context.getPackageManager().queryIntentServices(new Intent(RecognitionService.SERVICE_INTERFACE), MATCH_ALL);

if (list != null && list.size() != 0) {
    for (ResolveInfo info : list) {
        debugLog(TAG, "\t" + info.loadLabel(context.getPackageManager()) + ": "
                + info.serviceInfo.packageName + "/" + info.serviceInfo.name);
				
      	// 这里拿系统使用的语音识别服务和内置的语音识别比较,如果相同,OK我们直接直接使用
      	// 如果相同就可以直接使用mSpeechRecognizer = 			   SpeechRecognizer.createSpeechRecognizer(context);来创建实例,因为内置的可以使用
        if (info.serviceInfo.packageName.equals(component.getPackageName())) {
            isRecognizerServiceValid = true;
            break;
        } else {
         // 如果服务不同,说明 内置服务 和 系统使用 不是同一个,那么我们需要使用系统使用的
         // 因为内置的系统不用,我们用了也没有用
            currentRecognitionCmp = new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name);
        }

    }
} else {
  	// 这里既是查不到可用的语音识别服务,可以歇菜了
    debugLog(TAG, "No recognition services installed");
    return false;
}
复制代码

​ 根据判断结果创建实例

        // 当前系统内置语音识别服务可用
        if (isRecognizerServiceValid) {
            mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(context);
        } else {
          // 内置不可用,需要我们使用查找到的可用的
            mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(context, currentRecognitionCmp);
        }

        mSpeechRecognizer.setRecognitionListener(this);
复制代码

​ 关于*SpeechRecognizer createSpeechRecognizer(final Context context,final ComponentName serviceComponent)*方法源码如下

    /**
     * Factory method to create a new {@code SpeechRecognizer}. Please note that
     * {@link #setRecognitionListener(RecognitionListener)} should be called before dispatching any
     * command to the created {@code SpeechRecognizer}, otherwise no notifications will be
     * received.
     *
     * Use this version of the method to specify a specific service to direct this
     * {@link SpeechRecognizer} to. Normally you would not use this; use
     * {@link #createSpeechRecognizer(Context)} instead to use the system default recognition
     * service.
     * 
     * @param context in which to create {@code SpeechRecognizer}
     * @param serviceComponent the {@link ComponentName} of a specific service to direct this
     *        {@code SpeechRecognizer} to
     * @return a new {@code SpeechRecognizer}
     */
    public static SpeechRecognizer createSpeechRecognizer(final Context context,
            final ComponentName serviceComponent) {
        if (context == null) {
            throw new IllegalArgumentException("Context cannot be null)");
        }
        checkIsCalledFromMainThread();
        return new SpeechRecognizer(context, serviceComponent);
    }
复制代码

​ 注释写的很明白,不建议我们使用,但是没办法,我们也不想折腾,各大厂商自己有实现,只能这样了。

​ 使用该方法来做基本能满足大多数手机的功能实现,但是这种方法的前提有一个*SpeechRecognizer.isRecognitionAvailable(final Context context)*该方法要返回true,系统没有服务可用,那是没有办法的。

​ 以下是自己的实现,可以根据自己使用修改

        // 查找当前系统的内置使用的语音识别服务
        // com.huawei.vassistant/com.huawei.ziri.service.FakeRecognitionService
        String serviceComponent = Settings.Secure.getString(context.getContentResolver(),
                                                            "voice_recognition_service");

        debugLog(TAG, "voice_recognition_service : " + serviceComponent);

        if (TextUtils.isEmpty(serviceComponent)) {
            return false;
        }

        ComponentName component = ComponentName.unflattenFromString(serviceComponent);

        if (component == null) {
            debugLog(TAG, "voice_recognition_service component == null");
            return false;
        }

        debugLog(TAG, "serviceComponent : " + component.toShortString());

        boolean isRecognizerServiceValid = false;
        ComponentName currentRecognitionCmp = null;

        // 查找得到的 "可用的" 语音识别服务
        List<ResolveInfo> list = context.getPackageManager().queryIntentServices(new Intent(RecognitionService.SERVICE_INTERFACE), MATCH_ALL);
        if (list != null && list.size() != 0) {
            for (ResolveInfo info : list) {
                debugLog(TAG, "\t" + info.loadLabel(context.getPackageManager()) + ": "
                        + info.serviceInfo.packageName + "/" + info.serviceInfo.name);

                if (info.serviceInfo.packageName.equals(component.getPackageName())) {
                    isRecognizerServiceValid = true;
                    break;
                } else {
                    currentRecognitionCmp = new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name);
                }

            }
        } else {
            debugLog(TAG, "No recognition services installed");
            return false;
        }

        if (mSpeechRecognizer != null) {
            return true;
        }

        debugLog(TAG, "isRecognitionAvailable: " + SpeechRecognizer.isRecognitionAvailable(context));

        if (isRecognizerServiceValid) {
            mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(context);
        } else {
            mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(context, currentRecognitionCmp);
        }

        mSpeechRecognizer.setRecognitionListener(this);

        if (mRecognitionIntent == null) {
            mRecognitionIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
            mRecognitionIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
            mRecognitionIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
            mRecognitionIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 3);
        }
        return true;
复制代码

到这里之后就是监听回调,回到了第一种方法的实现。就不贴代码了。


如有错误,不吝赐教

转载自:https://juejin.im/post/5cfa1a866fb9a07eab687234

About Wouldn't your prefer to let your users speak instead of making them type? This plugin uses OS components for speech recognition and send it to your Unity scripts as String objects. Plugin supports: - Android >= 3.0 (haven’t tested below, it might work though… ), - iOS >= 10.0. That doesn’t mean you can’t target iOS lower than 10 - you simply have to prepare fallback code to cover cases when user doesn’t have access to speech recognition (SpeechRecognizer.EngineExists() for the help!). Keep in mind that both iOS and Android might use Internet connection for speech detection, which means it might fail in case there’s no active connection. Plugin doesn’t work in Editor! You have to run your app on real iOS or Android device. MOBILE SPEECH RECOGNIZER - UNITY PLUGIN ?2 Quick Start Open example scene Go to KKSpeechRecognizer/Example folder inside Unity and open ExampleScene: It shows basic usage of a plugin, which is: 1. Detecting if speech recognition exists on user’s device (keep in mind that it won’t be available on e.g. iOS 9 or old Android phones), 2. If it exists, and user clicks on “Start Recording” button it listens for recognized text and displays it on a screen, 3. On Android, speech recognition automatically detects when user finishes speaking, but on iOS we have to wait for user clicking “Stop Recording” to finish whole process (i.e. get final results). Before running it on Android or iOS device you have to… Setup permissions iOS After generating Xcode project (keep in mind that you have to use Xcode 8 or higher) you have to add two permissions keys to your project: MOBILE SPEECH RECOGNIZER - UNITY PLUGIN ?3 NSMicrophoneUsageDescription explanation from Apple docs: This key lets you describe the reason your app accesses any of the the device’s microphones. When the system prompts the user to allow access, this string is displayed as part of the alert. NSSpeechRecognitionUsageDescription explanation from Apple docs: This key lets you describe the reason y
以下是一个Android原生持续检测麦克风并语音转文字的代码示例: 1. 在Manifest文件中添加麦克风权限: ```xml <uses-permission android:name="android.permission.RECORD_AUDIO" /> ``` 2. 在MainActivity中创建SpeechRecognizer对象,并实现RecognitionListener接口: ```java public class MainActivity extends AppCompatActivity implements RecognitionListener { private SpeechRecognizer speechRecognizer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); speechRecognizer = SpeechRecognizer.createSpeechRecognizer(this); speechRecognizer.setRecognitionListener(this); } @Override public void onReadyForSpeech(Bundle params) { Log.d("Speech", "Ready for speech"); } @Override public void onBeginningOfSpeech() { Log.d("Speech", "Beginning of speech"); } @Override public void onRmsChanged(float rmsdB) { Log.d("Speech", "RMS changed"); } @Override public void onBufferReceived(byte[] buffer) { Log.d("Speech", "Buffer received"); } @Override public void onEndOfSpeech() { Log.d("Speech", "End of speech"); } @Override public void onError(int error) { Log.d("Speech", "Error: " + error); } @Override public void onResults(Bundle results) { Log.d("Speech", "Results received"); ArrayList<String> matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); if (matches != null && matches.size() > 0) { String text = matches.get(0); Log.d("Speech", "Text: " + text); } } @Override public void onPartialResults(Bundle partialResults) { Log.d("Speech", "Partial results received"); ArrayList<String> matches = partialResults.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); if (matches != null && matches.size() > 0) { String text = matches.get(0); Log.d("Speech", "Partial text: " + text); } } @Override public void onEvent(int eventType, Bundle params) { Log.d("Speech", "Event: " + eventType); } } ``` 3. 在需要开始检测麦克风的地方调用startListening方法: ```java Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); speechRecognizer.startListening(intent); ``` 这将启动语音识别,并在检测到语音时调用RecognitionListener接口中的方法。 4. 在需要停止检测麦克风的地方调用stopListening方法: ```java speechRecognizer.stopListening(); ``` 这将停止语音识别,并停止调用RecognitionListener接口中的方法。 注意:以上代码仅为示例,实际应用中需要根据需求进行修改和调整。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值