Программируем арт: openFrameworks — первый контакт
Когда вы последний раз программировали на C++?
Может быть это ваша каждодневная работа, а мой последний (до вчерашнего дня) проект на С++ был в далеком 2000 году — дипломный проект на базе Visual Studio 4.2 (хорошая, кстати, система была), и с тех пор перешёл в веб-разработку — скриптовые языки.
То есть сейчас я — начинающий на C++, но это не помешало мне за пару часов развернуть инфраструктуру, сделать и собрать мультимедийное приложение на C++, которое визуализирует музыку с разными эффектами. И в этом мне помогли:
открытый фреймворк для создания интерактивных приложений — openFrameworks бесплатное IDE Code: Blocks Что получилось — можно посмотреть в спойлере, ниже.А начиналось всё так — после очередного прослушивания музыки от одного композитора из Самары, я подумал — было бы интересно попробовать сделать визуализацию музыки, и обратился к Денису Перевалову (кто не первый год занимается созданием разнообразных интерактивных арт/перформанс систем) — он мне ответил, что это делается без проблем на базе openFrameworks и что в примерах к его книге (а он автор книги по openFramework), есть реализация такой задачи.
То есть мне нужно было всего лишь — установить фреймворк, доработать и собрать пример на С++… Об этом процессе — установке, настройки, и кратком описании openFrameworks и будет эта статья.
openFrameworks — это система с помощью которой можно запрограммировать интерактивное мультимедийное приложение, то есть арт, перформансы и т.п., она бесплатная, открытая и кроссплатформенная система (linux, mac, win), и так же есть версии для ARM (к примеру для RPi), и сборки для iPhone и Android.
Кстати на КДПВ — одна из инсталляций на базе openFrameworks (Семь Видеогидов. выставлено на ВДНХ в экспозиции Политехнического музея. Москва, 2014).
Что же такое openFrameworks? Это набор модулей — для интеграции с Arduino, с кинектом, с системой распознавания образов OpenCV, рисование 3д графики, работа со звуком, камерами и т.п. с помощью которых можно сделать интерактивное приложение. И всё это на базе C++.В поле моего зрения openFrameworks попала, когда я вышел на роботизированные инсталляции созданные с её помощью.
План статьи такой:
1. Настройка openFrameworks 2. Основные принципы openFrameworks приложения 3. Тестовый пример 1. Настройка openFrameworks Следующие шаги: установка openFrameworks (для CodeBlocks) установка IDE CodeBlocks копирование библиотек openFrameworks для CodeBlocks компилятора Установка openFrameworks Согласно этой страничке download я выбрал версию openFrameworks, для моей ОС и для IDE на которой я планировал работать.
В моем случае win и code: blocks: скачиваем архив of_v0.8.4_win_cb_release.zip
Распаковываем, архив содержит следующие папки:* addons* apps* docs* examples* export* libs* other* projectGenerator* scripts
Это C++ библиотеки openFrameworks, примеры, аддоны и т.п.
Для того чтобы создавать openFrameworks приложение, лучше использовать IDE среду.
Установка IDE CodeBlocks В качестве IDE, я решил выбрать code: blocks (visual studio всё таки великовата будет для меня сейчас)CodeBlocks — это бесплатная и открытая IDE, созданная на базе кроссплатформенной GUI библиотеки wxWidgets. Согласно этой странице openframeworks.cc/setup/codeblocks/ скачиваю IDE CodeBlocks. Версия Release 12.11 Отсюда. Эта сборка идёт вместе с MinGW — открытой средой разработки под win платформу.
Вот так выглядит IDE CodeBlocks
Копирование библиотек openFrameworks для CodeBlocks компилятора Важный пункт — для того чтобы из IDE CodeBlocks, успешно собирались openFrameworks проекты, необходимо скопировать дополнительные файлы в MinGW.Вот этот пункт.
Скачиваем Additions for Code: Blocks to work with openFrameworks zip архив.
Распаковываем во временной папке, и копируем в соответствующие папки в установленном CodeBlocks, согласно этой инструкции:
Add the contents of the folder «add_to_codeblocks_mingw_include» into »…\CodeBlocks\MinGW\include«Add the contents of the folder «add_to_codeblocks_mingw_lib» into »…\CodeBlocks\MinGW\lib»
Всё, теперь мы готовы к сборке openFrameworks проектов!2. Основные принципы openFrameworks приложения Сборка тестового проекта Откроем тестовый проект, для этого выберем со стартовой страницы IDE CodeBlocks выберем «Open an existing project…» (или в File — Import Project — Dev-C++ project… — и выбрав тип файлов *.*)Переходим в папку где мы развернули openFrameworks, заходим в examples/empty/emptyExample, и открываем файл проекта emptyExample.
Вот так выглядит IDE после открытия проекта:
Попробуем сразу же стартовать проект — на картинке указана стрелкой иконка или нажать F9 — RUN.
Если приложение не собрано, то будет стартована сборка (после вашего подтверждения) и по окончании сборки — приложение стартуется.
Если всё настроено верно, то по окончании процесса сборки будет открыто консольное окно, и мы увидим это окно:
Поздравляю! Значит всё настроено верно. И в папочке bin появилось приложение emptyExample.exe, которое вы можете уже запускать независимо.
Файлы Теперь посмотрим на файлы нашего emptyExample проекта, они находятся в папке src:* main.cpp* ofApp.h* ofApp.cppФайл main.cpp:
#include «ofMain.h» #include «ofApp.h»
//======================================================================== int main (){
ofSetupOpenGL (1024,768, OF_WINDOW); // <-------- setup the GL context
// this kicks off the running of my app // can be OF_WINDOW or OF_FULLSCREEN // pass in width and height too: ofRunApp (new ofApp ());
} В нем определяется окно нашего приложения, и далее создаётся экземпляр класса ofApp.Файл ofApp.h:
#pragma once
#include «ofMain.h»
class ofApp: public ofBaseApp{ public: void setup (); void update (); void draw (); void keyPressed (int key); void keyReleased (int key); void mouseMoved (int x, int y); void mouseDragged (int x, int y, int button); void mousePressed (int x, int y, int button); void mouseReleased (int x, int y, int button); void windowResized (int w, int h); void dragEvent (ofDragInfo dragInfo); void gotMessage (ofMessage msg); }; Здесь определяется класс наш класс ofApp, наследуется от ofBaseApp. И методы.Основной класс приложения ofApp.cpp:
#include «ofApp.h»
//-------------------------------------------------------------- void ofApp: setup (){
}
//-------------------------------------------------------------- void ofApp: update (){
}
//-------------------------------------------------------------- void ofApp: draw (){
}
//-------------------------------------------------------------- void ofApp: keyPressed (int key){
}
//-------------------------------------------------------------- void ofApp: keyReleased (int key){
}
//-------------------------------------------------------------- void ofApp: mouseMoved (int x, int y){
}
//-------------------------------------------------------------- void ofApp: mouseDragged (int x, int y, int button){
}
//-------------------------------------------------------------- void ofApp: mousePressed (int x, int y, int button){
}
//-------------------------------------------------------------- void ofApp: mouseReleased (int x, int y, int button){
}
//-------------------------------------------------------------- void ofApp: windowResized (int w, int h){
}
//-------------------------------------------------------------- void ofApp: gotMessage (ofMessage msg){
}
//-------------------------------------------------------------- void ofApp: dragEvent (ofDragInfo dragInfo){
} Как мы видим — ничего не реализовано, мы увидели просто пустое, но работающее openFrameworks приложение.
Цикл работы openFrameworks приложения Основными методами нашего класса являются: void setup (); void update (); void draw (); Архитектура любого openFramewors приложения следующая:
В методе setup прописываются настройки, подготовка ресурсов и т.п. Этот метод выполняется один раз при запуске приложения, перед началом основного цикла.
Основной цикл это update и draw, где в первом методе — происходят только расчеты, а во втором draw — рисование. И после этого цикл повторяется.
Выход происходит по нажатию Esc.
3. Тестовый пример Вот мы подошли к нашей задаче — визуализации музыки.На этом сайте представлены примеры к книге «Mastering openFrameworks: Creative Coding Demystified». Сами файлы можно бесплатно скачать с карточки книги (после регистрации).
Вот видео примеров.
Базовый пример Dancing Cloud И вот тот пример, что я хотел взять за основу и модифицировать — называется Dancing Cloud (06-Sound/06-DancingCloud):[embedded content]Я скачал этот пример, и распаковал архив в корне моей openFrameworks папки — это важно, т.к. папка проекта должна находиться на 2 уровня ниже.
Вот ВЕСЬ исходный код, проекта 06-Sound/06-DancingCloud:
main.cpp:
Скрытый текст #include «testApp.h» #include «ofAppGlutWindow.h»
//-------------------------------------------------------------- int main (){ ofAppGlutWindow window; // create a window // set width, height, mode (OF_WINDOW or OF_FULLSCREEN) ofSetupOpenGL (&window, 1024, 768, OF_WINDOW); ofRunApp (new testApp ()); // start the app } testApp.h:
Скрытый текст #pragma once
#include «ofMain.h»
/* This example draws points cloud and plays music track. Also it analyzes music spectrum and use this data for controlling the radius and shuffle of the cloud.
It’s the example 06-DancingCloud from the book «Mastering openFrameworks: Creative Coding Demystified», Chapter 6 — Working with Sounds
Music track «surface.wav» by Ilya Orange (soundcloud.com/ilyaorange)
*/
class testApp: public ofBaseApp{ public: void setup (); void update (); void draw (); void mousePressed (int x, int y, int button);
ofSoundPlayer sound; //Sound sample
void keyPressed (int key); void keyReleased (int key); void mouseMoved (int x, int y); void mouseDragged (int x, int y, int button); void mouseReleased (int x, int y, int button); void windowResized (int w, int h); void dragEvent (ofDragInfo dragInfo); void gotMessage (ofMessage msg); }; testApp.cpp
Скрытый текст #include «testApp.h»
const int N = 256; //Number of bands in spectrum float spectrum[ N ]; //Smoothed spectrum values float Rad = 500; //Cloud raduis parameter float Vel = 0.1; //Cloud points velocity parameter int bandRad = 2; //Band index in spectrum, affecting Rad value int bandVel = 100; //Band index in spectrum, affecting Vel value
const int n = 300; //Number of cloud points
//Offsets for Perlin noise calculation for points float tx[n], ty[n]; ofPoint p[n]; //Cloud’s points positions
float time0 = 0; //Time value, used for dt computing
//-------------------------------------------------------------- void testApp: setup (){ //Set up sound sample sound.loadSound («surface.wav»); sound.setLoop (true); sound.play ();
//Set spectrum values to 0
for (int i=0; i //Initialize points offsets by random numbers
for (int j=0; j //--------------------------------------------------------------
void testApp: update (){
//Update sound engine
ofSoundUpdate (); //Get current spectrum with N bands
float *val = ofSoundGetSpectrum (N);
//We should not release memory of val,
//because it is managed by sound engine //Update our smoothed spectrum,
//by slowly decreasing its values and getting maximum with val
//So we will have slowly falling peaks in spectrum
for (int i=0; i //Update particles using spectrum values //Computing dt as a time between the last
//and the current calling of update ()
float time = ofGetElapsedTimef ();
float dt = time — time0;
dt = ofClamp (dt, 0.0, 0.1);
time0 = time; //Store the current time //Update Rad and Vel from spectrum
//Note, the parameters in ofMap’s were tuned for best result
//just for current music track
Rad = ofMap (spectrum[ bandRad ], 1, 3, 400, 800, true);
Vel = ofMap (spectrum[ bandVel ], 0, 0.1, 0.05, 0.5); //Update particles positions
for (int j=0; j //--------------------------------------------------------------
void testApp: draw (){
ofBackground (255, 255, 255); //Set up the background //Draw background rect for spectrum
ofSetColor (230, 230, 230);
ofFill ();
ofRect (10, 700, N * 6, -100); //Draw spectrum
ofSetColor (0, 0, 0);
for (int i=0; i //Draw cloud //Move center of coordinate system to the screen center
ofPushMatrix ();
ofTranslate (ofGetWidth () / 2, ofGetHeight () / 2); //Draw points
ofSetColor (0, 0, 0);
ofFill ();
for (int i=0; i //Draw lines between near points
float dist = 40; //Threshold parameter of distance
for (int j=0; j //Restore coordinate system
ofPopMatrix ();
} //--------------------------------------------------------------
void testApp: keyPressed (int key){
} //--------------------------------------------------------------
void testApp: keyReleased (int key){ } //--------------------------------------------------------------
void testApp: mouseMoved (int x, int y){ } //--------------------------------------------------------------
void testApp: mouseDragged (int x, int y, int button){ } //--------------------------------------------------------------
void testApp: mousePressed (int x, int y, int button){ } //--------------------------------------------------------------
void testApp: mouseReleased (int x, int y, int button){ } //--------------------------------------------------------------
void testApp: windowResized (int w, int h){ } //--------------------------------------------------------------
void testApp: gotMessage (ofMessage msg){ } //--------------------------------------------------------------
void testApp: dragEvent (ofDragInfo dragInfo){ }
Комментарий от Дениса (автора книги), по поводу алгоритма, что визуализирует музыку:
300 точек (const int n = 300;) движутся по траекториям шума Перлина, причем соседние точки соединяются отрезками.Радиус облака и скорость движения — это два параметра, которые берутся из анализа звука. Анализ звука такой: исходный звук превращается в спектр (с помощью оконного преобразования Фурье). Выбираются два значения спектра, которые и становятся двумя параметрами, управляющими движением облака точек. Эти две частоты показаны на спектре чёрным цветом.
Смотрим отличия от нашего emptyExample.main.cpp — идентичен по сути. В testApp.h, добавился атрибут sound, класса ofSoundPlayer:
ofSoundPlayer sound; //Sound sample
ofSoundPlayer — это базовый класс для работы со звуком, docs.Самое интересное находится в testApp.cpp. Вот переменные, что используются для реализации логики:
const int N = 256; // Число полос спектра
float spectrum[ N ]; // массив для значений спектра
float Rad = 500; // радиус облака
float Vel = 0.1; // параметр скорости точек облака
int bandRad = 2; // полоса спектра что будет модифицировать Rad параметр
int bandVel = 100; // полоса спектра что будет модифицировать Vel параметр const int n = 300; // число точек в облаке // рассчитанные смещения точке согласно шума Перлина
float tx[n], ty[n];
ofPoint p[n]; // координаты точек облака float time0 = 0; // используется для вычисления dt — прошедшего времени между отображениями
Вот что прописано в методе testApp: setup () происходит инициализация музыки, переменных для отображения спектра, и точек облака:
void testApp: setup (){
//Set up sound sample
sound.loadSound («surface.wav»);
sound.setLoop (true);
sound.play (); //Set spectrum values to 0
for (int i=0; i //Initialize points offsets by random numbers
for (int j=0; j Скрытый текст
void testApp: update (){
//Update sound engine
ofSoundUpdate (); //Get current spectrum with N bands
float *val = ofSoundGetSpectrum (N);
//We should not release memory of val,
//because it is managed by sound engine //Update our smoothed spectrum,
//by slowly decreasing its values and getting maximum with val
//So we will have slowly falling peaks in spectrum
for (int i=0; i //Update particles using spectrum values //Computing dt as a time between the last
//and the current calling of update ()
float time = ofGetElapsedTimef ();
float dt = time — time0;
dt = ofClamp (dt, 0.0, 0.1);
time0 = time; //Store the current time //Update Rad and Vel from spectrum
//Note, the parameters in ofMap’s were tuned for best result
//just for current music track
Rad = ofMap (spectrum[ bandRad ], 1, 3, 400, 800, true);
Vel = ofMap (spectrum[ bandVel ], 0, 0.1, 0.05, 0.5); //Update particles positions
for (int j=0; j //Draw background rect for spectrum
ofSetColor (230, 230, 230);
ofFill ();
ofRect (10, 700, N * 6, -100); //Draw spectrum
ofSetColor (0, 0, 0);
for (int i=0; i //Draw cloud //Move center of coordinate system to the screen center
ofPushMatrix ();
ofTranslate (ofGetWidth () / 2, ofGetHeight () / 2); //Draw points
ofSetColor (0, 0, 0);
ofFill ();
for (int i=0; i //Draw lines between near points
float dist = 40; //Threshold parameter of distance
for (int j=0; j //Restore coordinate system
ofPopMatrix ();
}
Мои модификации
Я взял музыку volfworks: soundcloud.com/volfworksАвтор любезно согласился на мое использование его композиции Звезда. Первым дело — я заменил wav на mp3 — openFrameworks поддерживает mp3. Так же сделал потоковое воспроизведение (иначе все 8Мб должны быть сразу загружены — добавил true вторым параметром, docs).
sound.loadSound («zvezda.mp3», true);
Добавил загрузку фонового изображения:
stars.loadImage («stars.jpg»);
Поменял цветовую гамму, сделал эффекты прозрачности зависимые от времени.Фрагмент из ofApp: draw ():
// включение использования прозрачности, и рисование квадрата поверх фоновой картинки
// с прозрачностью определяемой bg_transparent
ofEnableAlphaBlending ();
ofSetColor (0, 0, 0, bg_transparent);
ofRect (0, 0, 1000, 700);
ofDisableAlphaBlending (); // рисование текста указанного цвета, в координатах
ofSetHexColor (0×606060);
ofDrawBitmapString («Music by: volfworks», 800,610);
Весь проект выложен на github: github.com/nemilya/of_volfworks_example Создание видео
В этом возникли некоторые сложности, и в конечном итоге было выполнено с помощью «Camtasia Recorder».Ссылки
Основной сайт проекта: openframeworks.cc/ там предоставлены хорошие туториалы.Если вы работаете с openFrameworks, или интересно попробовать, то приглашаю в русскоязычную группу по openFrameworks. Если вы из Екатеринбурга, и сегодня (27 ноября 2014) будете вечером в районе УрФУ (Ленина, 70), то сможете попасть на бесплатный семинар «Современные технологии в медиаискусстве и перформансах: технологии, тенденции, примеры» по openFrameworks который будет в 18:30. Подробности. Лекцию проведёт сам Денис Перевалов.