retrojs - escrevendo músicas da era 8-bits com javascript e web audio api

Post on 29-Nov-2014

2.512 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

Os navegadores modernos oferecem APIs que muitas vezes nem chegamos a conhecer e utilizar no dia-a-dia. Um exemplo é a API de Web Audio, que permite reproduzir e criar sons no browser utilizando apenas código JavaScript. Para estudá-la (e me divertir um pouco), resolvi bolar uma maneira de escrever partituras simples em formato JSON e conseguir reproduzi-las no navegador. O resultado disso foi o RetroJS: um experimento de Web Audio, com um feeling nostálgico da era 8-bits. Neste talk, vou explicar como transformei notações musicais em strings, e strings em música.

TRANSCRIPT

RETROJSParty like it's 1983

@shiota 2013

OH HAI!slideshare.net/eshiota

github.com/eshiota@shiota

+ +

Tudo começou na palestra do @almirfilho no OlhóSEO em Floripa sobre Web Audio API. O @fnando falou "Cara, dá pra fazer o JavaScript tocar o tema do Mario, imagina que foda?". Challenge accepted.

Web Audio API

// Vendor prefixedvar context = new webkitAudioContext();

context.createOscillator() context.destination

connect()

var oscillator = context.createOscillator();

oscillator.connect(context.destination);

frequência

duração

oscillator.frequency.value = 780;

oscillator.start(0);oscillator.stop(context.currentTime + 0.5);

about me

Estudei piano dos 8 aos 15 anos. Sou apaixonado por música clássica, e gosto de ler partituras. Nada como juntar um hobby antigo com uma paixão. =)

Voltando ao assunto: challenge accepted, né? =)

o projeto

&

?

44

44Piano

Allegro q»™ººœœ# œœ ‰

Jœœ ‰ œœ œœ ‰

œ œ ‰ Jœ ‰ œ œ ‰

Jœœœ ‰ Œ jœ ‰ Œ

Ó Jœ ‰ Œ

&

?

..

..

A3 jœœ ‰ ‰ jœœ Œ jœœ‰

Jœ ‰ ‰ Jœ Œ jœ ‰

‰ œœ ‰ œœ ‰ œœbb œœ ‰

‰ œ ‰ œ ‰ œb œ ‰

œœœœ œœ J

œœ ‰ œœ œœ3

œ œ œ Jœ ‰ œœ

3

‰ œœ ‰ œœ œœ œœ Œ

‰ œ ‰ œ œ œ Œ

&

?

7 jœœ ‰ ‰ jœœ Œ jœœ‰

Jœ ‰ ‰ Jœ Œ jœ ‰

‰ œœ ‰ œœ ‰ œœbb œœ ‰

‰ œ ‰ œ ‰ œb œ ‰

œœœœ œœ J

œœ ‰ œœ œœ3

œ œ œ Jœ ‰ œœ

3

‰ œœ ‰ œœ œœ œœ Œ

‰ œ ‰ œ œ œ Œ

&

?

B11

Œ œœ œœ## œœnn œœ# ‰ Jœœ

jœ ‰ ‰ Jœ Œ Jœ ‰

‰ œœ# œœ œœn ‰ œœ œœœœ

Jœ ‰ ‰ Jœ œ ‰ œ ‰

Œ œœ œœ## œœnn œœ# ‰ Jœœ

jœ ‰ ‰ Jœ Œ œ œ

‰œœœ ‰ œœœ J

œœœ ‰ Œ

Ó Œ Jœ ‰

Overworld ThemeFrom Super Mario Bros.

Koji KondoTranscribed by BLUESCD

http://www.gamemusicthemes.com/

primeira ideia

= 60

oscillator.frequency.value = 391;oscillator.start(0);oscillator.stop(context.currentTime + 1);

= 60

setTimeout(function () { oscillator.frequency.value = 391; oscillator.start(0); oscillator.stop(context.currentTime + 1);}, 1000);

= 60

setTimeout(function () { oscillator.frequency.value = 391; oscillator.start(0); oscillator.stop(context.currentTime + 1);}, 2000);

problemasmanter track do tempooscillator tem vida curtaimpraticável de escrever

