Некоторое время назад я уже писал про одну из своих разработок — технологию PhonoPaper и одноименную программу, позволяющую играть звук, напечатанный в виде спектрограммы на бумаге или любой другой поверхности. Процесс выглядит примерно так: 10-секунд звука (голос, кусок песни) преобразуются в картинку специального формата. Картинка распечатывается и, к примеру, клеится на стену. Прохожий, заметив код, запускает PhonoPaper-сканер на телефоне, наводит камерой на картинку и в тот же миг начинает слышать звук, закодированный в ней. При этом пользователь полностью вовлечен в процесс — от движения его руки зависит направление и скорость воспроизведения (хотя имеется и автоматический режим). Вся необходимая информация хранится в изображении, выход в Интернет не требуется.
PhonoPaper вызвал живой интерес в среде музыкантов, художников и просто любителей необычных экспериментов. А в 3 квартале прошлого года приложение заняло первое место в проекте «Рейтинг Intel для разработчиков» на сайте Apps4App.ru. В связи с чем Intel любезно предоставила мне планшет на базе Android x86 для дальнейшего улучшения и оптимизации PhonoPaper. Я поспешил этим воспользоваться, а о проделанной работе и результатах расскажу далее.
Захват видео
Первое, что было сделано — это подключен набор библиотек Intel INDE Media for Mobile. А конкретно — класс GLCapture для захвата видео c OpenGL ES поверхности в реальном времени (в HD-качестве и со звуком). Зачем это нужно? Во-первых, сам процесс поиска и проигрывания PhonoPaper-кодов — это fun, захватывающее зрелище, напоминающее игру на каком-то необычном музыкальном инструменте. Во-вторых, PhonoPaper может работать в свободном режиме, когда в звук преобразуется все без разбора — в том числе ваш ковер и кошка. И то и другое было бы здорово записывать и выкладывать на YouTube.
PhonoPaper-коды, нарисованные от руки
Свободный режим — любое изображение с камеры воспринимается, как спектр звука.
Процесс подключения GLCapture неоднократно описан в разных статьях. Расскажу лишь о нескольких моментах, о которых желательно знать перед началом работы.
- Версия Android должна быть не меньше 4.3. Для старых устройств я сделал кросс-платформенный MJPEG рекордер, скорость и качество которого, конечно же, сильно уступает hardware-accelerated GLCapture, пишущего в mp4.
- Приложение должно быть построено на базе OpenGL ES 2.0. Мои программы исторически использовали версию 1.1, поэтому код пришлось переписывать. Но переход на GLES 2.0 в конечном итоге положительно сказался на производительности, т. к. появилась возможность вручную настраивать шейдеры.
- GLCapture может писать звук с микрофона. Это хорошо, если вам нужно сопровождать видео своими комментариями. Если же требуется звук высокого качества непосредственно из приложения, то придется записывать его отдельно в файл, а потом объединять с mp4. Для объединения можно использовать класс MediaComposer с эффектом SubstituteAudioEffect из комплекта Media for Mobile. Другой путь — запись в WAV, кодирование из WAV в AAC, добавление AAC-дорожки в mp4-файл при помощи библиотеки mp4parser.
Так как PhonoPaper написан на языке программирования Pixilang, то функция захвата видео в дальнейшем распространится на другие pixilang-based приложения (PixiTracker, PixiVisor, Nature — Oscillator, Virtual ANS), а главное — будет доступна для всех разработчиков, использующих Pixilang. При этом доступна очень легко (всего несколько функций: для запуска захвата, для остановки и сохранения).
Intel C++ и оптимизация
Следующий шаг — сборка x86 Android-версии PhonoPaper при помощи компилятора Intel (версия 15.0.0) и сравнение результатов с GCC 4.8. Я пользователь Debian Linux и довольно старой версии к тому же. Поэтому первой проблемой стало найти соответствующую версию Intel C++. Почему-то большая часть ссылок вела на проект Intel INDE, внутри которого имеется нужный компилятор, но только для Windows и OS X. Это показалось странным… К счастью, нужный дистрибутив таки был найден — это Intel System Studio 2015. И не смотря на предупреждения при установке, все заработало и первая же сборка прошла успешно.
Компиляция выполнялась со следующими ключами: -xATOM_SSSE3 -ipo -fomit-frame-pointer -fstrict-aliasing -finline-limit=300 -ffunction-sections -restrict. Для проверки производительности виртуальной машины Pixilang (она в основе всех моих приложений) были написаны небольшие тесты, исходники и результаты которых можно посмотреть в этом архиве. В итоге, даже без предварительной подготовки, некоторые куски кода были ускорены в 5 (!) раз. Довольно впечатляющие результаты!
В PhonoPaper большая часть нагрузки идет на функцию спектрального синтезатора (таблично-волнового, не FFT) — wavetable_generator(). Для нее был написан отдельный тест, который в течение четырех секунд рендерит поток звука со случайным спектром. В конце тест выдает максимально возможную частоту дискретизации. Увы, здесь ICC справился хуже: 105 кГц против 100 кГц на GCC. Добавляем при компиляции ключ -qopt-report=2 и в отчете видим сообщение:
loop was not vectorized: vector dependence prevents vectorization.
Основной цикл внутри нашей функции не удалось векторизовать, т. к. указатели входных данных могут указывать на пересекающиеся участки памяти:
int* amp = (int*)amp_cont->data
int* amp_delta = (int*)amp_delta_cont->data;
Как разработчик, я вижу, что в данном месте пересечение исключено и об этом просто нужно сообщить компилятору. В C/C++ существует специальное ключевое слово restrict, сообщающее о том, что объявляемый указатель указывает на блок памяти, на который не указывает никакой другой указатель. Поэтому вышеприведенный код заменяем на такой:
int* restrict amp = (int*)amp_cont->data;
int* restrict amp_delta = (int*)amp_delta_cont->data;
После чего еще раз собираем приложение и видим, что цикл успешно векторизован. С учетом некоторых дополнительных изменений (в процессе оказалось, что можно избавиться от нескольких битовых операций) имеем результат — 190 кГц. GCC с учетом тех же изменений выдал 130 кГц. Получаем прирост производительности в 1.46 раз!
Что дальше
Как видим, результаты весьма позитивные! PhonoPaper стал быстрее (во многом благодаря компилятору Intel C++) и обрел функционал захвата видео. Кроме того, запись видео появится в виде нескольких простых функций в ближайшем обновлении Pixilang 3.6.
Для тех, кто не в курсе, Pixilang — это открытый кроссплатформенный язык программирования, заточенный на работу со звуком и графикой. Синтаксис языка весьма минималистичен и представляет собой некий гибрид Бейсика и Си, что вкупе с другими особенностями (возможность писать код без функций, универсальные контейнеры для хранения любых данных) снижает порог вхождения.
Автор: NightRadio