Um dos motivos que me fizeram ficar distante do blog por uns tempos foi o meu mestrado, cujo semestre acabou essa semana. E como estou trabahando direto com Java atualmente, tive que usar a imaginação para continuar praticando meus conhecimentos em Ruby. Para quem não sabe, estou fazendo mestrado em Sistemas Interativos de Visualização, com muitas cadeiras ligadas a Computação Gráfica, Animação e Visualização de Dados. Em muito dos trabalhos, utilizei uma das minha ferramentas favoritas, o Processing, mais precisamente sua alternativa rubista: o ruby-processing.
O Poder do JRuby
Uma das melhores vantagens fornecidas pelo JRuby é a possibilidade de utilizarmos as trocentas ferramentas já existentes no mundo Java utilizando Ruby. E foi exatamente isso que Jeremy Ashkenas fez. Perceba que a integração é relativamente simples entre as duas linguagens:
require 'java'
module Processing
# Conditionally load core.jar
require "#{RP5_ROOT}/lib/core/core.jar" unless Processing.online? || Processing.embedded?
import "processing.core"
.
.
.
class App < PApplet
.
.
Ao importar o .jar do Processing , temos à disposição todas as classes, inclusive a PApplet que é a que nos disponibiliza as principais funções de desenho. O resto do código é praticamente formado apenas por wrappers e funções auxiliares de integração. A lógica toda já está pronta, e em Java.
Aconselho fortemente uma olhada no código do ruby-processing para quem quiser aprender como se faz uma aplicação completa usando JRuby
Os Corais de Bach
Em uma disciplina de Visualização de Dados, um trabalho consistia em escolher um dataset do site Machine Learning Repository e criar uma maneira de visualizar esses dados. Escolhi os Corais de Bach, devido ao fato de eu ser fã do magnífico compositor alemão.
O primeiro desafio foi destrinchar a base de dados. Para vocês terem uma idéia, aqui segue um exemplo de uma peça de coral contida na coleção:
(1 ((st 8 ) (pitch 67) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 12) (pitch 67) (dur 8 ) (keysig 1) (timesig 12) (fermata 0))((st 20) (pitch 74) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 24) (pitch 71) (dur 6) (keysig 1) (timesig 12) (fermata 0))((st 30) (pitch 69) (dur 2) (keysig 1) (timesig 12) (fermata 0))((st 32) (pitch 67) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 36) (pitch 67) (dur 6) (keysig 1) (timesig 12) (fermata 0))((st 42) (pitch 69) (dur 2) (keysig 1) (timesig 12) (fermata 0))((st 44) (pitch 71) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 48 ) (pitch 69) (dur 8 ) (keysig 1) (timesig 12) (fermata 1))((st 56) (pitch 71) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 60) (pitch 74) (dur 8 ) (keysig 1) (timesig 12) (fermata 0))((st 68 ) (pitch 72) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 72) (pitch 71) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 76) (pitch 69) (dur 8 ) (keysig 1) (timesig 12) (fermata 0))((st 84) (pitch 67) (dur 8 ) (keysig 1) (timesig 12) (fermata 1))((st 92) (pitch 71) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 96) (pitch 71) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 100) (pitch 72) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 104) (pitch 74) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 108 ) (pitch 74) (dur 6) (keysig 1) (timesig 12) (fermata 0))((st 114) (pitch 72) (dur 2) (keysig 1) (timesig 12) (fermata 0))((st 116) (pitch 71) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 120) (pitch 69) (dur 8 ) (keysig 1) (timesig 12) (fermata 1))((st 128) (pitch 67) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 132) (pitch 71) (dur 8 ) (keysig 1) (timesig 12) (fermata 0))((st 140) (pitch 72) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 144) (pitch 74) (dur 8 ) (keysig 1) (timesig 12) (fermata 0))((st 152) (pitch 72) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 156) (pitch 71) (dur 12) (keysig 1) (timesig 12) (fermata 0))((st 168) (pitch 67) (dur 8 ) (keysig 1) (timesig 12) (fermata 1))((st 176) (pitch 71) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 180) (pitch 74) (dur 8 ) (keysig 1) (timesig 12) (fermata 0))((st 188 ) (pitch 72) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 192) (pitch 71) (dur 8 ) (keysig 1) (timesig 12) (fermata 0))((st 200) (pitch 69) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 204) (pitch 67) (dur 6) (keysig 1) (timesig 12) (fermata 0))((st 210) (pitch 69) (dur 2) (keysig 1) (timesig 12) (fermata 0))((st 212) (pitch 71) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 216) (pitch 69) (dur 8 ) (keysig 1) (timesig 12) (fermata 1))((st 224) (pitch 71) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 228 ) (pitch 74) (dur 8 ) (keysig 1) (timesig 12) (fermata 0))((st 236) (pitch 72) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 240) (pitch 71) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 244) (pitch 69) (dur 8 ) (keysig 1) (timesig 12) (fermata 0))((st 252) (pitch 67) (dur 8 ) (keysig 1) (timesig 12) (fermata 1)))
Pra quem conhece Lisp, essa bizarrice não assusta tanto, mas eu precisava de uma maneira de tornar essa maçaroca em algo entendível. Aí vem a beleza do Ruby e sua facilidade para processamento de texto. Com poucas linhas, um arquivo com 100 peças de corais como a mostrada acima vira uma estrutura simples com tudo que eu preciso para minha visualização:
lines = []
File.open("bach.lisp", "r") do |infile|
while (line = infile.gets)
lines << "#{line}" unless line.size == 1 #ignora linhas em branco
end
end
Com tudo mastigado, basta partirmos para as operações matemáticas.
Hora do Desenho
Na minha busca por reconhecer padrões nas músicas em minhas visualizações, escolhi arcos, porque eles passam uma noção de duração facilmente perceptível, e as cores foram escolhidas para as notas, ficando o eixo X para o momento em que as notas iniciam.
def draw
background 0,0,0
draw_labels
draw_rectangles
@arcs.each do |ar|
noFill
stroke(ar.c)
smooth
semicircle(ar.st+ar.duration, X_AXIS, ar.st,X_AXIS )
line(ar.st+ar.duration, X_AXIS+10, ar.st+ar.duration, X_AXIS+10+(ar.pitch*2))
end
end
def semicircle xLeft, yLeft, xRight, yRight
pushMatrix
diameter = dist xLeft, yLeft, xRight, yRight
angle = atan2(yRight - yLeft, xRight - xLeft)
arc((xLeft + xRight)/2, (yLeft + yRight) / 2, diameter, diameter, angle, angle + PI)
popMatrix
end
O resultado, pode ser conferido nos screenshots abaixo:
Se levarmos em conta o tempo curto entre os trabalho, o resultado alcançado foi interessante. Meu código possui muitas duplicações e o algoritmo não está nada otimizado. Com mais tempo, faço uma versão melhor, aonde qualquer arquivo de música MIDI possa ser interpretado.


Da hora! Somente tinha trabalhado com o Processing utilizando a sua API similar com o Java, não sabia que existia essa gem, terei que voltar a brincar :)
Nice work! You should consider adding this to the “Projects” page on the Ruby-Processing wiki.