3 января 2010 в 23:07

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

Давно хотел написать какой-никакой визуализатор музыки, но интересных идей не было. Потом увидел вот это — Аудио игра «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.


Герман @SkywalkerY
карма
108,0
рейтинг 0,0
Похожие публикации
Самое читаемое

Комментарии (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», намного бы лучше смотрелось.

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