Распознавание речи в Android

Распознавание речи — процесс преобразования речевого сигнала в текстовый поток. Первое устройство для распознавания речи появилось в 1952 году, оно могло распознавать произнесённые человеком цифры. Современные телефоны могут намного больше. В этой статье  я расскажу о том, как с помощью Голосового поиска Google Inc. научить ваши приложения слушать.

Немого теории. Андроид сам по себе не умеет распознавать речь. Поэтому, если у вас “голое” устройство с Андроидом на борту, то распознавать на нем ничего не получится. Как же тогда быть? Можно поискать стороннюю библиотеку для этих целей и добавить ее в проект (тернистый путь) или попросить другое приложение распознавать для нас речь (простой пусть). Мы пойдем по простому пути, следовательно, у нас на устройстве должно быть установлено хотя бы одно приложение способное обрабатывать специально созданный для такой задачи Intent с действием RecognizerIntent.ACTION_RECOGNIZE_SPEECH.

Одним из таких приложений является Голосовой поиск Google Inc. Работает оно просто замечательно, поддерживает различные языки. Для успешной работы требуется выход в интернет, так как сам процесс распознавания происходит где-то на серверах Google. При запуске приложения появляется Activity с парой управляющих кнопок (представлена на рисунке ниже), которая информирует пользователя о том, что надо говорить. Как только пользователь заканчивает говорить, диалог закрывается, а нам в приложение возвращается массив текстовых строк – распознанная речь.

Блок-схемы еще никто никто не отменял. Учитывая вышеизложенное, напрашивается следующая логика работы, представленная на схеме ниже.

Приступаем к реализации. Напишем программный код, который позволит максимально просто интегрировать голосовой поиск в наше приложение. Согласно представленной выше схеме, код должен делать следующее:

  • принимать запрос на распознавание речи
  • проверять наличие приложения способного распознавать речь.
  • если распознавание доступно, то вызвать стороннее приложение для распознавания речи, получать результат
  • если распознавание не доступно, то показать диалог запроса установки Голосового поиска Google и отправлять пользователя в маркет, если он захочет

Код программы распознавания речи на Android

Создадим класс, в котором реализуем логику для распознавания речи. Назовем этот класс SpeechRecognitionHelper. В классе объявим статичную, публичную функцию run, которая будет принимать запрос на запуск распознавания.



Класс-помощник для распознавания речи

public class SpeechRecognitionHelper {


     * Запускает процесс распознавания. Проверяет наличие Activity для распознавания речи.
     * Если Activity нет, отправляет пользователя в маркет установить Голосовой Поиск
     * Google. Если активи для распознавания есть, то отправляет Intent для ее запуска.
     *
     * @param ownerActivity Activity, которая инициировала процесс распознавания
     */
    public static void run(Activity ownerActivity) {
        // проверяем есть ли Activity для распознавания
        if (isSpeechRecognitionActivityPresented(ownerActivity) == true) {
            // если есть - запускаем распознавание
            startRecognition(ownerActivity);
        } else {
            // если нет, то показываем уведомление что надо установить Голосовой Поиск
            Toast.makeText(ownerActivity, "Чтобы активировать голосовой поиск необходимо установить "Голосовой поиск Google"", Toast.LENGTH_LONG).show();
            // начинаем процесс установки
            installGoogleVoiceSearch(ownerActivity);
        }
    }
}

Кроме функции run нам необходимо реализовать еще три функции:

  • isSpeechRecognitionActivityPresented – проверяет установлено ли приложение способное распознавать речь
  • startRecognition – формирует правильный Intent и запускает распознавание
  • installGoogleVoiceSearch – инициирует процесс установки Голосового Поиска Google

Проверить установлено ли на устройстве приложение для распознавания речи можно с помощью метода queryIntentActivities класса PackageManager. Данный метод выдает список Activity, которые могут обработать указанный Intent. Получить экземпляр класса PackageManager можно с помощью функции getPackageManager. Вот что получилось:


     * Проверяет наличие Activity способной выполнить распознавание речи
     *
     * @param ownerActivity Activity, которая запросила проверку
     * @return true - если есть, false - если такой Activity нет
     */
    private static boolean isSpeechRecognitionActivityPresented(Activity ownerActivity) {
        try {
            // получаем экземпляр менеджера пакетов
            PackageManager pm = ownerActivity.getPackageManager();
            // получаем список Activity способных обработать запрос на распознавание
            List activities = pm.queryIntentActivities(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);

            if (activities.size() != 0) {    // если список не пустой
                return true;                // то умеем распознавать речь
            }
        } catch (Exception e) {

        }

        return false; // не умеем распознавать речь

     * Проверяет наличие Activity способной выполнить распознавание речи
     *
     * @param ownerActivity Activity, которая запросила проверку
     * @return true - если есть, false - если такой Activity нет
     */
    private static boolean isSpeechRecognitionActivityPresented(Activity ownerActivity) {
        try {
            // получаем экземпляр менеджера пакетов
            PackageManager pm = ownerActivity.getPackageManager();
            // получаем список Activity способных обработать запрос на распознавание
            List activities = pm.queryIntentActivities(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);

            if (activities.size() != 0) {    // если список не пустой
                return true;                // то умеем распознавать речь
            }
        } catch (Exception e) {

        }

        return false; // не умеем распознавать речь
    }




Приступаем к реализации функции startRecognition. Данная функция должна сформировать правильный Intent для запуска Activity  распознавания речи. О том как это сделать изучаем на страничке документации. Получаем следующую реализацию:




    /**
     * Отправляет Intent с запросом на распознавание речи
     * @param ownerActivity инициировавшая запрос Activity
     */
    private static void startRecognitionActivity(Activity ownerActivity) {

        // создаем Intent с действием RecognizerIntent.ACTION_RECOGNIZE_SPEECH
        Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);

        // добавляем дополнительные параметры:
        intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Голосовой поиск Inforino");    // текстовая подсказка пользователю
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);    // модель распознавания оптимальная для распознавания коротких фраз-поисковых запросов
        intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 1);    // количество результатов, которое мы хотим получить, в данном случае хотим только первый - самый релевантный

        // стартуем Activity и ждем от нее результата
        ownerActivity.startActivityForResult(intent, SystemData.VOICE_RECOGNITION_REQUEST_CODE);
    }

    /**
     * Отправляет Intent с запросом на распознавание речи
     * @param ownerActivity инициировавшая запрос Activity
     */
    private static void startRecognitionActivity(Activity ownerActivity) {

        // создаем Intent с действием RecognizerIntent.ACTION_RECOGNIZE_SPEECH
        Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);

        // добавляем дополнительные параметры:
        intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Голосовой поиск Inforino");    // текстовая подсказка пользователю
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);    // модель распознавания оптимальная для распознавания коротких фраз-поисковых запросов
        intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 1);    // количество результатов, которое мы хотим получить, в данном случае хотим только первый - самый релевантный

        // стартуем Activity и ждем от нее результата
        ownerActivity.startActivityForResult(intent, SystemData.VOICE_RECOGNITION_REQUEST_CODE);
    }



