Мой визуализатор музыки

    Давно хотел написать какой-никакой визуализатор музыки, но интересных идей не было. Потом увидел вот это — Аудио игра «Devil's Tuning Fork» и захотел сделать нечто похожее.

    Введение


    Писать решил на языке Processing, чтобы заодно посмотреть, что это за зверь.
    В папке с языком валяется множество примеров и, что более важно, присутствует библиотека для работы со звуком, в которой уже реализовано FFT. Есть даже более важный для нас пример, где частоты делятся на три группы и на экране три слова прыгают под ритм музыки (пример называется FrequencyEnergy).

    Демонстрация работы




    Код


    Создаем новый проект, который в терминах Processing'a называется скетч. Скетч будет состоять из трех файлов. Первый — BeatListener, который мы просто перетянем из примера FrequencyEnergy, он нужен чтобы детектить ритм музыки. Второй — класс нашего кубика, выглядит он так:
    class Box {<br> //позиция в пространстве<br> int x,y,z;<br> //размер<br> int boxSize;<br> //яркость (от 0 до 255)<br> int bright;<br> <br> Box(int x, int y, int z, int boxSize) {<br>   this.x = x;<br>   this.y = y;<br>   this.z = z;<br>   this.boxSize = boxSize;<br>   this.bright = 0; //по дефолту черный<br> }<br> <br> //установить яркость<br> void setBright(int bright) {<br>  if (bright > 255) bright = 255;<br>  if (bright < 0) bright = 0;<br>  this.bright = bright;<br> }<br> <br> //получить яркость<br> int getBright() {<br>  return bright; <br> }<br> <br> //нарисовать кубик<br> void display() {<br>   //установить яркость<br>   fill(bright);<br>   //сохранить предыдущую матрицу преобразований<br>   pushMatrix();<br>   //переместить кубик в заданные координаты<br>   translate(x,y,z);<br>   //нарисовать<br>   box(boxSize);<br>   //вернуть предыдущую матрицу преобразований<br>   popMatrix();<br> }<br>}<br><br>* This source code was highlighted with Source Code Highlighter.


    Ну и третий файл, который собственно и производит все полезные ништяки:
    //подключение библиотек<br>import ddf.minim.*;<br>import ddf.minim.signals.*;<br>import ddf.minim.analysis.*;<br>import ddf.minim.effects.*;<br><br>import processing.opengl.*;<br><br>//размер кубика<br>int boxSize = 40;<br>//кол-во кубиков в ширину<br>int build_width = 20;<br>//кол-во кубиков в длину<br>int build_length = 20;<br>//кол-во кубиков в высоту<br>int build_height = 10;<br>//расстояние между кубиками<br>int space = 10;<br>//массив кубиков<br>Box[][][] build;<br><br>//плеер, детектор ритма, и т.д., все взято из примера FrequencyEnergy<br>Minim minim;<br>AudioPlayer song;<br>BeatDetect beat;<br>BeatListener bl;<br><br>//процедура setup вызывается в самом начале работы<br>void setup() {<br> //устанавливаем размер окна и тип рендера<br> size(800,600, OPENGL);<br> //фон черный<br> background(0);<br> build = new Box[build_width][build_length][build_height];<br> //делаем пол из кубиков<br> for (int i = 0; i < build_width; i++) {<br>  for (int j = 0; j < build_length; j++) {<br>    build[i][j][0] = new Box((boxSize+space)*i,(boxSize+space)*j,0,boxSize);<br>  }<br> } <br> //делаем стенку из кубиков<br> for (int i = 0; i < build_width; i++) {<br>   for (int j = 1; j < build_height; j++) {<br>    build[i][build_length-1][j] = new Box((boxSize+space)*i,(boxSize+space)*(build_length-1),(boxSize+space)*j,boxSize);<br>   }<br> }<br> //делаем другую стенку из кубиков<br> for (int i = 0; i < build_length; i++) {<br>   for (int j = 1; j < build_height; j++) {<br>    build[build_width-1][i][j] = new Box((boxSize+space)*(build_width-1),(boxSize+space)*i,(boxSize+space)*j,boxSize);<br>   }<br> }<br> //делаем другую стенку из кубиков<br> for (int i = 0; i < build_width; i++) {<br>   for (int j = 1; j < build_height; j++) {<br>    build[i][0][j] = new Box((boxSize+space)*i,0,(boxSize+space)*j,boxSize);<br>   }<br> }<br> //делаем другую стенку из кубиков<br> for (int i = 0; i < build_length; i++) {<br>   for (int j = 1; j < build_height; j++) {<br>    build[0][i][j] = new Box(0,(boxSize+space)*i,(boxSize+space)*j,boxSize);<br>   }<br> }<br> //делаем потолок из кубиков<br> for (int i = 0; i < build_width; i++) {<br>  for (int j = 0; j < build_length; j++) {<br>    build[i][j][build_height-1] = new Box((boxSize+space)*i,(boxSize+space)*j,(boxSize+space)*(build_height-1),boxSize);<br>  }<br> } <br> <br> //добавляем детали<br> build[9][9][1] = new Box((boxSize+space)*9,(boxSize+space)*9,(boxSize+space),boxSize);<br> build[10][9][1] = new Box((boxSize+space)*10,(boxSize+space)*9,(boxSize+space),boxSize);<br> build[9][10][1] = new Box((boxSize+space)*9,(boxSize+space)*10,(boxSize+space),boxSize);<br> build[10][10][1] = new Box((boxSize+space)*10,(boxSize+space)*10,(boxSize+space),boxSize);<br> <br> build[18][10][1] = new Box((boxSize+space)*18,(boxSize+space)*10,(boxSize+space),boxSize);<br> build[17][10][1] = new Box((boxSize+space)*17,(boxSize+space)*10,(boxSize+space),boxSize);<br> build[16][10][1] = new Box((boxSize+space)*16,(boxSize+space)*10,(boxSize+space),boxSize);<br> build[16][9][1] = new Box((boxSize+space)*16,(boxSize+space)*9,(boxSize+space),boxSize);<br> build[16][8][1] = new Box((boxSize+space)*16,(boxSize+space)*8,(boxSize+space),boxSize);<br> build[17][8][1] = new Box((boxSize+space)*17,(boxSize+space)*8,(boxSize+space),boxSize);<br> build[18][8][1] = new Box((boxSize+space)*18,(boxSize+space)*8,(boxSize+space),boxSize);<br> <br> build[10][18][1] = new Box((boxSize+space)*10,(boxSize+space)*18,(boxSize+space),boxSize);<br> build[10][17][1] = new Box((boxSize+space)*10,(boxSize+space)*17,(boxSize+space),boxSize);<br> build[10][16][1] = new Box((boxSize+space)*10,(boxSize+space)*16,(boxSize+space),boxSize);<br> build[9][16][1] = new Box((boxSize+space)*9,(boxSize+space)*16,(boxSize+space),boxSize);<br> build[8][16][1] = new Box((boxSize+space)*8,(boxSize+space)*16,(boxSize+space),boxSize);<br> build[8][17][1] = new Box((boxSize+space)*8,(boxSize+space)*17,(boxSize+space),boxSize);<br> build[8][18][1] = new Box((boxSize+space)*8,(boxSize+space)*18,(boxSize+space),boxSize);<br> <br> //устанавливаем камеру<br> //x,y,z глаз, x,y,z точки, куда смотрим, вектор нормали показывает, где верх<br> camera(50, 50, 250, 500, 500, 150, 0, 0, -1);<br> <br> //звуковая библиотечка<br> minim = new Minim(this);<br> //запрашиваем путь к звуковому файлу (вроде не распознает русские буквы)<br> String loadPath = selectInput();<br> //грузим песенку<br> song = minim.loadFile(loadPath, 2048);<br> //запускаем на воспроизведение<br> song.play();<br> //это все относится к детектору ритма<br> beat = new BeatDetect(song.bufferSize(), song.sampleRate());<br> //фиксировать следующий бит не раньше, чем через 100 мс после предыдущего<br> beat.setSensitivity(100); <br> bl = new BeatListener(beat, song); <br>}<br><br>//функция draw вызывается для прорисовки каждый кадр<br>void draw() {<br> //рисуем кубики<br> for (int i = 0; i < build_width; i++) {<br>  for (int j = 0; j < build_length; j++) {<br>   for (int k = 0; k < build_height; k++) {<br>    if (build[i][j][k] != null) {<br>     build[i][j][k].display();<br>     //очень хитроумный алгоритм изменения яркости кубиков<br>     if (build[i][j][k].getBright() > 0)<br>     {<br>      <br>       if (i-1 >= 0 && build[i-1][j][k] != null)<br>       {<br>        build[i-1][j][k].setBright(build[i-1][j][k].getBright()/2 + 18*build[i][j][k].getBright() / 32);<br>       } <br>       if (i+1 < build_width && build[i+1][j][k] != null)<br>       {<br>        build[i+1][j][k].setBright(build[i+1][j][k].getBright()/2 + 17*build[i][j][k].getBright() / 32); <br>       }<br>       if (j-1 >= 0 && build[i][j-1][k] != null)<br>       {<br>        build[i][j-1][k].setBright(build[i][j-1][k].getBright()/2 + 18*build[i][j][k].getBright() / 32); <br>       }<br>       if (j+1 < build_length && build[i][j+1][k] != null)<br>       {<br>        build[i][j+1][k].setBright(build[i][j+1][k].getBright()/2 + 17*build[i][j][k].getBright() / 32);<br>       }<br>       if (k-1 >= 0 && build[i][j][k-1] != null)<br>       {<br>        build[i][j][k-1].setBright(build[i][j][k-1].getBright()/2 + 17*build[i][j][k].getBright() / 32); <br>       }<br>       if (k+1 < build_height && build[i][j][k+1] != null)<br>       {<br>        build[i][j][k+1].setBright(build[i][j][k+1].getBright()/2 + 17*build[i][j][k].getBright() / 32); <br>       }<br><br>      build[i][j][k].setBright(3 * build[i][j][k].getBright() / 4); <br>     }<br>    }<br>   }<br>  }<br> }<br> <br> //при возникновении бита какой-нибудь группы частот<br> //устанавливаем максимальную яркость соответствующего кубика<br> if ( beat.isHat() ) {<br>  process(15,15,0,255);<br> }<br> if ( beat.isKick() ) {<br>  process(15,5,0,255);<br> }<br> if ( beat.isSnare() ) {<br>  process(5,15,0,255);<br> }<br>}<br><br>//процедура устанавливает яркость кубика, заданного x,y,z<br>void process(int x, int y, int z, int bright) {<br> build[x][y][z].setBright(bright);<br>}<br><br>* This source code was highlighted with Source Code Highlighter.


    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 41
    • +1
      А скомпилированную версию можно?

      ЗЫ: есть, кстати, еще игра AudioSurf, ее лично я считаю за эталон звуковых игрушек.
      • +2
        вот скомпильнул, только нужно, чтобы java стояла
        линк
        • 0
          У меня проц. п4 2.8 не осилил
          • 0
            1.73 все летает хз
            • 0
              аналогично, жуткие тормоза =(
        • 0
          Красиво, напомнило демосцену)
          Есть только один минус — долго на это не полюбуешься, глаза устают (хотя возможно это только у меня под конец дня).

          Ах, да. Исполнителя в ролике не подскажете?
          • +1
            дада, пока тестил тоже глаза убил:)
            Исполняет Edenbridge — The Most Beautiful Place
            <шепот>коварный замысел по продвижению готик и симфоник металла на хабре работает...</шепот>
          • +1
            темно очень
            • –1
              так и было задумано, тут ставилась во главу интересность идеи, а не юзабельность
              • +1
                Ну дык не видно в темноте идею та :(. Я увидел комнату, в которой изредка на полу что то светиться. Больше ничего не видно — темно очень.
            • НЛО прилетело и опубликовало эту надпись здесь
              • +1
                не мешало бы добавить хоть какой-то ambient light чтобы небыло таких перепадов в темноту, глаза тогда бы не вылазили так на лоб :)
                и думаю веслее смотрелось, если бы квадратики увеличивались пропорционально подсветке, чтобы хоть какое-то движение было… имхо конечно :)
                • НЛО прилетело и опубликовало эту надпись здесь
                  • 0
                    Красиво, чёрт побери. Автор молодец
                    • +1
                      посмотрим, выглядит впечатляюще
                      и укажите теги через запятую ;)
                      • НЛО прилетело и опубликовало эту надпись здесь
                        • 0
                          к сожалению даже не знаю в чем проблема, у меня проц 1.73, видюха ноутбучная, винда ХР — как можете видеть в ролике не тормозит, фпс стабильно идет 30. Может с версией java машины что-то или дровами opengl. Или из-за винды новой, если у вас не ХР.

                          Вот скомпилил с принудительной установкой фпс в 30, также уменьшил размер окна до 640x480 (было 800x600). Попробуйте.

                          link
                          • 0
                            извиняюсь, лучше попробуйте вот эту версию, я убрал 2 стенки и потолок, они все равно не видны, у меня фпс увеличился с 30 до 55.
                            link
                            • НЛО прилетело и опубликовало эту надпись здесь
                              • НЛО прилетело и опубликовало эту надпись здесь
                          • +3
                            //массив кубиков
                            Box[][][] build;

                            мне эта строка понравилась, сразу понятно что массив кубиков.
                            • 0
                              ладно хоть я не написал
                              //если яркость больше 0
                              if (build[i][j][k].getBright() > 0)
                              а то ведь была такая идея
                            • 0
                              Декомпозиция тут не помешала бы.
                              • 0
                                Inter Core2 Duo E8400 @ 3.00GHz
                                подтормаживает, но проц загружен лишь на 30-40%
                                • 0
                                  попробуйте вот эту версию, я убрал 2 стенки и потолок, они все равно не видны, у меня фпс увеличился с 30 до 55, еще размер окна уменьшил
                                  link
                                  • +1
                                    не тормозит, проц загружен на 100%
                                    • 0
                                      Эта лучше работает, намного :) Вот только одно не большая просьба, можно фулскрин версию :)
                                      • 0
                                        processing пока не поддерживает фулскрин, хотя вроде в версии 1.4.8 уже есть какие-то костыли, ну пока ждемс…
                                  • +1
                                    omg, magic numbers!
                                    • 0
                                      для линукса покомпиль пжлст. Потом в пм можешь линк кинуть)
                                      • 0
                                        link
                                        если не запустится, что делать не знаю. я просто поставил галочку linux в компиляторе)
                                      • 0
                                        Интересно, но как-то динамичности не хватает. Возможно, ракурс не самый удачный.
                                        • 0
                                          Предложение такое — в хависимости от частоты звучания изменяется анимация. Высокие частоты отвечают за стробоскопическое подсвечивание, средние — за гармонично плавающие «прожекторные лучи», низкие — за колебания самих коробок. Будет куда динмаичней и эффетней.
                                          • 0
                                            Технически наверное это хорошо, ну и первую минуту-другую даже интересно.
                                            Но мигает не в такт. А потом обнаруживаешь, что это просто волны света, бегут по комнате — видел первую минуту, всё, ничего нового :(
                                            • 0
                                              Я один ждал когда после ч/б эффектов появиться цвет?
                                              • 0
                                                увы, но я не заметил никакой зависимости между играемым и показываемым
                                                • 0
                                                  Аналогично. Интересно, те кто восхищается — чем именно восхищаются?..
                                                • 0
                                                  Не вникал в детали кода, но выглядит прикольно!
                                                  • 0
                                                    Такой бы плагин визуализации для аимпа.
                                                    • 0
                                                      А что именно Вас в этом привлекает?
                                                      • 0
                                                        Вместо «Analog Meter», намного бы лучше смотрелось.

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