soluçãocriar notação musicalcriar loop de execuçãocriar estrutura para lidar com tracks e notas

notação musical

&

?

44

44Piano

Allegro q»™ººœœ# œœ ‰

Jœœ ‰ œœ œœ ‰

œ œ ‰ Jœ ‰ œ œ ‰

Jœœœ ‰ Œ jœ ‰ Œ

Ó Jœ ‰ Œ

&

?

..

..

A3 jœœ ‰ ‰ jœœ Œ jœœ‰

Jœ ‰ ‰ Jœ Œ jœ ‰

‰ œœ ‰ œœ ‰ œœbb œœ ‰

‰ œ ‰ œ ‰ œb œ ‰

œœœœ œœ J

œœ ‰ œœ œœ3

œ œ œ Jœ ‰ œœ

3

‰ œœ ‰ œœ œœ œœ Œ

‰ œ ‰ œ œ œ Œ

&

?

7 jœœ ‰ ‰ jœœ Œ jœœ‰

Jœ ‰ ‰ Jœ Œ jœ ‰

‰ œœ ‰ œœ ‰ œœbb œœ ‰

‰ œ ‰ œ ‰ œb œ ‰

œœœœ œœ J

œœ ‰ œœ œœ3

œ œ œ Jœ ‰ œœ

3

‰ œœ ‰ œœ œœ œœ Œ

‰ œ ‰ œ œ œ Œ

&

?

B11

Œ œœ œœ## œœnn œœ# ‰ Jœœ

jœ ‰ ‰ Jœ Œ Jœ ‰

‰ œœ# œœ œœn ‰ œœ œœœœ

Jœ ‰ ‰ Jœ œ ‰ œ ‰

Œ œœ œœ## œœnn œœ# ‰ Jœœ

jœ ‰ ‰ Jœ Œ œ œ

‰œœœ ‰ œœœ J

œœœ ‰ Œ

Ó Œ Jœ ‰

Overworld ThemeFrom Super Mario Bros.

Koji KondoTranscribed by BLUESCD

http://www.gamemusicthemes.com/

{ "title" : "Imperial March", "tempo" : 120, "time_signature" : "4/4", "score" : [ { "instrument" : "oscillator-square", "volume" : 0.5, "sheet" : [ "G.8D", "-.16", "G.8D", "-.16", "G.8D", "-.16", "Eb.8D", "Bb.16", "G.8D", "-.16", "Eb.8D", "Bb.16", "G.4", "-.4" ] } ]}

Uma partitura, bem simplificada, tem o título, tempo, assinatura de tempo, e as notas. A implementação em JSON que bolei ficou assim.

[ "G.8D", "-.16", "G.8D", "-.16", "G.8D", "-.16", "Eb.8D", "Bb.16", "G.8D", "-.16", "Eb.8D", "Bb.16", "G.4", "-.4"]

Uma música é um array de notas. Mas como é a sintaxe dessas notas?

"G.4"

frequência (391)

duração (1/4)

Uma função `getFrequency` interpreta a nota e retorna a frequência, dada uma fórmula matemática.

"G5.4"

frequência (783)

duração (1/4)

"G5.4D"

frequência (783)

duração (1/4 + 1/8)

"-.4" duração (1/4)

Pausas são representadas com um "-".

criando o loop

conceitodividir a música nas menores marcações possíveis, e usar como marcação para iniciar as notas

&

?

44

44Piano

Allegro q»™ººœœ# œœ ‰

Jœœ ‰ œœ œœ ‰

œ œ ‰ Jœ ‰ œ œ ‰

Jœœœ ‰ Œ jœ ‰ Œ

Ó Jœ ‰ Œ

&

?

..

..

A3 jœœ ‰ ‰ jœœ Œ jœœ‰

Jœ ‰ ‰ Jœ Œ jœ ‰

‰ œœ ‰ œœ ‰ œœbb œœ ‰

‰ œ ‰ œ ‰ œb œ ‰

œœœœ œœ J

œœ ‰ œœ œœ3

œ œ œ Jœ ‰ œœ

3

‰ œœ ‰ œœ œœ œœ Œ

‰ œ ‰ œ œ œ Œ

&

?

7 jœœ ‰ ‰ jœœ Œ jœœ‰

Jœ ‰ ‰ Jœ Œ jœ ‰

‰ œœ ‰ œœ ‰ œœbb œœ ‰

‰ œ ‰ œ ‰ œb œ ‰

œœœœ œœ J

œœ ‰ œœ œœ3

œ œ œ Jœ ‰ œœ

3

‰ œœ ‰ œœ œœ œœ Œ

‰ œ ‰ œ œ œ Œ

&

?

B11

Œ œœ œœ## œœnn œœ# ‰ Jœœ

jœ ‰ ‰ Jœ Œ Jœ ‰

‰ œœ# œœ œœn ‰ œœ œœœœ

Jœ ‰ ‰ Jœ œ ‰ œ ‰

Œ œœ œœ## œœnn œœ# ‰ Jœœ

jœ ‰ ‰ Jœ Œ œ œ

‰œœœ ‰ œœœ J

œœœ ‰ Œ

Ó Œ Jœ ‰

Overworld ThemeFrom Super Mario Bros.

Koji KondoTranscribed by BLUESCD

http://www.gamemusicthemes.com/

semibreve1

=semínima

1/4colcheia

1/8

=semínima

1/4semicolcheia

1/16

=semínima

1/4fusa1/32

=semínima

1/4semifusa

1/64

= 60

=semínima

1/4semifusa

1/64

1000ms = 16 cycles de 62.5ms

Se uma música tem 60 bpm, uma semínima (uma "batida") tem um segundo. Uma semínima tem 16 semifusas, então tem 16 ciclos. Cada ciclo, portanto, tem 1000/16 = 62.5ms

parsedTrack["0"] = new Note("C.4");parsedTrack["16"] = new Note("D.4");parsedTrack["32"] = new Note("E.4");parsedTrack["64"] = new Note("F.4");

Tracks são hashes que contêm como índice o ciclo, e a nota que deve ser tocada nesse ciclo.

parsedTrack["0"] = new Note("C.4");parsedTrack["16"] = new Note("D.4");parsedTrack["32"] = new Note("E.4");parsedTrack["64"] = new Note("F.4");

var cycleDuration = 62.5 , currentCycle = 0;

function renderCycle () { parsedTrack[currentCycle].play()

currentCycle = currentCycle + 1;

setTimeout(renderCycle, cycleDuration);};

Uma loop incrementa os ciclos e checa se há notas a serem tocadas ali.

tracks múltiplos

parsedTrack0["0"] = new Note("C.4");parsedTrack0["16"] = new Note("D.4");parsedTrack0["32"] = new Note("E.4");parsedTrack0["64"] = new Note("F.4");

parsedTrack1["0"] = new Note("G.4");parsedTrack1["16"] = new Note("A.4");

tracks[0] = parsedTrack0;tracks[1] = parsedTrack1;

var cycleDuration = 62.5 , currentCycle = 0;

function renderCycle () { for (var i = 0, l = tracks.length; i < l; i++) { tracks[i][currentCycle].play(); }

currentCycle = currentCycle + 1;

setTimeout(renderCycle, cycleDuration);};

Se o parâmetro de indexação de uma nota na música é o mesmo entre todos os tracks, é fácil fazer músicas com múltiplos tracks.

dificuldades e adaptações no caminho

oscillator tem vida curtacada nota é uma nova instância

quartifusas (1/128)performance ficou baixa

Com múltiplos tracks, executar ciclos tomando uma nota quartifusa (1/128) de duração como base ficou muito pesado. Tive que voltar para uma semifusa (1/64).

notações não planejadasnotas pontuadas, tercinas, acordes

estrutura adaptávelnovos instrumentos, controles modificáveis

futuro

melhorar performance

mais instrumentos, usando samples

melhorias de interface

keep having fun =)

https://github.com/eshiota/retro-audio-js

Mas e o mario?

E nessa hora, foi tocado o tema do Mario completo, apenas com JavaScript. Achievement unlocked. ;D

thanks!slideshare.net/eshiota

github.com/eshiota@shiota

top related