При настройке параметров Intent’а мы указали: строку-подсказку для пользователя, которая отобразиться в диалоге запроса голосового ввода; модель распознавания – поисковый запрос; количество результатов – 1 (самый релевантный, другие нам в данном случае не нужны).


Переходим к реализации функции installGoogleVoiceSearch. Данная функция будет отображать диалог, в котором спросит у пользователя хотел ли они установить Голосовой Поиск Google и отправить его в маркет устанавливать его, если он захочет.




     * Запрашивает разрешение на установку Голосового Поиска Google, отображая диалог. Если разрешение
     * получено - направляет пользователя в маркет.
     * @param ownerActivity Activity инициировавшая установку
     */
    private static void installGoogleVoiceSearch(final Activity ownerActivity) {

        // создаем диалог, который спросит у пользователя хочет ли он
        // установить Голосовой Поиск
        Dialog dialog = new AlertDialog.Builder(ownerActivity)
            .setMessage("Для распознавания речи необходимо установить "Голосовой поиск Google"")    // сообщение
            .setTitle("Внимание")    // заголовок диалога
            .setPositiveButton("Установить", new DialogInterface.OnClickListener() {    // положительная кнопка

                // обработчик нажатия на кнопку Установить
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    try {
                        // создаем Intent для открытия в маркете странички с приложением
                        // Голосовой Поиск имя пакета: com.google.android.voicesearch
                        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.google.android.voicesearch"));
                        // настраиваем флаги, чтобы маркет не попал к в историю нашего приложения (стек Activity)
                        intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
                        // отправляем Intent
                        ownerActivity.startActivity(intent);
                     } catch (Exception ex) {
                         // не удалось открыть маркет
                         // например из-за того что он не установлен
                         // ничего не поделаешь
                     }
                }})

            .setNegativeButton("Отмена", null)    // негативная кнопка
            .create();

        dialog.show();    // показываем диалог
    }


/**
     * Запрашивает разрешение на установку Голосового Поиска Google, отображая диалог. Если разрешение
     * получено - направляет пользователя в маркет.
     * @param ownerActivity Activity инициировавшая установку
     */
    private static void installGoogleVoiceSearch(final Activity ownerActivity) {

        // создаем диалог, который спросит у пользователя хочет ли он
        // установить Голосовой Поиск
        Dialog dialog = new AlertDialog.Builder(ownerActivity)
            .setMessage("Для распознавания речи необходимо установить "Голосовой поиск Google"")    // сообщение
            .setTitle("Внимание")    // заголовок диалога
            .setPositiveButton("Установить", new DialogInterface.OnClickListener() {    // положительная кнопка

                // обработчик нажатия на кнопку Установить
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    try {
                        // создаем Intent для открытия в маркете странички с приложением
                        // Голосовой Поиск имя пакета: com.google.android.voicesearch
                        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.google.android.voicesearch"));
                        // настраиваем флаги, чтобы маркет не попал к в историю нашего приложения (стек Activity)
                        intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
                        // отправляем Intent
                        ownerActivity.startActivity(intent);
                     } catch (Exception ex) {
                         // не удалось открыть маркет
                         // например из-за того что он не установлен
                         // ничего не поделаешь
                     }
                }})

            .setNegativeButton("Отмена", null)    // негативная кнопка
            .create();

        dialog.show();    // показываем диалог
    }



В принципе все готово. У нас есть механизм проверки наличия требуемой нам Activity для распознавания речи, мы можем запустить эту Activity, можем запросить у пользователя разрешения на установку Голосового Поиска и при положительно ответе отправить его в маркет. Единственное что мы еще не умеем – это получать результаты распознавания голоса.


Отправка запроса на распознавание речи была неспроста реализована с помощью функции startActivityForResult, указывающий на то, что мы хотим получить результат запущенной нами ативити. Чтобы его получить требуется в нашей основной ативити, из которой мы отправили запрос на распознавание речи, переопределить метод OnActivityResult и получить в нем результаты распознавания голоса. Делается это следующим образом:




Голосовать: 
0
Голосов пока нет