├── README.md
├── media
├── intro.mp3
├── intro.ogg
├── intro2.mp3
├── intro2.ogg
├── instr2_1.mp3
├── instr2_1.ogg
├── bounce_1_uh.mp3
├── bounce_1_uh.ogg
├── bounce_2_uh.mp3
├── bounce_2_uh.ogg
├── bridge1_1.mp3
├── bridge1_1.ogg
├── bridge1_2.mp3
├── bridge1_2.ogg
├── hipstep1_1.mp3
├── hipstep1_1.ogg
├── hipstep1_2.mp3
├── hipstep1_2.ogg
├── hipstep2_2.mp3
├── hipstep2_2.ogg
├── instr2_2_1.mp3
├── instr2_2_1.ogg
├── instr2_2_2.mp3
├── instr2_2_2.ogg
├── putemup_1.mp3
├── putemup_1.ogg
├── putemup_2.mp3
├── putemup_2.ogg
├── tricka_1_uh.mp3
├── tricka_1_uh.ogg
├── tricka_2_uh.mp3
├── tricka_2_uh.ogg
├── bounce_1_yeah.mp3
├── bounce_1_yeah.ogg
├── bounce_2_yeah.mp3
├── bounce_2_yeah.ogg
├── tricka_1_cmon.mp3
├── tricka_1_cmon.ogg
├── tricka_2_yeah.mp3
├── tricka_2_yeah.ogg
├── youknowwhat_1.mp3
├── youknowwhat_1.ogg
├── youknowwhat_2.mp3
├── youknowwhat_2.ogg
├── youknowwhat_3.mp3
├── youknowwhat_3.ogg
├── bounce_1_intro.mp3
├── bounce_1_intro.ogg
├── bounce_2_outro.mp3
├── bounce_2_outro.ogg
├── hipstep1_2_outro.mp3
├── hipstep1_2_outro.ogg
├── hipstep2_1_intro.mp3
├── hipstep2_1_intro.ogg
├── instr1_1_intro.mp3
├── instr1_1_intro.ogg
├── instr1_1_plain.mp3
├── instr1_1_plain.ogg
├── instr1_2_outro.mp3
├── instr1_2_outro.ogg
├── instr1_2_plain.mp3
├── instr1_2_plain.ogg
├── instr2_1_intro.mp3
├── instr2_1_intro.ogg
├── instr2_1_scratch.mp3
├── instr2_1_scratch.ogg
├── instr2_2_outro.mp3
├── instr2_2_outro.ogg
├── putemup_1_intro.mp3
├── putemup_1_intro.ogg
├── putemup_2_outro.mp3
├── putemup_2_outro.ogg
├── tricka_1_intro.mp3
├── tricka_1_intro.ogg
├── tricka_1_intro2.mp3
├── tricka_1_intro2.ogg
├── tricka_1_plain.mp3
├── tricka_1_plain.ogg
├── tricka_1_putemup.mp3
├── tricka_1_putemup.ogg
├── tricka_2_plain.mp3
├── tricka_2_plain.ogg
├── tricka_2_putemup.mp3
├── tricka_2_putemup.ogg
├── tricka_2_tricka.mp3
├── tricka_2_tricka.ogg
├── hipstep2_1_guitar.mp3
├── hipstep2_1_guitar.ogg
├── youknowwhat_outro.mp3
├── youknowwhat_outro.ogg
├── tricka_1_trickawhat.mp3
├── tricka_1_trickawhat.ogg
├── hipstep2_2_guitar_outro.mp3
├── hipstep2_2_guitar_outro.ogg
├── tricka_1_tricka_tricka.mp3
├── tricka_1_tricka_tricka.ogg
├── tricka_2_outro_putemup.mp3
├── tricka_2_outro_putemup.ogg
├── tricka_2_outro_tricka_cmon.mp3
├── tricka_2_outro_tricka_cmon.ogg
├── tricka_2_outro_what_what.mp3
└── tricka_2_outro_what_what.ogg
├── Makefile
├── .gitignore
├── fetch.LICENSE
├── index.html
├── fetch.js
├── client.coffee.md
├── data.json
├── client.js
└── Technology
└── Technology.RPP
/README.md:
--------------------------------------------------------------------------------
1 | client.coffee.md
--------------------------------------------------------------------------------
/media/intro.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/intro.mp3
--------------------------------------------------------------------------------
/media/intro.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/intro.ogg
--------------------------------------------------------------------------------
/media/intro2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/intro2.mp3
--------------------------------------------------------------------------------
/media/intro2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/intro2.ogg
--------------------------------------------------------------------------------
/media/instr2_1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_1.mp3
--------------------------------------------------------------------------------
/media/instr2_1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_1.ogg
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: build watch
2 |
3 | build:
4 | coffee -c .
5 |
6 | watch:
7 | coffee -cw .
8 |
--------------------------------------------------------------------------------
/media/bounce_1_uh.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_1_uh.mp3
--------------------------------------------------------------------------------
/media/bounce_1_uh.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_1_uh.ogg
--------------------------------------------------------------------------------
/media/bounce_2_uh.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_2_uh.mp3
--------------------------------------------------------------------------------
/media/bounce_2_uh.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_2_uh.ogg
--------------------------------------------------------------------------------
/media/bridge1_1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bridge1_1.mp3
--------------------------------------------------------------------------------
/media/bridge1_1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bridge1_1.ogg
--------------------------------------------------------------------------------
/media/bridge1_2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bridge1_2.mp3
--------------------------------------------------------------------------------
/media/bridge1_2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bridge1_2.ogg
--------------------------------------------------------------------------------
/media/hipstep1_1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep1_1.mp3
--------------------------------------------------------------------------------
/media/hipstep1_1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep1_1.ogg
--------------------------------------------------------------------------------
/media/hipstep1_2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep1_2.mp3
--------------------------------------------------------------------------------
/media/hipstep1_2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep1_2.ogg
--------------------------------------------------------------------------------
/media/hipstep2_2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep2_2.mp3
--------------------------------------------------------------------------------
/media/hipstep2_2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep2_2.ogg
--------------------------------------------------------------------------------
/media/instr2_2_1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_2_1.mp3
--------------------------------------------------------------------------------
/media/instr2_2_1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_2_1.ogg
--------------------------------------------------------------------------------
/media/instr2_2_2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_2_2.mp3
--------------------------------------------------------------------------------
/media/instr2_2_2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_2_2.ogg
--------------------------------------------------------------------------------
/media/putemup_1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/putemup_1.mp3
--------------------------------------------------------------------------------
/media/putemup_1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/putemup_1.ogg
--------------------------------------------------------------------------------
/media/putemup_2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/putemup_2.mp3
--------------------------------------------------------------------------------
/media/putemup_2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/putemup_2.ogg
--------------------------------------------------------------------------------
/media/tricka_1_uh.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_uh.mp3
--------------------------------------------------------------------------------
/media/tricka_1_uh.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_uh.ogg
--------------------------------------------------------------------------------
/media/tricka_2_uh.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_uh.mp3
--------------------------------------------------------------------------------
/media/tricka_2_uh.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_uh.ogg
--------------------------------------------------------------------------------
/media/bounce_1_yeah.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_1_yeah.mp3
--------------------------------------------------------------------------------
/media/bounce_1_yeah.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_1_yeah.ogg
--------------------------------------------------------------------------------
/media/bounce_2_yeah.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_2_yeah.mp3
--------------------------------------------------------------------------------
/media/bounce_2_yeah.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_2_yeah.ogg
--------------------------------------------------------------------------------
/media/tricka_1_cmon.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_cmon.mp3
--------------------------------------------------------------------------------
/media/tricka_1_cmon.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_cmon.ogg
--------------------------------------------------------------------------------
/media/tricka_2_yeah.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_yeah.mp3
--------------------------------------------------------------------------------
/media/tricka_2_yeah.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_yeah.ogg
--------------------------------------------------------------------------------
/media/youknowwhat_1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/youknowwhat_1.mp3
--------------------------------------------------------------------------------
/media/youknowwhat_1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/youknowwhat_1.ogg
--------------------------------------------------------------------------------
/media/youknowwhat_2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/youknowwhat_2.mp3
--------------------------------------------------------------------------------
/media/youknowwhat_2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/youknowwhat_2.ogg
--------------------------------------------------------------------------------
/media/youknowwhat_3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/youknowwhat_3.mp3
--------------------------------------------------------------------------------
/media/youknowwhat_3.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/youknowwhat_3.ogg
--------------------------------------------------------------------------------
/media/bounce_1_intro.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_1_intro.mp3
--------------------------------------------------------------------------------
/media/bounce_1_intro.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_1_intro.ogg
--------------------------------------------------------------------------------
/media/bounce_2_outro.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_2_outro.mp3
--------------------------------------------------------------------------------
/media/bounce_2_outro.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_2_outro.ogg
--------------------------------------------------------------------------------
/media/hipstep1_2_outro.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep1_2_outro.mp3
--------------------------------------------------------------------------------
/media/hipstep1_2_outro.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep1_2_outro.ogg
--------------------------------------------------------------------------------
/media/hipstep2_1_intro.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep2_1_intro.mp3
--------------------------------------------------------------------------------
/media/hipstep2_1_intro.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep2_1_intro.ogg
--------------------------------------------------------------------------------
/media/instr1_1_intro.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr1_1_intro.mp3
--------------------------------------------------------------------------------
/media/instr1_1_intro.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr1_1_intro.ogg
--------------------------------------------------------------------------------
/media/instr1_1_plain.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr1_1_plain.mp3
--------------------------------------------------------------------------------
/media/instr1_1_plain.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr1_1_plain.ogg
--------------------------------------------------------------------------------
/media/instr1_2_outro.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr1_2_outro.mp3
--------------------------------------------------------------------------------
/media/instr1_2_outro.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr1_2_outro.ogg
--------------------------------------------------------------------------------
/media/instr1_2_plain.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr1_2_plain.mp3
--------------------------------------------------------------------------------
/media/instr1_2_plain.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr1_2_plain.ogg
--------------------------------------------------------------------------------
/media/instr2_1_intro.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_1_intro.mp3
--------------------------------------------------------------------------------
/media/instr2_1_intro.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_1_intro.ogg
--------------------------------------------------------------------------------
/media/instr2_1_scratch.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_1_scratch.mp3
--------------------------------------------------------------------------------
/media/instr2_1_scratch.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_1_scratch.ogg
--------------------------------------------------------------------------------
/media/instr2_2_outro.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_2_outro.mp3
--------------------------------------------------------------------------------
/media/instr2_2_outro.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_2_outro.ogg
--------------------------------------------------------------------------------
/media/putemup_1_intro.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/putemup_1_intro.mp3
--------------------------------------------------------------------------------
/media/putemup_1_intro.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/putemup_1_intro.ogg
--------------------------------------------------------------------------------
/media/putemup_2_outro.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/putemup_2_outro.mp3
--------------------------------------------------------------------------------
/media/putemup_2_outro.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/putemup_2_outro.ogg
--------------------------------------------------------------------------------
/media/tricka_1_intro.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_intro.mp3
--------------------------------------------------------------------------------
/media/tricka_1_intro.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_intro.ogg
--------------------------------------------------------------------------------
/media/tricka_1_intro2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_intro2.mp3
--------------------------------------------------------------------------------
/media/tricka_1_intro2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_intro2.ogg
--------------------------------------------------------------------------------
/media/tricka_1_plain.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_plain.mp3
--------------------------------------------------------------------------------
/media/tricka_1_plain.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_plain.ogg
--------------------------------------------------------------------------------
/media/tricka_1_putemup.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_putemup.mp3
--------------------------------------------------------------------------------
/media/tricka_1_putemup.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_putemup.ogg
--------------------------------------------------------------------------------
/media/tricka_2_plain.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_plain.mp3
--------------------------------------------------------------------------------
/media/tricka_2_plain.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_plain.ogg
--------------------------------------------------------------------------------
/media/tricka_2_putemup.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_putemup.mp3
--------------------------------------------------------------------------------
/media/tricka_2_putemup.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_putemup.ogg
--------------------------------------------------------------------------------
/media/tricka_2_tricka.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_tricka.mp3
--------------------------------------------------------------------------------
/media/tricka_2_tricka.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_tricka.ogg
--------------------------------------------------------------------------------
/media/hipstep2_1_guitar.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep2_1_guitar.mp3
--------------------------------------------------------------------------------
/media/hipstep2_1_guitar.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep2_1_guitar.ogg
--------------------------------------------------------------------------------
/media/youknowwhat_outro.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/youknowwhat_outro.mp3
--------------------------------------------------------------------------------
/media/youknowwhat_outro.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/youknowwhat_outro.ogg
--------------------------------------------------------------------------------
/media/tricka_1_trickawhat.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_trickawhat.mp3
--------------------------------------------------------------------------------
/media/tricka_1_trickawhat.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_trickawhat.ogg
--------------------------------------------------------------------------------
/media/hipstep2_2_guitar_outro.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep2_2_guitar_outro.mp3
--------------------------------------------------------------------------------
/media/hipstep2_2_guitar_outro.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep2_2_guitar_outro.ogg
--------------------------------------------------------------------------------
/media/tricka_1_tricka_tricka.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_tricka_tricka.mp3
--------------------------------------------------------------------------------
/media/tricka_1_tricka_tricka.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_tricka_tricka.ogg
--------------------------------------------------------------------------------
/media/tricka_2_outro_putemup.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_outro_putemup.mp3
--------------------------------------------------------------------------------
/media/tricka_2_outro_putemup.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_outro_putemup.ogg
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Technology/02 Tricka Technology.mp3
2 | Technology/02 Tricka Technology.mp3.reapeaks
3 | Technology/Technology.RPP-bak
4 |
--------------------------------------------------------------------------------
/media/tricka_2_outro_tricka_cmon.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_outro_tricka_cmon.mp3
--------------------------------------------------------------------------------
/media/tricka_2_outro_tricka_cmon.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_outro_tricka_cmon.ogg
--------------------------------------------------------------------------------
/media/tricka_2_outro_what_what.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_outro_what_what.mp3
--------------------------------------------------------------------------------
/media/tricka_2_outro_what_what.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_outro_what_what.ogg
--------------------------------------------------------------------------------
/fetch.LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014-2016 GitHub, Inc.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Markov Technology
4 |
27 |
28 |
29 |
37 |
38 |
41 |
42 | 🎵 A. Skillz & Krafty Kuts - Tricka Technology (
iTunes Amazon)
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/fetch.js:
--------------------------------------------------------------------------------
1 | (function(self) {
2 | 'use strict';
3 |
4 | if (self.fetch) {
5 | return
6 | }
7 |
8 | var support = {
9 | searchParams: 'URLSearchParams' in self,
10 | iterable: 'Symbol' in self && 'iterator' in Symbol,
11 | blob: 'FileReader' in self && 'Blob' in self && (function() {
12 | try {
13 | new Blob()
14 | return true
15 | } catch(e) {
16 | return false
17 | }
18 | })(),
19 | formData: 'FormData' in self,
20 | arrayBuffer: 'ArrayBuffer' in self
21 | }
22 |
23 | function normalizeName(name) {
24 | if (typeof name !== 'string') {
25 | name = String(name)
26 | }
27 | if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) {
28 | throw new TypeError('Invalid character in header field name')
29 | }
30 | return name.toLowerCase()
31 | }
32 |
33 | function normalizeValue(value) {
34 | if (typeof value !== 'string') {
35 | value = String(value)
36 | }
37 | return value
38 | }
39 |
40 | // Build a destructive iterator for the value list
41 | function iteratorFor(items) {
42 | var iterator = {
43 | next: function() {
44 | var value = items.shift()
45 | return {done: value === undefined, value: value}
46 | }
47 | }
48 |
49 | if (support.iterable) {
50 | iterator[Symbol.iterator] = function() {
51 | return iterator
52 | }
53 | }
54 |
55 | return iterator
56 | }
57 |
58 | function Headers(headers) {
59 | this.map = {}
60 |
61 | if (headers instanceof Headers) {
62 | headers.forEach(function(value, name) {
63 | this.append(name, value)
64 | }, this)
65 |
66 | } else if (headers) {
67 | Object.getOwnPropertyNames(headers).forEach(function(name) {
68 | this.append(name, headers[name])
69 | }, this)
70 | }
71 | }
72 |
73 | Headers.prototype.append = function(name, value) {
74 | name = normalizeName(name)
75 | value = normalizeValue(value)
76 | var list = this.map[name]
77 | if (!list) {
78 | list = []
79 | this.map[name] = list
80 | }
81 | list.push(value)
82 | }
83 |
84 | Headers.prototype['delete'] = function(name) {
85 | delete this.map[normalizeName(name)]
86 | }
87 |
88 | Headers.prototype.get = function(name) {
89 | var values = this.map[normalizeName(name)]
90 | return values ? values[0] : null
91 | }
92 |
93 | Headers.prototype.getAll = function(name) {
94 | return this.map[normalizeName(name)] || []
95 | }
96 |
97 | Headers.prototype.has = function(name) {
98 | return this.map.hasOwnProperty(normalizeName(name))
99 | }
100 |
101 | Headers.prototype.set = function(name, value) {
102 | this.map[normalizeName(name)] = [normalizeValue(value)]
103 | }
104 |
105 | Headers.prototype.forEach = function(callback, thisArg) {
106 | Object.getOwnPropertyNames(this.map).forEach(function(name) {
107 | this.map[name].forEach(function(value) {
108 | callback.call(thisArg, value, name, this)
109 | }, this)
110 | }, this)
111 | }
112 |
113 | Headers.prototype.keys = function() {
114 | var items = []
115 | this.forEach(function(value, name) { items.push(name) })
116 | return iteratorFor(items)
117 | }
118 |
119 | Headers.prototype.values = function() {
120 | var items = []
121 | this.forEach(function(value) { items.push(value) })
122 | return iteratorFor(items)
123 | }
124 |
125 | Headers.prototype.entries = function() {
126 | var items = []
127 | this.forEach(function(value, name) { items.push([name, value]) })
128 | return iteratorFor(items)
129 | }
130 |
131 | if (support.iterable) {
132 | Headers.prototype[Symbol.iterator] = Headers.prototype.entries
133 | }
134 |
135 | function consumed(body) {
136 | if (body.bodyUsed) {
137 | return Promise.reject(new TypeError('Already read'))
138 | }
139 | body.bodyUsed = true
140 | }
141 |
142 | function fileReaderReady(reader) {
143 | return new Promise(function(resolve, reject) {
144 | reader.onload = function() {
145 | resolve(reader.result)
146 | }
147 | reader.onerror = function() {
148 | reject(reader.error)
149 | }
150 | })
151 | }
152 |
153 | function readBlobAsArrayBuffer(blob) {
154 | var reader = new FileReader()
155 | reader.readAsArrayBuffer(blob)
156 | return fileReaderReady(reader)
157 | }
158 |
159 | function readBlobAsText(blob) {
160 | var reader = new FileReader()
161 | reader.readAsText(blob)
162 | return fileReaderReady(reader)
163 | }
164 |
165 | function Body() {
166 | this.bodyUsed = false
167 |
168 | this._initBody = function(body) {
169 | this._bodyInit = body
170 | if (typeof body === 'string') {
171 | this._bodyText = body
172 | } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
173 | this._bodyBlob = body
174 | } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
175 | this._bodyFormData = body
176 | } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
177 | this._bodyText = body.toString()
178 | } else if (!body) {
179 | this._bodyText = ''
180 | } else if (support.arrayBuffer && ArrayBuffer.prototype.isPrototypeOf(body)) {
181 | // Only support ArrayBuffers for POST method.
182 | // Receiving ArrayBuffers happens via Blobs, instead.
183 | } else {
184 | throw new Error('unsupported BodyInit type')
185 | }
186 |
187 | if (!this.headers.get('content-type')) {
188 | if (typeof body === 'string') {
189 | this.headers.set('content-type', 'text/plain;charset=UTF-8')
190 | } else if (this._bodyBlob && this._bodyBlob.type) {
191 | this.headers.set('content-type', this._bodyBlob.type)
192 | } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
193 | this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8')
194 | }
195 | }
196 | }
197 |
198 | if (support.blob) {
199 | this.blob = function() {
200 | var rejected = consumed(this)
201 | if (rejected) {
202 | return rejected
203 | }
204 |
205 | if (this._bodyBlob) {
206 | return Promise.resolve(this._bodyBlob)
207 | } else if (this._bodyFormData) {
208 | throw new Error('could not read FormData body as blob')
209 | } else {
210 | return Promise.resolve(new Blob([this._bodyText]))
211 | }
212 | }
213 |
214 | this.arrayBuffer = function() {
215 | return this.blob().then(readBlobAsArrayBuffer)
216 | }
217 |
218 | this.text = function() {
219 | var rejected = consumed(this)
220 | if (rejected) {
221 | return rejected
222 | }
223 |
224 | if (this._bodyBlob) {
225 | return readBlobAsText(this._bodyBlob)
226 | } else if (this._bodyFormData) {
227 | throw new Error('could not read FormData body as text')
228 | } else {
229 | return Promise.resolve(this._bodyText)
230 | }
231 | }
232 | } else {
233 | this.text = function() {
234 | var rejected = consumed(this)
235 | return rejected ? rejected : Promise.resolve(this._bodyText)
236 | }
237 | }
238 |
239 | if (support.formData) {
240 | this.formData = function() {
241 | return this.text().then(decode)
242 | }
243 | }
244 |
245 | this.json = function() {
246 | return this.text().then(JSON.parse)
247 | }
248 |
249 | return this
250 | }
251 |
252 | // HTTP methods whose capitalization should be normalized
253 | var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']
254 |
255 | function normalizeMethod(method) {
256 | var upcased = method.toUpperCase()
257 | return (methods.indexOf(upcased) > -1) ? upcased : method
258 | }
259 |
260 | function Request(input, options) {
261 | options = options || {}
262 | var body = options.body
263 | if (Request.prototype.isPrototypeOf(input)) {
264 | if (input.bodyUsed) {
265 | throw new TypeError('Already read')
266 | }
267 | this.url = input.url
268 | this.credentials = input.credentials
269 | if (!options.headers) {
270 | this.headers = new Headers(input.headers)
271 | }
272 | this.method = input.method
273 | this.mode = input.mode
274 | if (!body) {
275 | body = input._bodyInit
276 | input.bodyUsed = true
277 | }
278 | } else {
279 | this.url = input
280 | }
281 |
282 | this.credentials = options.credentials || this.credentials || 'omit'
283 | if (options.headers || !this.headers) {
284 | this.headers = new Headers(options.headers)
285 | }
286 | this.method = normalizeMethod(options.method || this.method || 'GET')
287 | this.mode = options.mode || this.mode || null
288 | this.referrer = null
289 |
290 | if ((this.method === 'GET' || this.method === 'HEAD') && body) {
291 | throw new TypeError('Body not allowed for GET or HEAD requests')
292 | }
293 | this._initBody(body)
294 | }
295 |
296 | Request.prototype.clone = function() {
297 | return new Request(this)
298 | }
299 |
300 | function decode(body) {
301 | var form = new FormData()
302 | body.trim().split('&').forEach(function(bytes) {
303 | if (bytes) {
304 | var split = bytes.split('=')
305 | var name = split.shift().replace(/\+/g, ' ')
306 | var value = split.join('=').replace(/\+/g, ' ')
307 | form.append(decodeURIComponent(name), decodeURIComponent(value))
308 | }
309 | })
310 | return form
311 | }
312 |
313 | function headers(xhr) {
314 | var head = new Headers()
315 | var pairs = (xhr.getAllResponseHeaders() || '').trim().split('\n')
316 | pairs.forEach(function(header) {
317 | var split = header.trim().split(':')
318 | var key = split.shift().trim()
319 | var value = split.join(':').trim()
320 | head.append(key, value)
321 | })
322 | return head
323 | }
324 |
325 | Body.call(Request.prototype)
326 |
327 | function Response(bodyInit, options) {
328 | if (!options) {
329 | options = {}
330 | }
331 |
332 | this.type = 'default'
333 | this.status = options.status
334 | this.ok = this.status >= 200 && this.status < 300
335 | this.statusText = options.statusText
336 | this.headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers)
337 | this.url = options.url || ''
338 | this._initBody(bodyInit)
339 | }
340 |
341 | Body.call(Response.prototype)
342 |
343 | Response.prototype.clone = function() {
344 | return new Response(this._bodyInit, {
345 | status: this.status,
346 | statusText: this.statusText,
347 | headers: new Headers(this.headers),
348 | url: this.url
349 | })
350 | }
351 |
352 | Response.error = function() {
353 | var response = new Response(null, {status: 0, statusText: ''})
354 | response.type = 'error'
355 | return response
356 | }
357 |
358 | var redirectStatuses = [301, 302, 303, 307, 308]
359 |
360 | Response.redirect = function(url, status) {
361 | if (redirectStatuses.indexOf(status) === -1) {
362 | throw new RangeError('Invalid status code')
363 | }
364 |
365 | return new Response(null, {status: status, headers: {location: url}})
366 | }
367 |
368 | self.Headers = Headers
369 | self.Request = Request
370 | self.Response = Response
371 |
372 | self.fetch = function(input, init) {
373 | return new Promise(function(resolve, reject) {
374 | var request
375 | if (Request.prototype.isPrototypeOf(input) && !init) {
376 | request = input
377 | } else {
378 | request = new Request(input, init)
379 | }
380 |
381 | var xhr = new XMLHttpRequest()
382 |
383 | function responseURL() {
384 | if ('responseURL' in xhr) {
385 | return xhr.responseURL
386 | }
387 |
388 | // Avoid security warnings on getResponseHeader when not allowed by CORS
389 | if (/^X-Request-URL:/m.test(xhr.getAllResponseHeaders())) {
390 | return xhr.getResponseHeader('X-Request-URL')
391 | }
392 |
393 | return
394 | }
395 |
396 | xhr.onload = function() {
397 | var options = {
398 | status: xhr.status,
399 | statusText: xhr.statusText,
400 | headers: headers(xhr),
401 | url: responseURL()
402 | }
403 | var body = 'response' in xhr ? xhr.response : xhr.responseText
404 | resolve(new Response(body, options))
405 | }
406 |
407 | xhr.onerror = function() {
408 | reject(new TypeError('Network request failed'))
409 | }
410 |
411 | xhr.ontimeout = function() {
412 | reject(new TypeError('Network request failed'))
413 | }
414 |
415 | xhr.open(request.method, request.url, true)
416 |
417 | if (request.credentials === 'include') {
418 | xhr.withCredentials = true
419 | }
420 |
421 | if ('responseType' in xhr && support.blob) {
422 | xhr.responseType = 'blob'
423 | }
424 |
425 | request.headers.forEach(function(value, name) {
426 | xhr.setRequestHeader(name, value)
427 | })
428 |
429 | xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)
430 | })
431 | }
432 | self.fetch.polyfill = true
433 | })(typeof self !== 'undefined' ? self : this);
434 |
--------------------------------------------------------------------------------
/client.coffee.md:
--------------------------------------------------------------------------------
1 | Markov Technology
2 | =================
3 |
4 | This is a demo showing the relationship between Markov chains and music. We
5 | use Tricka Technology by A. Skillz & Kraft Kuts because it has the large- and
6 | small-scale self-similarity that makes markov generation work best.
7 |
8 |
9 | $ = document.querySelector.bind(document)
10 |
11 | context = new (window.AudioContext || window.webkitAudioContext)
12 | console.log context.sampleRate
13 |
14 |
15 | Audio loading
16 | -------------
17 |
18 | This loads audio data from the server, caching the audio buffer in memory for future plays.
19 |
20 | buffercache = {}
21 | maybeFetch = (src) ->
22 | if buffercache[src]
23 | Promise.resolve(buffercache[src])
24 | else
25 | fetch(src)
26 | .then (response) -> response.arrayBuffer()
27 | .then (audioData) -> new Promise (accept) -> context.decodeAudioData audioData, accept
28 | .then (buffer) -> buffercache[src] = buffer; buffer
29 |
30 | play = (src, at) ->
31 | maybeFetch src
32 | .then (buffer) ->
33 | node = context.createBufferSource()
34 | node.buffer = buffer
35 | node.connect groupPulser.node
36 | if at then node.start(at, 0, buffer.duration) else node.start(context.currentTime, 0, buffer.duration)
37 | node
38 |
39 |
40 | Lag detector
41 | ------------
42 |
43 | We do a few different graphical things that can stress out mobile devices and
44 | lesser browsers. In case it's too much, we can turn off some things using this
45 | lag detector.
46 |
47 | massiveLagCallbacks = []
48 | onMassiveLag = (f) -> massiveLagCallbacks.push f
49 | watchFrameDrop = ->
50 | framesDropped = 0
51 | oldt = 0
52 | frameTime = 1/30 * 1000
53 | checkFrameDrop = -> requestAnimationFrame (t) ->
54 | if t - oldt > frameTime
55 | framesDropped += Math.min(Math.round((t - oldt) / frameTime), 10)
56 | console.log "framedrop", framesDropped, t - oldt
57 | else if framesDropped > 0
58 | framesDropped -= 0.25
59 |
60 | if framesDropped > 100
61 | f() for f in massiveLagCallbacks
62 | return
63 |
64 | oldt = t
65 | checkFrameDrop()
66 | checkFrameDrop()
67 | watchFrameDrop()
68 |
69 |
70 | Pulser
71 | ------
72 |
73 | We make the current node pulse using a Web Audio Analyser Node hooked up to
74 | some JS to set the opacity of the node's pulse element.
75 |
76 | makePulser = (opts={}) ->
77 | node = context.createAnalyser()
78 | node.fftSize = opts.fftSize or 256
79 | ary = new Float32Array(node.fftSize)
80 | min = null
81 | max = null
82 | oldavg = null
83 | attachedEl = null
84 | drawing = false
85 | decay = opts.decay ? 0.0001
86 | attack = opts.attack ? 0.5
87 | smoothing = opts.smoothing ? 0.66
88 | oldt = null
89 | stopped = false
90 | draw = -> requestAnimationFrame (t) ->
91 | return if stopped
92 |
93 | if node.getFloatTimeDomainData
94 | node.getFloatTimeDomainData(ary)
95 | else
96 | ary[0] = 1
97 |
98 | avg = 0
99 | avg += Math.abs(val) for val in ary
100 | avg = avg * (1-smoothing) + oldavg * smoothing
101 | oldavg = avg
102 |
103 | if !oldavg
104 | min = avg
105 | max = avg
106 | oldavg = avg
107 | min = min * (1-attack) + avg * attack if avg < min
108 | max = max * (1-attack) + avg * attack if avg > max
109 |
110 | val = Math.round(Math.min((avg-min)/(max-min), 1)*1000)/1000
111 |
112 | newmax = max * (1-decay) + min * decay
113 | newmin = min * (1-decay) + max * decay
114 | max = newmax
115 | min = newmin
116 |
117 | attachedEl.style.opacity = if opts.invert then 1-val else val
118 | draw()
119 |
120 | attach = (el) ->
121 | attachedEl.style.opacity = 0 if attachedEl
122 | attachedEl = el
123 | draw() unless drawing
124 | drawing = true
125 |
126 | stop = -> stopped = true
127 |
128 | {node, attach, stop}
129 |
130 | pulser = makePulser()
131 | groupPulser = makePulser(fftsize: 2048, attack: 1, smoothing: 0.96, invert: true)
132 | pulser.node.connect context.destination
133 | groupPulser.node.connect pulser.node
134 |
135 | onMassiveLag -> groupPulser.stop()
136 |
137 |
138 | SVG
139 | ---
140 |
141 | This sets up our SVG document, some helpers for creating new SVG elements, and
142 | the top-level groups for different elements.
143 |
144 | svg = document.querySelector('svg')
145 |
146 | svgEl = (name, attribs={}) ->
147 | el = document.createElementNS 'http://www.w3.org/2000/svg', name
148 | el.setAttribute k, v for k, v of attribs
149 | el
150 |
151 | svgText = (text, attribs) ->
152 | el = svgEl 'text', attribs
153 | el.textContent = text
154 | el
155 |
156 | defs = svgEl 'defs'
157 | defs.innerHTML = """
158 |
164 |
165 |
166 | """
167 | svg.appendChild defs
168 |
169 | labelGroup = svgEl 'g',
170 | "font-size": 96
171 | "text-anchor": "middle"
172 | linkGroup = svgEl 'g',
173 | "opacity": 0.8
174 | nodeGroup = svgEl 'g'
175 | shadeGroup = svgEl 'g'
176 | svg.appendChild shadeGroup
177 | svg.appendChild labelGroup
178 | svg.appendChild linkGroup
179 | svg.appendChild nodeGroup
180 |
181 | labelGroup.style.fontFamily = '"Helvetica Neue", "Helvetica", Sans Serif'
182 | labelGroup.style.fontWeight = "600"
183 |
184 | Node group
185 | ----------
186 |
187 | A node group is a group of Markov nodes, which is used to set the fill, stroke
188 | and pulse colours for nodes in the subgroup, as well as add the fuzzy coloured
189 | background shadow thing.
190 |
191 | groups = {}
192 |
193 | addGroup = (name, group) ->
194 | groups[name] = group
195 |
196 | group.el = el = svgEl 'g',
197 | fill: group.fill
198 | stroke: group.stroke
199 |
200 | group.pulseEl = pulseEl = svgEl 'g',
201 | opacity: 0
202 | stroke: group.stroke
203 | fill: group.pulse
204 |
205 | labelGroup.appendChild el
206 | labelGroup.appendChild pulseEl
207 |
208 | for t, i in group.label.text
209 | text = svgText t,
210 | x: group.label.x
211 | y: group.label.y + i * 96
212 | el.appendChild text
213 | pulseEl.appendChild text.cloneNode(true)
214 |
215 |
216 | if group.shade
217 | grad = svgEl 'radialGradient', id: "#{name}-gradient"
218 | grad.appendChild svgEl 'stop', offset: "0%", "stop-opacity": 1, "stop-color": group.shade.fill
219 | grad.appendChild svgEl 'stop', offset: "50%", "stop-opacity": 1, "stop-color": group.shade.fill
220 | grad.appendChild svgEl 'stop', offset: "100%", "stop-opacity": 0, "stop-color": group.shade.fill
221 | defs.appendChild grad
222 |
223 | shade = svgEl 'circle',
224 | cx: group.shade.x
225 | cy: group.shade.y
226 | r: group.shade.r * 1.5
227 | fill: "url(##{name}-gradient)"
228 | opacity: 0.8
229 |
230 | rate = 2.3529
231 | start = (Math.random() * -2 * rate).toFixed(4)
232 | shade.style.animation = "#{rate}s ease-in-out #{start}s infinite alternate pulse"
233 |
234 | onMassiveLag -> shade.style.animation = ""
235 |
236 | shadeGroup.appendChild shade
237 |
238 |
239 | Markov node
240 | -----------
241 |
242 | Our audio is played by slowly traversing a weighted directed graph of Markov
243 | nodes. The nodes handle playing audio, and have an internal list of outgoing
244 | links that they randomly choose from.
245 |
246 | In order to achieve seamless playback, we have to schedule playing the next
247 | node ahead of when our node actually finishes. We use a combination of
248 | `setTimeout` and the Web Audio API's scheduler to do this.
249 |
250 | nodeProto =
251 | render: ->
252 | return if @el
253 | @el = el = svgEl 'g',
254 | opacity: 0.7
255 | x = @x
256 | y = @y
257 |
258 | circle = svgEl 'circle',
259 | cx: x
260 | cy: y
261 | r: 40
262 | fill: @group.fill
263 | stroke: @group.stroke
264 |
265 | @pulse = pulse = svgEl 'circle',
266 | cx: x
267 | cy: y
268 | r: 40
269 | fill: @group.pulse
270 | stroke: @group.stroke
271 | opacity: 0
272 |
273 | nameparts = @name.split '_'
274 | topname = nameparts.slice(0,2).join '_'
275 | botname = nameparts.slice(2).join '_'
276 |
277 | if @label
278 | label = svgText @label,
279 | x: x
280 | y: y + 6
281 | "text-anchor": "middle"
282 | "font-size": 16
283 | "font-family": "Helvetica Neue"
284 | "font-weight": "800"
285 |
286 | el.appendChild circle
287 | el.appendChild pulse
288 | el.appendChild label if label
289 | nodeGroup.appendChild el
290 |
291 | renderLink: (next) ->
292 | return unless @el and next.el
293 | @linkEls ||= {}
294 | if !@linkEl
295 | @linkEl = svgEl 'g',
296 | stroke: '#111'
297 | linkGroup.appendChild @linkEl
298 |
299 | m = (next.y - @y) / (next.x - @x)
300 | d = Math.sqrt((next.y - @y)**2 + (next.x - @x)**2)
301 | x1 = @x + (next.x - @x) * (40/d)
302 | y1 = @y + (next.y - @y) * (40/d)
303 | x2 = next.x - (next.x - @x) * (48/d)
304 | y2 = next.y - (next.y - @y) * (48/d)
305 |
306 | line = svgEl 'line',
307 | x1: x1, y1: y1
308 | x2: x2, y2: y2
309 | 'stroke-width': 2
310 | 'marker-end': 'url(#markerArrow)'
311 | @linkEl.appendChild line
312 | @linkEls[next.name] = line
313 |
314 |
315 | display: ->
316 | total = 0
317 | total += l.weight for l in @links
318 | "#{@name} ⇒ " + ("#{l.next?.name} (#{Math.round(l.weight/total*100)}%)" for l in @links).join ', '
319 |
320 | activate: ->
321 | console.log @display()
322 | @el.setAttribute 'opacity', 0.8
323 | pulser.attach @pulse
324 | groupPulser.attach @group.pulseEl if @group.pulseEl
325 |
326 | deactivate: ->
327 | @el.setAttribute 'opacity', 0.7
328 | linkEl.setAttribute 'stroke', '#111' for k, linkEl of @linkEls if @linkEls
329 |
330 | preload: ->
331 | console.log "Loading", @src
332 | maybeFetch @src
333 |
334 | play: (at=context.currentTime+0.1, last) ->
335 | console.log "Playing", @src, "at", at
336 |
337 | setTimeout =>
338 | @activate()
339 | last?.deactivate()
340 | , (at-context.currentTime)*1000
341 |
342 | play @src, at
343 | .then (audio) =>
344 | scheduled = at + audio.buffer.duration
345 | time = (at - context.currentTime) * 1000 + 1000 # 500ms grace period
346 | next = @getNext()
347 | if next
348 | next.preload()
349 | setTimeout =>
350 | @linkEls[next.name].setAttribute 'stroke', '#FF5252'
351 | next.play(scheduled, this)
352 | , time
353 | audio.onended = @ended.bind(this)
354 |
355 | ended: ->
356 | console.log "Finished", @src
357 |
358 | getNext: ->
359 | total = @links.reduce ((total, link) -> total + link.weight), 0
360 | rand = Math.random()*total
361 | cur = 0
362 | for link in @links
363 | cur += link.weight
364 | return link.next if rand <= cur
365 | null
366 |
367 | link: (nexts, weight=1) ->
368 | nexts = [nexts] unless Array.isArray(nexts)
369 | for next in nexts
370 | linkEl = @renderLink next
371 | @links.push {next, weight}
372 | this
373 |
374 | makeNode = (src, data) ->
375 | o = Object.create nodeProto
376 | o.src = src
377 | o.links = []
378 | o.name = src.split('/').pop().split('.')[0]
379 | o[k] = data[k] for k in 'x y label'.split(' ')
380 | if data.group and groups[data.group]
381 | o.group = groups[data.group]
382 | else
383 | o.group = {fill: '#DDD', stroke: '#111', pulse: '#FFF'}
384 | o.render()
385 | o
386 |
387 | Setup
388 | -----
389 |
390 | We create nodes for every media file we're using. The nodes are not preloaded,
391 | which probably means it will stutter if the network connection is too slow and
392 | the cache is cold.
393 |
394 | Firefox has issues with inaccurate mp3 buffer length, so we need to format
395 | sniff, use ogg, and fall back to mp3 if it's not available.
396 |
397 | canOgg = document.createElement('audio').canPlayType('audio/ogg')
398 | ext = if canOgg then 'ogg' else 'mp3'
399 |
400 | nodes = {}
401 | fetch('data.json')
402 | .then (result) -> result.json()
403 | .then (data) ->
404 | for groupName, group of data.groups
405 | addGroup groupName, group
406 |
407 | nodes[k] = makeNode "media/#{k}.#{ext}", v for k, v of data.nodes
408 |
409 | for k, v of data.nodes
410 | for weight, links of v.links
411 | nodes[k].link (nodes[l] for l in links), Number(weight)
412 |
413 | nodes.intro.preload()
414 | .then ->
415 | nodes.intro.play()
416 |
--------------------------------------------------------------------------------
/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "groups": {
3 | "bounce": {
4 | "fill": "#2196F3",
5 | "stroke": "#1976D2",
6 | "pulse": "#E3F2FD",
7 | "label": {
8 | "x": 200,
9 | "y": 640,
10 | "text": ["Bounce"]
11 | },
12 | "shade": {
13 | "x": 200,
14 | "y": 640,
15 | "r": 400,
16 | "fill": "#BBDEFB"
17 | }
18 | },
19 | "youknowwhat": {
20 | "fill": "#FFEB3B",
21 | "stroke": "#FDD835",
22 | "pulse": "#FFFDE7",
23 | "label": {
24 | "x": 225,
25 | "y": 150,
26 | "text": ["You", "Know", "What"]
27 | },
28 | "shade": {
29 | "x": 225,
30 | "y": 150,
31 | "r": 400,
32 | "fill": "#FFF9C4"
33 | }
34 | },
35 | "instr": {
36 | "fill": "#FF5722",
37 | "stroke": "#E64A19",
38 | "pulse": "#FBE9E7",
39 | "label": {
40 | "x": 1350,
41 | "y": 575,
42 | "text": ["Guitar"]
43 | },
44 | "shade": {
45 | "x": 1350,
46 | "y": 575,
47 | "r": 400,
48 | "fill": "#FFCCBC"
49 | }
50 | },
51 | "putemup": {
52 | "fill": "#E91E63",
53 | "stroke": "#C2185B",
54 | "pulse": "#FCE4EC",
55 | "label": {
56 | "x": 1075,
57 | "y": 115,
58 | "text": ["Put Em", "Up"]
59 | },
60 | "shade": {
61 | "x": 1075,
62 | "y": 115,
63 | "r": 400,
64 | "fill": "#F8BBD0"
65 | }
66 | },
67 | "hipstep": {
68 | "fill": "#673AB7",
69 | "stroke": "#4527A0",
70 | "pulse": "#EDE7F6",
71 | "label": {
72 | "x": 1400,
73 | "y": 115,
74 | "text": ["Hip", "Step"]
75 | },
76 | "shade": {
77 | "x": 1400,
78 | "y": 115,
79 | "r": 250,
80 | "fill": "#D1C4E9"
81 | }
82 | },
83 | "markov": {
84 | "fill": "#FF9800",
85 | "stroke": "#FF9800",
86 | "pulse": "#FFF3E0",
87 | "label": {
88 | "x": 800,
89 | "y": 475,
90 | "text": ["Markov", "Technology"]
91 | },
92 | "shade": {
93 | "x": 800,
94 | "y": 475,
95 | "r": 400,
96 | "fill": "#FFE0B2"
97 | }
98 | }
99 | },
100 | "nodes": {
101 | "intro": {
102 | "x": 750, "y": 50,
103 | "label": "Intro",
104 | "links": {
105 | "1": ["tricka_1_intro"]
106 | }
107 | },
108 | "intro2": {
109 | "x": 850, "y": 50,
110 | "label": "Intro #2",
111 | "links": {
112 | "1": ["tricka_1_intro2"]
113 | }
114 | },
115 |
116 |
117 | "tricka_1_intro": {
118 | "x": 750, "y": 200,
119 | "group": "markov",
120 | "label": "Intro",
121 | "links": {
122 | "1": ["tricka_2_plain"]
123 | }
124 | },
125 | "tricka_1_intro2": {
126 | "x": 850, "y": 200,
127 | "group": "markov",
128 | "label": "Intro #2",
129 | "links": {
130 | "1": ["tricka_2_plain"]
131 | }
132 | },
133 |
134 |
135 | "tricka_1_plain": {
136 | "x": 1100, "y": 425,
137 | "group": "markov",
138 | "label": "Plain",
139 | "links": {
140 | "1": [
141 | "tricka_2_plain", "tricka_2_uh", "tricka_2_yeah", "tricka_2_tricka",
142 | "tricka_2_outro_tricka_cmon", "tricka_2_outro_what_what"
143 | ]
144 | }
145 | },
146 | "tricka_1_cmon": {
147 | "x": 500, "y": 425,
148 | "group": "markov",
149 | "label": "Cmon",
150 | "links": {
151 | "1": [
152 | "tricka_2_plain", "tricka_2_uh", "tricka_2_yeah", "tricka_2_tricka",
153 | "tricka_2_outro_tricka_cmon", "tricka_2_outro_what_what"
154 | ]
155 | }
156 | },
157 | "tricka_1_uh": {
158 | "x": 1066.5, "y": 312.5,
159 | "group": "markov",
160 | "label": "Uh",
161 | "links": {
162 | "1": [
163 | "tricka_2_plain", "tricka_2_uh", "tricka_2_yeah", "tricka_2_tricka",
164 | "tricka_2_outro_tricka_cmon", "tricka_2_outro_what_what"
165 | ]
166 | }
167 | },
168 | "tricka_1_trickawhat": {
169 | "x": 533.5, "y": 312.5,
170 | "group": "markov",
171 | "label": "Trickawha",
172 | "links": {
173 | "1": [
174 | "tricka_2_plain", "tricka_2_uh", "tricka_2_yeah", "tricka_2_tricka",
175 | "tricka_2_outro_tricka_cmon", "tricka_2_outro_what_what"
176 | ]
177 | }
178 | },
179 | "tricka_1_tricka_tricka": {
180 | "x": 975, "y": 230,
181 | "group": "markov",
182 | "label": "Tricka",
183 | "links": {
184 | "1": [
185 | "tricka_2_plain", "tricka_2_uh", "tricka_2_yeah", "tricka_2_tricka",
186 | "tricka_2_outro_tricka_cmon", "tricka_2_outro_what_what"
187 | ]
188 | }
189 | },
190 | "tricka_1_putemup": {
191 | "x": 625, "y": 230,
192 | "label": "Putemup",
193 | "group": "markov",
194 | "links": {
195 | "1": ["tricka_2_putemup", "tricka_2_outro_putemup"]
196 | }
197 | },
198 |
199 |
200 | "tricka_2_plain": {
201 | "x": 533.5, "y": 687.5,
202 | "group": "markov",
203 | "label": "Plain",
204 | "links": {
205 | "1": ["tricka_1_plain", "tricka_1_cmon", "tricka_1_uh", "tricka_1_trickawhat", "tricka_1_putemup"]
206 | }
207 | },
208 | "tricka_2_uh": {
209 | "x": 1100, "y": 575,
210 | "group": "markov",
211 | "label": "Uh",
212 | "links": {
213 | "1": ["tricka_1_plain", "tricka_1_cmon", "tricka_1_uh", "tricka_1_trickawhat", "tricka_1_putemup"]
214 | }
215 | },
216 | "tricka_2_yeah": {
217 | "x": 500, "y": 575,
218 | "group": "markov",
219 | "label": "Yeah",
220 | "links": {
221 | "1": ["tricka_1_plain", "tricka_1_cmon", "tricka_1_uh", "tricka_1_trickawhat", "tricka_1_putemup"]
222 | }
223 | },
224 | "tricka_2_tricka": {
225 | "x": 1066.5, "y": 687.5,
226 | "group": "markov",
227 | "label": "Tricka",
228 | "links": {
229 | "1": ["tricka_1_plain", "tricka_1_cmon", "tricka_1_uh", "tricka_1_trickawhat", "tricka_1_putemup"]
230 | }
231 | },
232 | "tricka_2_putemup": {
233 | "x": 625, "y": 770,
234 | "group": "markov",
235 | "label": "Putemup",
236 | "links": {
237 | "1": ["tricka_1_plain", "tricka_1_cmon", "tricka_1_uh", "tricka_1_trickawhat", "tricka_1_putemup"]
238 | }
239 | },
240 |
241 |
242 | "tricka_2_outro_tricka_cmon": {
243 | "x": 750, "y": 800,
244 | "group": "markov",
245 | "label": "Outro #1",
246 | "links": {
247 | "1": ["instr1_1_intro", "instr2_1_intro", "bounce_1_intro"]
248 | }
249 | },
250 | "tricka_2_outro_what_what": {
251 | "x": 850, "y": 800,
252 | "group": "markov",
253 | "label": "Outro #2",
254 | "links": {
255 | "1": ["instr1_1_intro", "instr2_1_intro", "bounce_1_intro"]
256 | }
257 | },
258 | "tricka_2_outro_putemup": {
259 | "x": 975, "y": 770,
260 | "group": "markov",
261 | "label": "Outro #3",
262 | "links": {
263 | "1": ["instr1_1_intro", "instr2_1_intro", "bounce_1_intro"]
264 | }
265 | },
266 |
267 |
268 | "instr1_1_intro": {
269 | "x": 1200, "y": 725,
270 | "group": "instr",
271 | "label": "Intro",
272 | "links": {
273 | "1": ["instr1_2_plain"]
274 | }
275 | },
276 | "instr1_1_plain": {
277 | "x": 1200, "y": 475,
278 | "group": "instr",
279 | "label": "1-1",
280 | "links": {
281 | "1": ["instr1_2_plain", "instr1_2_outro"]
282 | }
283 | },
284 | "instr1_2_plain": {
285 | "x": 1300, "y": 600,
286 | "group": "instr",
287 | "label": "1-2",
288 | "links": {
289 | "1": ["instr1_1_plain", "instr2_1"]
290 | }
291 | },
292 | "instr1_2_outro": {
293 | "x": 1300, "y": 475,
294 | "group": "instr",
295 | "label": "Outro",
296 | "links": {
297 | "1": ["bridge1_1", "intro2"]
298 | }
299 | },
300 |
301 |
302 | "instr2_1_intro": {
303 | "x": 1400, "y": 800,
304 | "group": "instr",
305 | "label": "Intro #2",
306 | "links": {
307 | "1": ["instr2_2_1", "instr2_2_2"]
308 | }
309 | },
310 | "instr2_1": {
311 | "x": 1400, "y": 675,
312 | "group": "instr",
313 | "label": "2-1 #1",
314 | "links": {
315 | "1": ["instr2_2_1", "instr2_2_2"]
316 | }
317 | },
318 | "instr2_1_scratch": {
319 | "x": 1400, "y": 550,
320 | "group": "instr",
321 | "label": "2-1 #2",
322 | "links": {
323 | "1": ["instr2_2_outro"]
324 | }
325 | },
326 | "instr2_2_1": {
327 | "x": 1500, "y": 675,
328 | "group": "instr",
329 | "label": "2-2 #1",
330 | "links": {
331 | "1": ["instr2_1", "instr2_1_scratch"]
332 | }
333 | },
334 | "instr2_2_2": {
335 | "x": 1500, "y": 550,
336 | "group": "instr",
337 | "label": "2-2 #2",
338 | "links": {
339 | "1": ["instr2_1", "instr2_1_scratch"]
340 | }
341 | },
342 | "instr2_2_outro": {
343 | "x": 1500, "y": 425,
344 | "group": "instr",
345 | "label": "Outro #2",
346 | "links": {
347 | "1": ["hipstep1_1", "hipstep2_1_intro"]
348 | }
349 | },
350 |
351 |
352 | "bridge1_1": {
353 | "x": 1300, "y": 350,
354 | "group": "instr",
355 | "label": "Bridge #1",
356 | "links": {
357 | "1": ["bridge1_2"]
358 | }
359 | },
360 | "bridge1_2": {
361 | "group": "instr",
362 | "label": "Bridge #2",
363 | "x": 1400, "y": 350,
364 | "links": {
365 | "1": ["hipstep1_1"]
366 | }
367 | },
368 |
369 |
370 | "hipstep1_1": {
371 | "x": 1400, "y": 200,
372 | "group": "hipstep",
373 | "label": "Plain 1",
374 | "links": {
375 | "1": ["hipstep1_2"]
376 | }
377 | },
378 | "hipstep1_2": {
379 | "x": 1500, "y": 50,
380 | "group": "hipstep",
381 | "label": "Plain 2",
382 | "links": {
383 | "1": ["hipstep1_1"],
384 | "2": ["hipstep2_1_guitar"]
385 | }
386 | },
387 | "hipstep2_1_intro": {
388 | "x": 1500, "y": 200,
389 | "group": "hipstep",
390 | "label": "Synth 1",
391 | "links": {
392 | "1": ["hipstep2_2"]
393 | }
394 | },
395 | "hipstep2_1_guitar": {
396 | "x": 1400, "y": 50,
397 | "group": "hipstep",
398 | "label": "Guitar 1",
399 | "links": {
400 | "1": ["hipstep2_2"],
401 | "2": ["hipstep2_2_guitar_outro"]
402 | }
403 | },
404 | "hipstep2_2": {
405 | "x": 1300, "y": 200,
406 | "group": "hipstep",
407 | "label": "Synth 2",
408 | "links": {
409 | "1": ["hipstep2_1_guitar"]
410 | }
411 | },
412 | "hipstep2_2_guitar_outro": {
413 | "x": 1300, "y": 50,
414 | "group": "hipstep",
415 | "label": "Guitar 2",
416 | "links": {
417 | "1": ["putemup_1_intro"]
418 | }
419 | },
420 |
421 |
422 | "putemup_1_intro": {
423 | "x": 1175, "y": 50,
424 | "group": "putemup",
425 | "label": "Intro",
426 | "links": {
427 | "1": ["putemup_2"]
428 | }
429 | },
430 | "putemup_2": {
431 | "x": 1075, "y": 50,
432 | "group": "putemup",
433 | "label": "Part 2",
434 | "links": {
435 | "1": ["putemup_1"]
436 | }
437 | },
438 | "putemup_1": {
439 | "x": 1075, "y": 175,
440 | "group": "putemup",
441 | "label": "Part 1",
442 | "links": {
443 | "1": ["putemup_2"],
444 | "2": ["putemup_2_outro"]
445 | }
446 | },
447 | "putemup_2_outro": {
448 | "x": 975, "y": 50,
449 | "group": "putemup",
450 | "label": "Outro",
451 | "links": {
452 | "1": ["tricka_1_intro"]
453 | }
454 | },
455 |
456 |
457 | "bounce_1_intro": {
458 | "x": 200, "y": 800,
459 | "group": "bounce",
460 | "label": "Intro",
461 | "links": {
462 | "1": ["bounce_2_yeah", "bounce_2_uh"]
463 | }
464 | },
465 | "bounce_1_uh": {
466 | "x": 300, "y": 550,
467 | "group": "bounce",
468 | "label": "Uh #1",
469 | "links": {
470 | "1": ["bounce_2_yeah", "bounce_2_uh"],
471 | "2": ["bounce_2_outro"]
472 | }
473 | },
474 | "bounce_1_yeah": {
475 | "x": 100, "y": 550,
476 | "group": "bounce",
477 | "label": "Yeah #1",
478 | "links": {
479 | "1": ["bounce_2_yeah", "bounce_2_uh"],
480 | "2": ["bounce_2_outro"]
481 | }
482 | },
483 | "bounce_2_uh": {
484 | "x": 300, "y": 675,
485 | "group": "bounce",
486 | "label": "Uh #2",
487 | "links": {
488 | "1": ["bounce_1_uh"]
489 | }
490 | },
491 | "bounce_2_yeah": {
492 | "x": 100, "y": 675,
493 | "group": "bounce",
494 | "label": "Yeah #2",
495 | "links": {
496 | "1": ["bounce_1_yeah"]
497 | }
498 | },
499 | "bounce_2_outro": {
500 | "x": 200, "y": 450,
501 | "group": "bounce",
502 | "label": "Outro",
503 | "links": {
504 | "1": ["youknowwhat_1"]
505 | }
506 | },
507 |
508 |
509 | "youknowwhat_1": {
510 | "x": 100, "y": 350,
511 | "group": "youknowwhat",
512 | "label": "#1",
513 | "links": {
514 | "1": ["youknowwhat_2", "youknowwhat_3"],
515 | "2": ["youknowwhat_outro"]
516 | }
517 | },
518 | "youknowwhat_2": {
519 | "x": 225, "y": 225,
520 | "group": "youknowwhat",
521 | "label": "#2",
522 | "links": {
523 | "1": ["youknowwhat_1", "youknowwhat_3"],
524 | "2": ["youknowwhat_outro"]
525 | }
526 | },
527 | "youknowwhat_3": {
528 | "x": 100, "y": 100,
529 | "group": "youknowwhat",
530 | "label": "#3",
531 | "links": {
532 | "1": ["youknowwhat_1", "youknowwhat_2"],
533 | "2": ["youknowwhat_outro"]
534 | }
535 | },
536 | "youknowwhat_outro": {
537 | "x": 400, "y": 200,
538 | "group": "youknowwhat",
539 | "label": "Outro",
540 | "links": {
541 | "1": ["tricka_1_intro2"]
542 | }
543 | }
544 | }
545 | }
--------------------------------------------------------------------------------
/client.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.10.0
2 | (function() {
3 | var $, addGroup, buffercache, canOgg, context, defs, ext, groupPulser, groups, labelGroup, linkGroup, makeNode, makePulser, massiveLagCallbacks, maybeFetch, nodeGroup, nodeProto, nodes, onMassiveLag, play, pulser, shadeGroup, svg, svgEl, svgText, watchFrameDrop;
4 |
5 | $ = document.querySelector.bind(document);
6 |
7 | context = new (window.AudioContext || window.webkitAudioContext);
8 |
9 | console.log(context.sampleRate);
10 |
11 | buffercache = {};
12 |
13 | maybeFetch = function(src) {
14 | if (buffercache[src]) {
15 | return Promise.resolve(buffercache[src]);
16 | } else {
17 | return fetch(src).then(function(response) {
18 | return response.arrayBuffer();
19 | }).then(function(audioData) {
20 | return new Promise(function(accept) {
21 | return context.decodeAudioData(audioData, accept);
22 | });
23 | }).then(function(buffer) {
24 | buffercache[src] = buffer;
25 | return buffer;
26 | });
27 | }
28 | };
29 |
30 | play = function(src, at) {
31 | return maybeFetch(src).then(function(buffer) {
32 | var node;
33 | node = context.createBufferSource();
34 | node.buffer = buffer;
35 | node.connect(groupPulser.node);
36 | if (at) {
37 | node.start(at, 0, buffer.duration);
38 | } else {
39 | node.start(context.currentTime, 0, buffer.duration);
40 | }
41 | return node;
42 | });
43 | };
44 |
45 | massiveLagCallbacks = [];
46 |
47 | onMassiveLag = function(f) {
48 | return massiveLagCallbacks.push(f);
49 | };
50 |
51 | watchFrameDrop = function() {
52 | var checkFrameDrop, frameTime, framesDropped, oldt;
53 | framesDropped = 0;
54 | oldt = 0;
55 | frameTime = 1 / 30 * 1000;
56 | checkFrameDrop = function() {
57 | return requestAnimationFrame(function(t) {
58 | var f, j, len;
59 | if (t - oldt > frameTime) {
60 | framesDropped += Math.min(Math.round((t - oldt) / frameTime), 10);
61 | console.log("framedrop", framesDropped, t - oldt);
62 | } else if (framesDropped > 0) {
63 | framesDropped -= 0.25;
64 | }
65 | if (framesDropped > 100) {
66 | for (j = 0, len = massiveLagCallbacks.length; j < len; j++) {
67 | f = massiveLagCallbacks[j];
68 | f();
69 | }
70 | return;
71 | }
72 | oldt = t;
73 | return checkFrameDrop();
74 | });
75 | };
76 | return checkFrameDrop();
77 | };
78 |
79 | watchFrameDrop();
80 |
81 | makePulser = function(opts) {
82 | var ary, attach, attachedEl, attack, decay, draw, drawing, max, min, node, oldavg, oldt, ref, ref1, ref2, smoothing, stop, stopped;
83 | if (opts == null) {
84 | opts = {};
85 | }
86 | node = context.createAnalyser();
87 | node.fftSize = opts.fftSize || 256;
88 | ary = new Float32Array(node.fftSize);
89 | min = null;
90 | max = null;
91 | oldavg = null;
92 | attachedEl = null;
93 | drawing = false;
94 | decay = (ref = opts.decay) != null ? ref : 0.0001;
95 | attack = (ref1 = opts.attack) != null ? ref1 : 0.5;
96 | smoothing = (ref2 = opts.smoothing) != null ? ref2 : 0.66;
97 | oldt = null;
98 | stopped = false;
99 | draw = function() {
100 | return requestAnimationFrame(function(t) {
101 | var avg, j, len, newmax, newmin, val;
102 | if (stopped) {
103 | return;
104 | }
105 | if (node.getFloatTimeDomainData) {
106 | node.getFloatTimeDomainData(ary);
107 | } else {
108 | ary[0] = 1;
109 | }
110 | avg = 0;
111 | for (j = 0, len = ary.length; j < len; j++) {
112 | val = ary[j];
113 | avg += Math.abs(val);
114 | }
115 | avg = avg * (1 - smoothing) + oldavg * smoothing;
116 | oldavg = avg;
117 | if (!oldavg) {
118 | min = avg;
119 | max = avg;
120 | oldavg = avg;
121 | }
122 | if (avg < min) {
123 | min = min * (1 - attack) + avg * attack;
124 | }
125 | if (avg > max) {
126 | max = max * (1 - attack) + avg * attack;
127 | }
128 | val = Math.round(Math.min((avg - min) / (max - min), 1) * 1000) / 1000;
129 | newmax = max * (1 - decay) + min * decay;
130 | newmin = min * (1 - decay) + max * decay;
131 | max = newmax;
132 | min = newmin;
133 | attachedEl.style.opacity = opts.invert ? 1 - val : val;
134 | return draw();
135 | });
136 | };
137 | attach = function(el) {
138 | if (attachedEl) {
139 | attachedEl.style.opacity = 0;
140 | }
141 | attachedEl = el;
142 | if (!drawing) {
143 | draw();
144 | }
145 | return drawing = true;
146 | };
147 | stop = function() {
148 | return stopped = true;
149 | };
150 | return {
151 | node: node,
152 | attach: attach,
153 | stop: stop
154 | };
155 | };
156 |
157 | pulser = makePulser();
158 |
159 | groupPulser = makePulser({
160 | fftsize: 2048,
161 | attack: 1,
162 | smoothing: 0.96,
163 | invert: true
164 | });
165 |
166 | pulser.node.connect(context.destination);
167 |
168 | groupPulser.node.connect(pulser.node);
169 |
170 | onMassiveLag(function() {
171 | return groupPulser.stop();
172 | });
173 |
174 | svg = document.querySelector('svg');
175 |
176 | svgEl = function(name, attribs) {
177 | var el, k, v;
178 | if (attribs == null) {
179 | attribs = {};
180 | }
181 | el = document.createElementNS('http://www.w3.org/2000/svg', name);
182 | for (k in attribs) {
183 | v = attribs[k];
184 | el.setAttribute(k, v);
185 | }
186 | return el;
187 | };
188 |
189 | svgText = function(text, attribs) {
190 | var el;
191 | el = svgEl('text', attribs);
192 | el.textContent = text;
193 | return el;
194 | };
195 |
196 | defs = svgEl('defs');
197 |
198 | defs.innerHTML = "\n \n";
199 |
200 | svg.appendChild(defs);
201 |
202 | labelGroup = svgEl('g', {
203 | "font-size": 96,
204 | "text-anchor": "middle"
205 | });
206 |
207 | linkGroup = svgEl('g', {
208 | "opacity": 0.8
209 | });
210 |
211 | nodeGroup = svgEl('g');
212 |
213 | shadeGroup = svgEl('g');
214 |
215 | svg.appendChild(shadeGroup);
216 |
217 | svg.appendChild(labelGroup);
218 |
219 | svg.appendChild(linkGroup);
220 |
221 | svg.appendChild(nodeGroup);
222 |
223 | labelGroup.style.fontFamily = '"Helvetica Neue", "Helvetica", Sans Serif';
224 |
225 | labelGroup.style.fontWeight = "600";
226 |
227 | groups = {};
228 |
229 | addGroup = function(name, group) {
230 | var el, grad, i, j, len, pulseEl, rate, ref, shade, start, t, text;
231 | groups[name] = group;
232 | group.el = el = svgEl('g', {
233 | fill: group.fill,
234 | stroke: group.stroke
235 | });
236 | group.pulseEl = pulseEl = svgEl('g', {
237 | opacity: 0,
238 | stroke: group.stroke,
239 | fill: group.pulse
240 | });
241 | labelGroup.appendChild(el);
242 | labelGroup.appendChild(pulseEl);
243 | ref = group.label.text;
244 | for (i = j = 0, len = ref.length; j < len; i = ++j) {
245 | t = ref[i];
246 | text = svgText(t, {
247 | x: group.label.x,
248 | y: group.label.y + i * 96
249 | });
250 | el.appendChild(text);
251 | pulseEl.appendChild(text.cloneNode(true));
252 | }
253 | if (group.shade) {
254 | grad = svgEl('radialGradient', {
255 | id: name + "-gradient"
256 | });
257 | grad.appendChild(svgEl('stop', {
258 | offset: "0%",
259 | "stop-opacity": 1,
260 | "stop-color": group.shade.fill
261 | }));
262 | grad.appendChild(svgEl('stop', {
263 | offset: "50%",
264 | "stop-opacity": 1,
265 | "stop-color": group.shade.fill
266 | }));
267 | grad.appendChild(svgEl('stop', {
268 | offset: "100%",
269 | "stop-opacity": 0,
270 | "stop-color": group.shade.fill
271 | }));
272 | defs.appendChild(grad);
273 | shade = svgEl('circle', {
274 | cx: group.shade.x,
275 | cy: group.shade.y,
276 | r: group.shade.r * 1.5,
277 | fill: "url(#" + name + "-gradient)",
278 | opacity: 0.8
279 | });
280 | rate = 2.3529;
281 | start = (Math.random() * -2 * rate).toFixed(4);
282 | shade.style.animation = rate + "s ease-in-out " + start + "s infinite alternate pulse";
283 | onMassiveLag(function() {
284 | return shade.style.animation = "";
285 | });
286 | return shadeGroup.appendChild(shade);
287 | }
288 | };
289 |
290 | nodeProto = {
291 | render: function() {
292 | var botname, circle, el, label, nameparts, pulse, topname, x, y;
293 | if (this.el) {
294 | return;
295 | }
296 | this.el = el = svgEl('g', {
297 | opacity: 0.7
298 | });
299 | x = this.x;
300 | y = this.y;
301 | circle = svgEl('circle', {
302 | cx: x,
303 | cy: y,
304 | r: 40,
305 | fill: this.group.fill,
306 | stroke: this.group.stroke
307 | });
308 | this.pulse = pulse = svgEl('circle', {
309 | cx: x,
310 | cy: y,
311 | r: 40,
312 | fill: this.group.pulse,
313 | stroke: this.group.stroke,
314 | opacity: 0
315 | });
316 | nameparts = this.name.split('_');
317 | topname = nameparts.slice(0, 2).join('_');
318 | botname = nameparts.slice(2).join('_');
319 | if (this.label) {
320 | label = svgText(this.label, {
321 | x: x,
322 | y: y + 6,
323 | "text-anchor": "middle",
324 | "font-size": 16,
325 | "font-family": "Helvetica Neue",
326 | "font-weight": "800"
327 | });
328 | }
329 | el.appendChild(circle);
330 | el.appendChild(pulse);
331 | if (label) {
332 | el.appendChild(label);
333 | }
334 | return nodeGroup.appendChild(el);
335 | },
336 | renderLink: function(next) {
337 | var d, line, m, x1, x2, y1, y2;
338 | if (!(this.el && next.el)) {
339 | return;
340 | }
341 | this.linkEls || (this.linkEls = {});
342 | if (!this.linkEl) {
343 | this.linkEl = svgEl('g', {
344 | stroke: '#111'
345 | });
346 | linkGroup.appendChild(this.linkEl);
347 | }
348 | m = (next.y - this.y) / (next.x - this.x);
349 | d = Math.sqrt(Math.pow(next.y - this.y, 2) + Math.pow(next.x - this.x, 2));
350 | x1 = this.x + (next.x - this.x) * (40 / d);
351 | y1 = this.y + (next.y - this.y) * (40 / d);
352 | x2 = next.x - (next.x - this.x) * (48 / d);
353 | y2 = next.y - (next.y - this.y) * (48 / d);
354 | line = svgEl('line', {
355 | x1: x1,
356 | y1: y1,
357 | x2: x2,
358 | y2: y2,
359 | 'stroke-width': 2,
360 | 'marker-end': 'url(#markerArrow)'
361 | });
362 | this.linkEl.appendChild(line);
363 | return this.linkEls[next.name] = line;
364 | },
365 | display: function() {
366 | var j, l, len, ref, total;
367 | total = 0;
368 | ref = this.links;
369 | for (j = 0, len = ref.length; j < len; j++) {
370 | l = ref[j];
371 | total += l.weight;
372 | }
373 | return (this.name + " ⇒ ") + ((function() {
374 | var len1, n, ref1, ref2, results;
375 | ref1 = this.links;
376 | results = [];
377 | for (n = 0, len1 = ref1.length; n < len1; n++) {
378 | l = ref1[n];
379 | results.push(((ref2 = l.next) != null ? ref2.name : void 0) + " (" + (Math.round(l.weight / total * 100)) + "%)");
380 | }
381 | return results;
382 | }).call(this)).join(', ');
383 | },
384 | activate: function() {
385 | console.log(this.display());
386 | this.el.setAttribute('opacity', 0.8);
387 | pulser.attach(this.pulse);
388 | if (this.group.pulseEl) {
389 | return groupPulser.attach(this.group.pulseEl);
390 | }
391 | },
392 | deactivate: function() {
393 | var k, linkEl, ref, results;
394 | this.el.setAttribute('opacity', 0.7);
395 | if (this.linkEls) {
396 | ref = this.linkEls;
397 | results = [];
398 | for (k in ref) {
399 | linkEl = ref[k];
400 | results.push(linkEl.setAttribute('stroke', '#111'));
401 | }
402 | return results;
403 | }
404 | },
405 | preload: function() {
406 | console.log("Loading", this.src);
407 | return maybeFetch(this.src);
408 | },
409 | play: function(at, last) {
410 | if (at == null) {
411 | at = context.currentTime + 0.1;
412 | }
413 | console.log("Playing", this.src, "at", at);
414 | setTimeout((function(_this) {
415 | return function() {
416 | _this.activate();
417 | return last != null ? last.deactivate() : void 0;
418 | };
419 | })(this), (at - context.currentTime) * 1000);
420 | return play(this.src, at).then((function(_this) {
421 | return function(audio) {
422 | var next, scheduled, time;
423 | scheduled = at + audio.buffer.duration;
424 | time = (at - context.currentTime) * 1000 + 1000;
425 | next = _this.getNext();
426 | if (next) {
427 | next.preload();
428 | setTimeout(function() {
429 | _this.linkEls[next.name].setAttribute('stroke', '#FF5252');
430 | return next.play(scheduled, _this);
431 | }, time);
432 | }
433 | return audio.onended = _this.ended.bind(_this);
434 | };
435 | })(this));
436 | },
437 | ended: function() {
438 | return console.log("Finished", this.src);
439 | },
440 | getNext: function() {
441 | var cur, j, len, link, rand, ref, total;
442 | total = this.links.reduce((function(total, link) {
443 | return total + link.weight;
444 | }), 0);
445 | rand = Math.random() * total;
446 | cur = 0;
447 | ref = this.links;
448 | for (j = 0, len = ref.length; j < len; j++) {
449 | link = ref[j];
450 | cur += link.weight;
451 | if (rand <= cur) {
452 | return link.next;
453 | }
454 | }
455 | return null;
456 | },
457 | link: function(nexts, weight) {
458 | var j, len, linkEl, next;
459 | if (weight == null) {
460 | weight = 1;
461 | }
462 | if (!Array.isArray(nexts)) {
463 | nexts = [nexts];
464 | }
465 | for (j = 0, len = nexts.length; j < len; j++) {
466 | next = nexts[j];
467 | linkEl = this.renderLink(next);
468 | this.links.push({
469 | next: next,
470 | weight: weight
471 | });
472 | }
473 | return this;
474 | }
475 | };
476 |
477 | makeNode = function(src, data) {
478 | var j, k, len, o, ref;
479 | o = Object.create(nodeProto);
480 | o.src = src;
481 | o.links = [];
482 | o.name = src.split('/').pop().split('.')[0];
483 | ref = 'x y label'.split(' ');
484 | for (j = 0, len = ref.length; j < len; j++) {
485 | k = ref[j];
486 | o[k] = data[k];
487 | }
488 | if (data.group && groups[data.group]) {
489 | o.group = groups[data.group];
490 | } else {
491 | o.group = {
492 | fill: '#DDD',
493 | stroke: '#111',
494 | pulse: '#FFF'
495 | };
496 | }
497 | o.render();
498 | return o;
499 | };
500 |
501 | canOgg = document.createElement('audio').canPlayType('audio/ogg');
502 |
503 | ext = canOgg ? 'ogg' : 'mp3';
504 |
505 | nodes = {};
506 |
507 | fetch('data.json').then(function(result) {
508 | return result.json();
509 | }).then(function(data) {
510 | var group, groupName, k, l, links, ref, ref1, ref2, ref3, v, weight;
511 | ref = data.groups;
512 | for (groupName in ref) {
513 | group = ref[groupName];
514 | addGroup(groupName, group);
515 | }
516 | ref1 = data.nodes;
517 | for (k in ref1) {
518 | v = ref1[k];
519 | nodes[k] = makeNode("media/" + k + "." + ext, v);
520 | }
521 | ref2 = data.nodes;
522 | for (k in ref2) {
523 | v = ref2[k];
524 | ref3 = v.links;
525 | for (weight in ref3) {
526 | links = ref3[weight];
527 | nodes[k].link((function() {
528 | var j, len, results;
529 | results = [];
530 | for (j = 0, len = links.length; j < len; j++) {
531 | l = links[j];
532 | results.push(nodes[l]);
533 | }
534 | return results;
535 | })(), Number(weight));
536 | }
537 | }
538 | return nodes.intro.preload();
539 | }).then(function() {
540 | return nodes.intro.play();
541 | });
542 |
543 | }).call(this);
544 |
--------------------------------------------------------------------------------
/Technology/Technology.RPP:
--------------------------------------------------------------------------------
1 |
27 |
29 | RENDER_FILE "/Users/samg/Code/markov-technology/media"
30 | RENDER_PATTERN $track
31 | RENDER_FMT 0 2 44100
32 | RENDER_1X 0
33 | RENDER_RANGE 4 0 0
34 | RENDER_RESAMPLE 3 0 1
35 | RENDER_ADDTOPROJ 0
36 | RENDER_STEMS 32
37 | RENDER_DITHER 0
38 | TIMELOCKMODE 1
39 | TEMPOENVLOCKMODE 1
40 | ITEMMIX 0
41 | DEFPITCHMODE 589824
42 | TAKELANE 1
43 | SAMPLERATE 44100 0 0
44 |
47 | LOCK 1
48 |
55 | GLOBAL_AUTO -1
56 | TEMPO 101.85 4 4
57 | PLAYRATE 1 0 0.25 4
58 | SELECTION 0 0
59 | SELECTION2 0 0
60 | MASTERAUTOMODE 0
61 | MASTERTRACKHEIGHT 0
62 | MASTERPEAKCOL 16576
63 | MASTERMUTESOLO 0
64 | MASTERTRACKVIEW 0 0.6667 0.5 0.5 -1 -1 -1
65 | MASTERHWOUT 0 0 1 0 0 0 0 -1
66 | MASTER_NCH 2 2
67 | MASTER_VOLUME 1 0 -1 -1 1
68 | MASTER_FX 1
69 | MASTER_SEL 0
70 |
77 |
84 | MARKER 1 4.71281296023565 "Chorus 1" 0 0 1
85 | MARKER 2 23.5640648011782 "Verse 1" 0 0 1
86 | MARKER 3 42.41531664212077 "Chorus 2" 0 0 1
87 | MARKER 4 61.26656848306333 "Instrumental 1" 0 0 1
88 | MARKER 5 80.11782032400589 Bridge 0 0 1
89 | MARKER 6 89.54344624447718 "Hip Step" 0 0 1
90 | MARKER 7 108.39469808541975 "Verse 2" 0 0 1
91 | MARKER 8 122.53313696612666 Bridge 0 0 1
92 | MARKER 9 127.2459499263623 "Chorus 3" 0 0 1
93 | MARKER 10 146.09720176730488 Bounce 0 0 1
94 | MARKER 11 164.94845360824743 "You know what" 0 0 1
95 | MARKER 12 183.79970544918999 "Chorus 4" 0 0 1
96 | MARKER 13 202.65095729013257 "Instrumental 2" 0 0 1
97 | MARKER 14 221.50220913107512 "Hip Step 2" 0 0 1
98 | MARKER 15 240.3534609720177 "Put em up" 0 0 1
99 | MARKER 16 259.20471281296022 Outro 0 0 1
100 |
102 |