Yandex слышит тебя, dude

    image
    Неожиданно пришел приказ — написать приложение под iOS, использующее Yandex Speechkit для распознавания русской речи. Точнее, для распознавания коротких фраз на произвольную тему. Цель задания — сравнить успехи яндекс-двигателя с нашим, саровским движком.

    Приказали — сделал следующие шаги.

    1. Зашел на yandex.ru в раздел распознавание речи
    2. Зарегистрировался и получил ключ, он же API_KEY
    3. Отправил письмо в yandex с просьбой активировать ключ


    На вопрос, как будет использоваться ключ, я ответил, что выпускаю карточную игру Diablo 3-13, управляемую голосом.

    Через два дня ключ активировали. Я поначалу нетерпеливо бил копытом, затем понял, что в yandex работают вдумчивые, синхронные сотрудники.
    В своем приложении в дальнейшем я также отказался от асинхронных запросов к yandex.api.

    Получив в свое распоряжение волшебный ключ, он же API_KEY, я скачал по указанной ссылке архив.
    YandexSpeechKit-2.1-ios.zip


    В архиве содержится два проекта, демонстрирующих работу библиотеки.
    Собрав оба примера, я заменил в тексте SAMPLE_API_KEY на свой и запустил приложения.
    Оба не работают под Xcode 5.1.1, вылетают по какой-то внутренней ошибке, спрятанной глубоко в недрах библиотеки.

    Пришлось загрузить актуальную SDK c github.

    Cкачал по указанной выше ссылке архив
    yandex-speechkit-ios-master.zip

    собрал примеры, но ошибка не исчезла.

    Я немедленно отправил письмо-диагностику в службу поддержки и, в ожидании ответа, написал очередную игрушку.
    Прошло пару дней, ответа от службы не было.

    Выложив игрушку в маркет, я решил написать собственное iOS-приложение, используя url-запросы к яндекс-сервису распознавания речи.

    Ведь волшебный ключ можно использовать тот же.

    Шаг первый — проверка командной строкой


    В командной строке надо подавать wav файлы с заранее записанными фразами.

    Запрос выглядит просто, как бамбук
       curl -v -4 "asr.yandex.net/asr_xml?key=e547b4f5-хрен-вам-ключ-97130fdbcd74&uuid=01ae13cb744628b58fb536d496daa177&topic=notes&lang=ru-RU" -H "Content-Type: audio/x-wav" --data-binary "@recordedFile.wav"
     


    В комментарии запрос не нуждается, все сделано в точности с великолепной документацией на сайте yandex.
    С первого раза запрос не прошел, поскольку вместо 32-цифрового uuid я подсунул udid своего iPhone. А в нем не только HEX.

    Фразы типа
    тридцать восемь попугаев
    пойдем-ка покурим-ка
    Владимир Сысоев
    чо смотришь дубина давай помогай
    кто не пашет тот сачок
    Антон Субботин
    Хабрахабр — полный улет


    Распознались великолепно, в исполнении разных дикторов.

    image

    К моему удовольствию, срамные слова яндекс беспощадно вырезает.

    Шаг второй — собираем iOS-приложение, где записывается речь


    Есть стандартный проект на сайте apple, в котором демонстрируется запись/проигрывание звука.
    Скачиваем проект SpeakHere, запускаем — все в порядке. Уважаю я этих ребят из Купертино, пусть индусы. Код, конечно у них, хмм, но работает же.

    Модифицируем файл SpeakHereController.mm

    Заходим в функцию — (void)stopRecord и дописываем одну строчку

     - (void)stopRecord
    {
       // здесь ничего не трогаем
    ...
      // btn_play.enabled = YES;
        
      // эту строчку добавляем
        [self yandexTool];
    }
    

    Понятно, что мы добавили вызов функции, обрабатывающей сформированный во время звукозаписи аудиофайл.
    Изначально в проекте звук записывается в файл recordedFile.caff

     recordFilePath = (CFStringRef)[NSTemporaryDirectory() stringByAppendingPathComponent: @"recordedFile.caff"];
    

    Яндекс не умеет работать с файлами такого типа, поэтому по всему файлу SpeakHereController.mm расширитель имени надо заменить на

     recordFilePath = (CFStringRef)[NSTemporaryDirectory() stringByAppendingPathComponent: @"recordedFile.wav"];
    

    Кроме того в файле проекта AQRecorder.mm в теле функции void AQRecorder::StartRecord(CFStringRef inRecordFile) надо поменять параметр в строке

    		OSStatus status = AudioFileCreateWithURL(url, kAudioFileCAFType, &mRecordFormat, kAudioFileFlags_EraseFile, &mRecordFile);
    

    на

    		OSStatus status = AudioFileCreateWithURL(url, kAudioFileWAVEType, &mRecordFormat, kAudioFileFlags_EraseFile, &mRecordFile);
    

    И последнее — яндекс понимает звуковые файлы, записанные на частоте 16000. У Apple по умолчанию частота — 44100. Надо менять.

    В файле проекта AQRecorder.mm в теле функции void AQRecorder::SetupAudioFormat(UInt32 inFormatID) добавляем строчку

       Float64 newRate = 16000;
       XThrowIfError(AudioSessionSetProperty(	kAudioSessionProperty_PreferredHardwareSampleRate,
                                              sizeof(newRate),
                                              &newRate), "couldn't set hardware sample rate");
     

    Все, осталось вставить функцию запроса к серверу яндекс. В запросе будем аналогично командному запросу подавать файл recordedFile.wav
    Привожу ниже текст функции yandexTool, простой как колея от трактора Беларусь.

    -(void) yandexTool
    {
        NSString *urltext_temp = [NSString stringWithFormat:@"https://asr.yandex.net/asr_xml?key=%@&uuid=%@&topic=queries&lang=ru-RU", API_KEY, API_UUID];
        NSString* urltext =
        [urltext_temp stringByAddingPercentEscapesUsingEncoding:
         NSUTF8StringEncoding];
        NSLog(@"url=%@", urltext);
        NSURL *url = [NSURL URLWithString: urltext];
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        [request setURL:url];
        [request setHTTPMethod:@"PUT"];
        [request setValue:@"audio/x-wav" forHTTPHeaderField:@"Content-Type"];
        NSString *filePath=[NSTemporaryDirectory() stringByAppendingPathComponent: @"recordedFile.wav"];
        NSData *myData = [NSData dataWithContentsOfFile:filePath];
        request.HTTPBody = myData;
     
       NSError *error;
        NSURLResponse *response;
        NSData *data2 = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
        NSString *responseString = [[NSString alloc] initWithData:data2 encoding:NSUTF8StringEncoding];
        NSLog(@"responseString=%@",responseString);
     //  Ответ возвращается в XML - разбор ответа сами сделаете, чай не маленькие
     //   NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data2];
     //   [parser setDelegate:self];
     //   [parser parse];
    }
     

    Свое приложение я еще немного модифицировал — добавил собственную морду и вывод распознанного текста.

    Распознает, надо сказать обалденно хорошо.

    image

    Я, как обещал яндексу, вставил распозновалку в карточную игру King of Hearts, но задержка в 1-2 секунды при управлении голосом начинает раздражать через 5 минут после начала игры.

    Тем не менее, ни одной ошибки распознавания названия игральной карты за время игры не произошло.
    Браво, Яндекс!

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

    Надо, наверное, ответить им.
    Papa Buba Diop 64,53
    Компания
    Поделиться публикацией
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 27
      +5
      ни одной ошибки распознавания названия игральной карты за время игры не произошло
      «Трифоновой король», как на картинке, больше не появлялся?
        +1
        Да, трифоновый это было. Но в игре я парсил слоги чер-, чир-, буб-, кре-, кри-, пик-, треф-, триф, троф-, кор-, кар-, да-, вал-, вол-, ту-.

        Числительные не проверял — Яндекс их возвращает в цифрах, если находит.

        И, честно говоря, в игре не произносит масти прилагательными — говорил семь трефей. Или семь треф. Вместо трефей иногда приходил трофей, трифона не было)
          +4
          А расскажи про саровский движок, а? Особенно интересно, что там было ещё в 12-м. Насколько я помню, у вас какие-то нереальные лайфхаки использовались для длинных текстов.
            +1
            Не могу — коммерческая тайна.
        0
        А нет софта чтобы записанный звук на своём компе распознавать? Чтобы стенограммы всякие из записи делать, короче. Скорость распознавания практически некритична, хоть пусть час голоса за день распознаёт.
          +1
          Google — движок распознает свыше 80 процентов русской речи и для некоммерческого использования бесплатен (был). Но я им лично не пользовался, лишь читал в обзорах и наблюдал у коллеги год назад. Подробностей не знаю, может кто в комментариях разъяснит.
            +1
            Движок распознавания Google официально нигде не описан, то есть по сути нет у них API, но дотошный народ его раскопал.
            Купить гугловое распознавание тоже нельзя, просто нет нигде формы с кнопкой КУПИТЬ.
            В версии V1 Google ASR все было бесплатно, пару мес. назад V1 закрыли и появилась версия 2, там стал нужен API ключ, который можно получить у гугла, но с ограничением в 50 запросов на распознавание в день, получить больше лимит просто нереально, ссылка на это ведет в Google Drive который доступен только под корпоративным акаунтом.
              +1
              Гугловское распознавание полностью бесплатно и неограниченно для сторонних приложениях только под Android…
                0
                Ключевое слово: только под Android, под Windows и Linux если использовать www.google.com/speech-api/v2/recognize то там ограничения и нет официальной документации о существовании этого API
              0
              Скажите, из того что использовали для распознавания, что оказалось точнее всего? Неужели правда стоит переходить на Яндекс?) Не пробовали Nuance Dev?
                +1
                Хоть вопрос и не ко мне, но на iphone-ipad яндекс показывает себя очень неплохо для поисковых запросов, хорошо так же распознает и не очень распространенные русские фамилии. Из аналогов, работающих по статистической модели и поддерживающий русский — Nuance Dragon Dictation — дороже и зачастую не может распознать (что естественно) имена и фамилии.
                Еще есть Nuance VoCon Hybrid — там гибридная модель распознавания — распознается одновременно на устройстве по грмматике и в облаке (диктовка) — качество повышается в разы. Но очень сложная интеграция. Вроде как они обещались запустить VoCon Hi — должно стать резко проще.
                Для задачи, описанной в статье, не обязательно использовать диктовку. Можно было бы использовать распознавание по грамматике, например, проект с открытым кодом pocketsphinx и его порт под ios. Точность повышалась бы до 90-95 процентов. И при этом не нужен был бы сервер.
            +2
            Да, кстати, а почему вы хотели использовать именно серверный яндекс??
            Для вашей задачи можно было бы прикрутить тот же poscketsphinx. www.politepix.com/openears/ Написать простую грамматику, которая распознавалась бы с точностью 95% и со скоростью в несколько десятков миллисекунд без сервера прямо на устройстве.
              0
              Спасибо, morfeusys, прикручу грамматику и попробую.
                0
                Но только учтите — это требует некоторого погружения в тему распознавания — нужно будет все правильно сконфигурить, подсунуть русскую аккустическую модель, разобраться с синтаксисом транскрипций для слов в словаре и тд. В этом плане это не просто сделать запрос и получить ответ от сервера.
                  +2
                  Скажите, а вы бы не могли написать топик на хабре про это? Интересная тема, да ещё и с практическим применением.
                    +1
                    В принципе мог бы. Но я могу компетентно описать весь этот процесс только под Android… Если это будет интересно…
                      0
                      Будет, конечно.
                        +3
                        Ну вот собственно я написал запрошенный вами топик — http://habrahabr.ru/post/237589/
                        Надеюсь, будет полезен.
                          0
                          Спасибо, уверен, хабросообщество также оценит.
                    0
                    В нашем движке используется акустическая модель с похожим синтаксисом, что идеально для сравнительного анализа работы библиотек.
                      0
                      А о каком вашем движке вы говорите? В смысле, в статье я увидел только описание работы с яндекс speechkit.
                        0
                        Да есть тут, за соседней стенкой сидят, вышли из Intel. Для американцев делали распознавание на английском и китайском, теперь пилят русский язык. Больше сказать не могу.
                          0
                          Скажем так — не синтаксис аккустической модели, а формализм для описания грамматики. Скорее всего ваша система использует jsgf. Но для описания транскрипций используется либо worldbet, либо еще что-то.
                            0
                            Пришлось повозиться — в нашем корпусе синтаксис слогов русский, а в открытые-уши синтаксис — английский, что вызывает трудности для многих фонем.
                +2
                Спасибо за интересную и подробную статью об интеграции нашего сервиса распознавания :)
                Можете написать, с какой ошибкой вылетает пример? У меня такая же нога, и не болит версия Xcode, и примеры стабильно работают на разных девайсах и симуляторах.
                Кстати, задержку можно существенно уменьшить, если отправлять звук на сервер по мере его записи (Chunked transfer encoding). SpeechKit именно так и делает.
                  0
                  Привет, про задержку я знаю (использую тег chunk в заголовке запроса). Я написал ответ с кодом ошибки в службу поддержки yandex, можно ли общаться напрямую?
                  0
                  Да, получил ваш ответ. Давайте общаться через техподдержку — обычно так удобнее.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое