├── .gitignore ├── README.md ├── blank.html ├── index.html ├── now.json ├── package-lock.json ├── package.json ├── public ├── _boilerplate │ ├── index.html │ ├── main.js │ ├── sonic-pi-beat.rb │ └── styles.css ├── blank.html ├── css │ ├── global.css │ └── grid.css ├── graphs │ ├── css │ │ └── notes.css │ ├── index.html │ └── js │ │ └── notes.js ├── index.html ├── js │ └── slides-setup.js ├── matrix-16x8 │ ├── example-sonic-pi-driver.rb │ ├── index.html │ ├── matrix-visualizer.js │ └── styles.css ├── samples │ ├── BD.WAV │ ├── CB.WAV │ ├── CH.WAV │ ├── CL.WAV │ ├── CP.WAV │ ├── OH.WAV │ ├── SD.WAV │ ├── hotline-1-4s.wav │ ├── hotline-2-4s.wav │ ├── hotline-3-7s.wav │ ├── hotline-4-3.5s.wav │ ├── hotline-5-3.5s.wav │ ├── thug-01.wav │ ├── thug-02.wav │ └── thug-03.wav └── sqcr-demo │ ├── buffers │ ├── init.js │ ├── loops.js │ └── synths.js │ ├── config │ ├── matrix.json │ └── presentation.json │ ├── css │ └── slides.css │ ├── html │ ├── 808.html │ ├── akai.html │ ├── beats-graph.html │ ├── chords-graph.html │ ├── matrix-16x8.html │ ├── notes-graph.html │ ├── random-tones.html │ └── scale-tones.html │ ├── js │ ├── chords-graph-synth.js │ ├── hats-graph-synth.js │ ├── matrix-16x8-init.js │ ├── matrix-16x8-synths.js │ ├── matrix-16x8.js │ └── notes-graph-synth.js │ ├── lib │ ├── Tone.min.js │ ├── audiosynth.js │ ├── osc-browser.js │ ├── tonal.js │ └── transpiled.js │ ├── main.js │ ├── md │ └── generative-hip-hop-slides.md │ ├── renderers │ ├── matrix-renderer.js │ └── slider.js │ ├── sqcr-setup.js │ ├── styles.css │ └── visualizer.js └── src └── Markov.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Markov Music JS 2 | 3 | A few examples of how to use markov chains to make music. 4 | 5 | Live URL: [markov-music.now.sh](https://markov-music.now.sh) 6 | 7 | ## Locel Setup 8 | 9 | ``` 10 | npm install 11 | npm start:slides 12 | ``` 13 | 14 | Visit `localhost:8081` 15 | -------------------------------------------------------------------------------- /blank.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omardelarosa/markov-music-js/e7de4956ff0fcf81874b1cb9d6a44f5a9172e025/blank.html -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Markov Music JS 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /now.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "static", 3 | "name": "markov-music-js", 4 | "alias": ["markov-music"], 5 | "files": ["index.html", "blank.html", "src", "node_modules", "public"] 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "markov-music-js", 3 | "main": "index.js", 4 | "version": "0.0.1", 5 | "description": 6 | "Example of how to use markov chaining to make music in javascript", 7 | "author": "Omar Delarosa", 8 | "scripts": { 9 | "start:slides": "npm run start:sqcr-demo:slider", 10 | "start:sqcr-demo:matrix": 11 | "sqcr public/sqcr-demo/buffers --config public/sqcr-demo/config/matrix.json --path .", 12 | "start:sqcr-demo:slider": 13 | "sqcr public/sqcr-demo/buffers --config public/sqcr-demo/config/presentation.json --path ." 14 | }, 15 | "dependencies": { 16 | "express": "4.16.3", 17 | "meow": "5.0.0", 18 | "sqcr": "omardelarosa/sqcr#release-v1.1.1", 19 | "tonal": "1.1.3", 20 | "tone": "0.12.80", 21 | "vis": "4.21.0", 22 | "webmidi": "2.2.0", 23 | "ws": "5.1.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /public/_boilerplate/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Untitled - osc.js demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /public/_boilerplate/main.js: -------------------------------------------------------------------------------- 1 | // DATA container 2 | let data = []; 3 | 4 | 5 | // OSC.js stuff 6 | const handleMessage = (msg) => { 7 | // console.log('MSG', msg); 8 | data = msg.address.split('/'); 9 | } 10 | 11 | const initOSC = () => { 12 | // Init container 13 | 14 | // Init port 15 | oscPort = new osc.WebSocketPort({ 16 | url: "ws://localhost:8081" 17 | }); 18 | 19 | 20 | // listen 21 | oscPort.on('message', (msg) => { 22 | handleMessage(msg); // Debugging 23 | }); 24 | 25 | // open port 26 | oscPort.open(); 27 | }; 28 | 29 | // used later to start OSC 30 | window.initOSC = initOSC(); 31 | 32 | // Additional code below 33 | -------------------------------------------------------------------------------- /public/_boilerplate/sonic-pi-beat.rb: -------------------------------------------------------------------------------- 1 | T = 4.0 2 | 3 | use_osc "192.168.1.7", 57121 # this synchronizes with the OSC server in the window. <- 4 | 5 | kick_patterns = [ 6 | (bools, 1,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0), # Kick Pattern 1 / C 7 | (bools, 1,0,0,0, 0,0,1,0, 0,1,1,0, 0,1,1,0) # Kick Pattern 2 / C 8 | ].ring 9 | 10 | snare_patterns = [ 11 | (bools, 0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0), # Snare Pattern 1 / G 12 | (bools, 0,0,0,0, 1,1,0,0, 0,0,0,0, 1,0,1,0) # Snare Pattern 2 / G 13 | ].ring 14 | 15 | live_loop :kicks do 16 | p = kick_patterns.choose 17 | 16.times do |n| 18 | sample :bd_mehackit if p.tick 19 | osc "/blink/#{n}/#{n/2}/2" if p.look 20 | sleep T/16 21 | end 22 | end 23 | 24 | live_loop :snares do 25 | p = snare_patterns.choose 26 | 16.times do |n| 27 | sample :sn_dolf if p.tick 28 | osc "/blink/#{n}/#{n/2}/3" if p.look 29 | sleep T/16 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /public/_boilerplate/styles.css: -------------------------------------------------------------------------------- 1 | /** Add css here **/ 2 | -------------------------------------------------------------------------------- /public/blank.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | BLANK 5 | 10 | 11 | 12 |
Loading...
13 | 14 | 15 | -------------------------------------------------------------------------------- /public/css/global.css: -------------------------------------------------------------------------------- 1 | body, 2 | html, 3 | div { 4 | background-color: black; 5 | padding: 0; 6 | margin: 0; 7 | color: white; 8 | font-family: 'Helvetica', sans-serif; 9 | } 10 | 11 | .container { 12 | margin-left: auto; 13 | margin-right: auto; 14 | } 15 | 16 | .k-row { 17 | margin-left: auto; 18 | margin-right: auto; 19 | max-width: 1600px; 20 | } 21 | -------------------------------------------------------------------------------- /public/css/grid.css: -------------------------------------------------------------------------------- 1 | div { 2 | min-height: 90px; 3 | padding: 30px; 4 | } 5 | body { 6 | background: #008080; 7 | padding: 0; 8 | margin: 0; 9 | } 10 | html { 11 | box-sizing: border-box; 12 | } 13 | *, 14 | *:before, 15 | *:after { 16 | box-sizing: inherit; 17 | } 18 | .k-center { 19 | text-align: center; 20 | } 21 | .k-container { 22 | width: 100%; 23 | height: 100%; 24 | display: block; 25 | max-width: 1600px; 26 | } 27 | .k-centered { 28 | margin-left: auto; 29 | margin-right: auto; 30 | } 31 | .k-row { 32 | max-width: 1600px; 33 | } 34 | .k-row:after { 35 | display: table; 36 | clear: both; 37 | content: ''; 38 | } 39 | .k-col.k-c-1 { 40 | width: calc(calc(100% - 20px) / 1); 41 | margin-right: 10px; 42 | margin-left: 10px; 43 | float: left; 44 | } 45 | .k-col.k-c-2 { 46 | width: calc(calc(100% - 40px) / 2); 47 | margin-right: 10px; 48 | margin-left: 10px; 49 | float: left; 50 | } 51 | .k-col.k-c-3 { 52 | width: calc(calc(100% - 60px) / 3); 53 | margin-right: 10px; 54 | margin-left: 10px; 55 | float: left; 56 | } 57 | .k-col.k-c-4 { 58 | width: calc(calc(100% - 80px) / 4); 59 | margin-right: 10px; 60 | margin-left: 10px; 61 | float: left; 62 | } 63 | .k-col.k-c-5 { 64 | width: calc(calc(100% - 100px) / 5); 65 | margin-right: 10px; 66 | margin-left: 10px; 67 | float: left; 68 | } 69 | .k-col.k-c-6 { 70 | width: calc(calc(100% - 120px) / 6); 71 | margin-right: 10px; 72 | margin-left: 10px; 73 | float: left; 74 | } 75 | .k-col.k-c-7 { 76 | width: calc(calc(100% - 140px) / 7); 77 | margin-right: 10px; 78 | margin-left: 10px; 79 | float: left; 80 | } 81 | .k-col.k-c-8 { 82 | width: calc(calc(100% - 160px) / 8); 83 | margin-right: 10px; 84 | margin-left: 10px; 85 | float: left; 86 | } 87 | .k-col.k-c-9 { 88 | width: calc(calc(100% - 180px) / 9); 89 | margin-right: 10px; 90 | margin-left: 10px; 91 | float: left; 92 | } 93 | .k-col.k-c-10 { 94 | width: calc(calc(100% - 200px) / 10); 95 | margin-right: 10px; 96 | margin-left: 10px; 97 | float: left; 98 | } 99 | .k-col.k-c-11 { 100 | width: calc(calc(100% - 220px) / 11); 101 | margin-right: 10px; 102 | margin-left: 10px; 103 | float: left; 104 | } 105 | .k-col.k-c-12 { 106 | width: calc(calc(100% - 240px) / 12); 107 | margin-right: 10px; 108 | margin-left: 10px; 109 | float: left; 110 | } 111 | .k-col.k-c-13 { 112 | width: calc(calc(100% - 260px) / 13); 113 | margin-right: 10px; 114 | margin-left: 10px; 115 | float: left; 116 | } 117 | .k-col.k-c-14 { 118 | width: calc(calc(100% - 280px) / 14); 119 | margin-right: 10px; 120 | margin-left: 10px; 121 | float: left; 122 | } 123 | .k-col.k-c-15 { 124 | width: calc(calc(100% - 300px) / 15); 125 | margin-right: 10px; 126 | margin-left: 10px; 127 | float: left; 128 | } 129 | .k-col.k-c-16 { 130 | width: calc(calc(100% - 320px) / 16); 131 | margin-right: 10px; 132 | margin-left: 10px; 133 | float: left; 134 | } 135 | @media (min-width: 0px) { 136 | .k-col.k-c-1-xs { 137 | width: calc(calc(100% - 20px) / 1); 138 | margin-right: 10px; 139 | margin-left: 10px; 140 | float: left; 141 | } 142 | } 143 | @media (min-width: 0px) { 144 | .k-col.k-c-2-xs { 145 | width: calc(calc(100% - 40px) / 2); 146 | margin-right: 10px; 147 | margin-left: 10px; 148 | float: left; 149 | } 150 | } 151 | @media (min-width: 0px) { 152 | .k-col.k-c-3-xs { 153 | width: calc(calc(100% - 60px) / 3); 154 | margin-right: 10px; 155 | margin-left: 10px; 156 | float: left; 157 | } 158 | } 159 | @media (min-width: 0px) { 160 | .k-col.k-c-4-xs { 161 | width: calc(calc(100% - 80px) / 4); 162 | margin-right: 10px; 163 | margin-left: 10px; 164 | float: left; 165 | } 166 | } 167 | @media (min-width: 0px) { 168 | .k-col.k-c-5-xs { 169 | width: calc(calc(100% - 100px) / 5); 170 | margin-right: 10px; 171 | margin-left: 10px; 172 | float: left; 173 | } 174 | } 175 | @media (min-width: 0px) { 176 | .k-col.k-c-6-xs { 177 | width: calc(calc(100% - 120px) / 6); 178 | margin-right: 10px; 179 | margin-left: 10px; 180 | float: left; 181 | } 182 | } 183 | @media (min-width: 0px) { 184 | .k-col.k-c-7-xs { 185 | width: calc(calc(100% - 140px) / 7); 186 | margin-right: 10px; 187 | margin-left: 10px; 188 | float: left; 189 | } 190 | } 191 | @media (min-width: 0px) { 192 | .k-col.k-c-8-xs { 193 | width: calc(calc(100% - 160px) / 8); 194 | margin-right: 10px; 195 | margin-left: 10px; 196 | float: left; 197 | } 198 | } 199 | @media (min-width: 0px) { 200 | .k-col.k-c-9-xs { 201 | width: calc(calc(100% - 180px) / 9); 202 | margin-right: 10px; 203 | margin-left: 10px; 204 | float: left; 205 | } 206 | } 207 | @media (min-width: 0px) { 208 | .k-col.k-c-10-xs { 209 | width: calc(calc(100% - 200px) / 10); 210 | margin-right: 10px; 211 | margin-left: 10px; 212 | float: left; 213 | } 214 | } 215 | @media (min-width: 0px) { 216 | .k-col.k-c-11-xs { 217 | width: calc(calc(100% - 220px) / 11); 218 | margin-right: 10px; 219 | margin-left: 10px; 220 | float: left; 221 | } 222 | } 223 | @media (min-width: 0px) { 224 | .k-col.k-c-12-xs { 225 | width: calc(calc(100% - 240px) / 12); 226 | margin-right: 10px; 227 | margin-left: 10px; 228 | float: left; 229 | } 230 | } 231 | @media (min-width: 0px) { 232 | .k-col.k-c-13-xs { 233 | width: calc(calc(100% - 260px) / 13); 234 | margin-right: 10px; 235 | margin-left: 10px; 236 | float: left; 237 | } 238 | } 239 | @media (min-width: 0px) { 240 | .k-col.k-c-14-xs { 241 | width: calc(calc(100% - 280px) / 14); 242 | margin-right: 10px; 243 | margin-left: 10px; 244 | float: left; 245 | } 246 | } 247 | @media (min-width: 0px) { 248 | .k-col.k-c-15-xs { 249 | width: calc(calc(100% - 300px) / 15); 250 | margin-right: 10px; 251 | margin-left: 10px; 252 | float: left; 253 | } 254 | } 255 | @media (min-width: 0px) { 256 | .k-col.k-c-16-xs { 257 | width: calc(calc(100% - 320px) / 16); 258 | margin-right: 10px; 259 | margin-left: 10px; 260 | float: left; 261 | } 262 | } 263 | @media (min-width: 400px) { 264 | .k-col.k-c-1-s { 265 | width: calc(calc(100% - 20px) / 1); 266 | margin-right: 10px; 267 | margin-left: 10px; 268 | float: left; 269 | } 270 | } 271 | @media (min-width: 400px) { 272 | .k-col.k-c-2-s { 273 | width: calc(calc(100% - 40px) / 2); 274 | margin-right: 10px; 275 | margin-left: 10px; 276 | float: left; 277 | } 278 | } 279 | @media (min-width: 400px) { 280 | .k-col.k-c-3-s { 281 | width: calc(calc(100% - 60px) / 3); 282 | margin-right: 10px; 283 | margin-left: 10px; 284 | float: left; 285 | } 286 | } 287 | @media (min-width: 400px) { 288 | .k-col.k-c-4-s { 289 | width: calc(calc(100% - 80px) / 4); 290 | margin-right: 10px; 291 | margin-left: 10px; 292 | float: left; 293 | } 294 | } 295 | @media (min-width: 400px) { 296 | .k-col.k-c-5-s { 297 | width: calc(calc(100% - 100px) / 5); 298 | margin-right: 10px; 299 | margin-left: 10px; 300 | float: left; 301 | } 302 | } 303 | @media (min-width: 400px) { 304 | .k-col.k-c-6-s { 305 | width: calc(calc(100% - 120px) / 6); 306 | margin-right: 10px; 307 | margin-left: 10px; 308 | float: left; 309 | } 310 | } 311 | @media (min-width: 400px) { 312 | .k-col.k-c-7-s { 313 | width: calc(calc(100% - 140px) / 7); 314 | margin-right: 10px; 315 | margin-left: 10px; 316 | float: left; 317 | } 318 | } 319 | @media (min-width: 400px) { 320 | .k-col.k-c-8-s { 321 | width: calc(calc(100% - 160px) / 8); 322 | margin-right: 10px; 323 | margin-left: 10px; 324 | float: left; 325 | } 326 | } 327 | @media (min-width: 400px) { 328 | .k-col.k-c-9-s { 329 | width: calc(calc(100% - 180px) / 9); 330 | margin-right: 10px; 331 | margin-left: 10px; 332 | float: left; 333 | } 334 | } 335 | @media (min-width: 400px) { 336 | .k-col.k-c-10-s { 337 | width: calc(calc(100% - 200px) / 10); 338 | margin-right: 10px; 339 | margin-left: 10px; 340 | float: left; 341 | } 342 | } 343 | @media (min-width: 400px) { 344 | .k-col.k-c-11-s { 345 | width: calc(calc(100% - 220px) / 11); 346 | margin-right: 10px; 347 | margin-left: 10px; 348 | float: left; 349 | } 350 | } 351 | @media (min-width: 400px) { 352 | .k-col.k-c-12-s { 353 | width: calc(calc(100% - 240px) / 12); 354 | margin-right: 10px; 355 | margin-left: 10px; 356 | float: left; 357 | } 358 | } 359 | @media (min-width: 400px) { 360 | .k-col.k-c-13-s { 361 | width: calc(calc(100% - 260px) / 13); 362 | margin-right: 10px; 363 | margin-left: 10px; 364 | float: left; 365 | } 366 | } 367 | @media (min-width: 400px) { 368 | .k-col.k-c-14-s { 369 | width: calc(calc(100% - 280px) / 14); 370 | margin-right: 10px; 371 | margin-left: 10px; 372 | float: left; 373 | } 374 | } 375 | @media (min-width: 400px) { 376 | .k-col.k-c-15-s { 377 | width: calc(calc(100% - 300px) / 15); 378 | margin-right: 10px; 379 | margin-left: 10px; 380 | float: left; 381 | } 382 | } 383 | @media (min-width: 400px) { 384 | .k-col.k-c-16-s { 385 | width: calc(calc(100% - 320px) / 16); 386 | margin-right: 10px; 387 | margin-left: 10px; 388 | float: left; 389 | } 390 | } 391 | @media (min-width: 600px) { 392 | .k-col.k-c-1-m { 393 | width: calc(calc(100% - 20px) / 1); 394 | margin-right: 10px; 395 | margin-left: 10px; 396 | float: left; 397 | } 398 | } 399 | @media (min-width: 600px) { 400 | .k-col.k-c-2-m { 401 | width: calc(calc(100% - 40px) / 2); 402 | margin-right: 10px; 403 | margin-left: 10px; 404 | float: left; 405 | } 406 | } 407 | @media (min-width: 600px) { 408 | .k-col.k-c-3-m { 409 | width: calc(calc(100% - 60px) / 3); 410 | margin-right: 10px; 411 | margin-left: 10px; 412 | float: left; 413 | } 414 | } 415 | @media (min-width: 600px) { 416 | .k-col.k-c-4-m { 417 | width: calc(calc(100% - 80px) / 4); 418 | margin-right: 10px; 419 | margin-left: 10px; 420 | float: left; 421 | } 422 | } 423 | @media (min-width: 600px) { 424 | .k-col.k-c-5-m { 425 | width: calc(calc(100% - 100px) / 5); 426 | margin-right: 10px; 427 | margin-left: 10px; 428 | float: left; 429 | } 430 | } 431 | @media (min-width: 600px) { 432 | .k-col.k-c-6-m { 433 | width: calc(calc(100% - 120px) / 6); 434 | margin-right: 10px; 435 | margin-left: 10px; 436 | float: left; 437 | } 438 | } 439 | @media (min-width: 600px) { 440 | .k-col.k-c-7-m { 441 | width: calc(calc(100% - 140px) / 7); 442 | margin-right: 10px; 443 | margin-left: 10px; 444 | float: left; 445 | } 446 | } 447 | @media (min-width: 600px) { 448 | .k-col.k-c-8-m { 449 | width: calc(calc(100% - 160px) / 8); 450 | margin-right: 10px; 451 | margin-left: 10px; 452 | float: left; 453 | } 454 | } 455 | @media (min-width: 600px) { 456 | .k-col.k-c-9-m { 457 | width: calc(calc(100% - 180px) / 9); 458 | margin-right: 10px; 459 | margin-left: 10px; 460 | float: left; 461 | } 462 | } 463 | @media (min-width: 600px) { 464 | .k-col.k-c-10-m { 465 | width: calc(calc(100% - 200px) / 10); 466 | margin-right: 10px; 467 | margin-left: 10px; 468 | float: left; 469 | } 470 | } 471 | @media (min-width: 600px) { 472 | .k-col.k-c-11-m { 473 | width: calc(calc(100% - 220px) / 11); 474 | margin-right: 10px; 475 | margin-left: 10px; 476 | float: left; 477 | } 478 | } 479 | @media (min-width: 600px) { 480 | .k-col.k-c-12-m { 481 | width: calc(calc(100% - 240px) / 12); 482 | margin-right: 10px; 483 | margin-left: 10px; 484 | float: left; 485 | } 486 | } 487 | @media (min-width: 600px) { 488 | .k-col.k-c-13-m { 489 | width: calc(calc(100% - 260px) / 13); 490 | margin-right: 10px; 491 | margin-left: 10px; 492 | float: left; 493 | } 494 | } 495 | @media (min-width: 600px) { 496 | .k-col.k-c-14-m { 497 | width: calc(calc(100% - 280px) / 14); 498 | margin-right: 10px; 499 | margin-left: 10px; 500 | float: left; 501 | } 502 | } 503 | @media (min-width: 600px) { 504 | .k-col.k-c-15-m { 505 | width: calc(calc(100% - 300px) / 15); 506 | margin-right: 10px; 507 | margin-left: 10px; 508 | float: left; 509 | } 510 | } 511 | @media (min-width: 600px) { 512 | .k-col.k-c-16-m { 513 | width: calc(calc(100% - 320px) / 16); 514 | margin-right: 10px; 515 | margin-left: 10px; 516 | float: left; 517 | } 518 | } 519 | @media (min-width: 800px) { 520 | .k-col.k-c-1-l { 521 | width: calc(calc(100% - 20px) / 1); 522 | margin-right: 10px; 523 | margin-left: 10px; 524 | float: left; 525 | } 526 | } 527 | @media (min-width: 800px) { 528 | .k-col.k-c-2-l { 529 | width: calc(calc(100% - 40px) / 2); 530 | margin-right: 10px; 531 | margin-left: 10px; 532 | float: left; 533 | } 534 | } 535 | @media (min-width: 800px) { 536 | .k-col.k-c-3-l { 537 | width: calc(calc(100% - 60px) / 3); 538 | margin-right: 10px; 539 | margin-left: 10px; 540 | float: left; 541 | } 542 | } 543 | @media (min-width: 800px) { 544 | .k-col.k-c-4-l { 545 | width: calc(calc(100% - 80px) / 4); 546 | margin-right: 10px; 547 | margin-left: 10px; 548 | float: left; 549 | } 550 | } 551 | @media (min-width: 800px) { 552 | .k-col.k-c-5-l { 553 | width: calc(calc(100% - 100px) / 5); 554 | margin-right: 10px; 555 | margin-left: 10px; 556 | float: left; 557 | } 558 | } 559 | @media (min-width: 800px) { 560 | .k-col.k-c-6-l { 561 | width: calc(calc(100% - 120px) / 6); 562 | margin-right: 10px; 563 | margin-left: 10px; 564 | float: left; 565 | } 566 | } 567 | @media (min-width: 800px) { 568 | .k-col.k-c-7-l { 569 | width: calc(calc(100% - 140px) / 7); 570 | margin-right: 10px; 571 | margin-left: 10px; 572 | float: left; 573 | } 574 | } 575 | @media (min-width: 800px) { 576 | .k-col.k-c-8-l { 577 | width: calc(calc(100% - 160px) / 8); 578 | margin-right: 10px; 579 | margin-left: 10px; 580 | float: left; 581 | } 582 | } 583 | @media (min-width: 800px) { 584 | .k-col.k-c-9-l { 585 | width: calc(calc(100% - 180px) / 9); 586 | margin-right: 10px; 587 | margin-left: 10px; 588 | float: left; 589 | } 590 | } 591 | @media (min-width: 800px) { 592 | .k-col.k-c-10-l { 593 | width: calc(calc(100% - 200px) / 10); 594 | margin-right: 10px; 595 | margin-left: 10px; 596 | float: left; 597 | } 598 | } 599 | @media (min-width: 800px) { 600 | .k-col.k-c-11-l { 601 | width: calc(calc(100% - 220px) / 11); 602 | margin-right: 10px; 603 | margin-left: 10px; 604 | float: left; 605 | } 606 | } 607 | @media (min-width: 800px) { 608 | .k-col.k-c-12-l { 609 | width: calc(calc(100% - 240px) / 12); 610 | margin-right: 10px; 611 | margin-left: 10px; 612 | float: left; 613 | } 614 | } 615 | @media (min-width: 800px) { 616 | .k-col.k-c-13-l { 617 | width: calc(calc(100% - 260px) / 13); 618 | margin-right: 10px; 619 | margin-left: 10px; 620 | float: left; 621 | } 622 | } 623 | @media (min-width: 800px) { 624 | .k-col.k-c-14-l { 625 | width: calc(calc(100% - 280px) / 14); 626 | margin-right: 10px; 627 | margin-left: 10px; 628 | float: left; 629 | } 630 | } 631 | @media (min-width: 800px) { 632 | .k-col.k-c-15-l { 633 | width: calc(calc(100% - 300px) / 15); 634 | margin-right: 10px; 635 | margin-left: 10px; 636 | float: left; 637 | } 638 | } 639 | @media (min-width: 800px) { 640 | .k-col.k-c-16-l { 641 | width: calc(calc(100% - 320px) / 16); 642 | margin-right: 10px; 643 | margin-left: 10px; 644 | float: left; 645 | } 646 | } 647 | @media (min-width: 1080px) { 648 | .k-col.k-c-1-xl { 649 | width: calc(calc(100% - 20px) / 1); 650 | margin-right: 10px; 651 | margin-left: 10px; 652 | float: left; 653 | } 654 | } 655 | @media (min-width: 1080px) { 656 | .k-col.k-c-2-xl { 657 | width: calc(calc(100% - 40px) / 2); 658 | margin-right: 10px; 659 | margin-left: 10px; 660 | float: left; 661 | } 662 | } 663 | @media (min-width: 1080px) { 664 | .k-col.k-c-3-xl { 665 | width: calc(calc(100% - 60px) / 3); 666 | margin-right: 10px; 667 | margin-left: 10px; 668 | float: left; 669 | } 670 | } 671 | @media (min-width: 1080px) { 672 | .k-col.k-c-4-xl { 673 | width: calc(calc(100% - 80px) / 4); 674 | margin-right: 10px; 675 | margin-left: 10px; 676 | float: left; 677 | } 678 | } 679 | @media (min-width: 1080px) { 680 | .k-col.k-c-5-xl { 681 | width: calc(calc(100% - 100px) / 5); 682 | margin-right: 10px; 683 | margin-left: 10px; 684 | float: left; 685 | } 686 | } 687 | @media (min-width: 1080px) { 688 | .k-col.k-c-6-xl { 689 | width: calc(calc(100% - 120px) / 6); 690 | margin-right: 10px; 691 | margin-left: 10px; 692 | float: left; 693 | } 694 | } 695 | @media (min-width: 1080px) { 696 | .k-col.k-c-7-xl { 697 | width: calc(calc(100% - 140px) / 7); 698 | margin-right: 10px; 699 | margin-left: 10px; 700 | float: left; 701 | } 702 | } 703 | @media (min-width: 1080px) { 704 | .k-col.k-c-8-xl { 705 | width: calc(calc(100% - 160px) / 8); 706 | margin-right: 10px; 707 | margin-left: 10px; 708 | float: left; 709 | } 710 | } 711 | @media (min-width: 1080px) { 712 | .k-col.k-c-9-xl { 713 | width: calc(calc(100% - 180px) / 9); 714 | margin-right: 10px; 715 | margin-left: 10px; 716 | float: left; 717 | } 718 | } 719 | @media (min-width: 1080px) { 720 | .k-col.k-c-10-xl { 721 | width: calc(calc(100% - 200px) / 10); 722 | margin-right: 10px; 723 | margin-left: 10px; 724 | float: left; 725 | } 726 | } 727 | @media (min-width: 1080px) { 728 | .k-col.k-c-11-xl { 729 | width: calc(calc(100% - 220px) / 11); 730 | margin-right: 10px; 731 | margin-left: 10px; 732 | float: left; 733 | } 734 | } 735 | @media (min-width: 1080px) { 736 | .k-col.k-c-12-xl { 737 | width: calc(calc(100% - 240px) / 12); 738 | margin-right: 10px; 739 | margin-left: 10px; 740 | float: left; 741 | } 742 | } 743 | @media (min-width: 1080px) { 744 | .k-col.k-c-13-xl { 745 | width: calc(calc(100% - 260px) / 13); 746 | margin-right: 10px; 747 | margin-left: 10px; 748 | float: left; 749 | } 750 | } 751 | @media (min-width: 1080px) { 752 | .k-col.k-c-14-xl { 753 | width: calc(calc(100% - 280px) / 14); 754 | margin-right: 10px; 755 | margin-left: 10px; 756 | float: left; 757 | } 758 | } 759 | @media (min-width: 1080px) { 760 | .k-col.k-c-15-xl { 761 | width: calc(calc(100% - 300px) / 15); 762 | margin-right: 10px; 763 | margin-left: 10px; 764 | float: left; 765 | } 766 | } 767 | @media (min-width: 1080px) { 768 | .k-col.k-c-16-xl { 769 | width: calc(calc(100% - 320px) / 16); 770 | margin-right: 10px; 771 | margin-left: 10px; 772 | float: left; 773 | } 774 | } 775 | -------------------------------------------------------------------------------- /public/graphs/css/notes.css: -------------------------------------------------------------------------------- 1 | #graph { 2 | height: 100vh; 3 | width: 100%; 4 | /* background-color: #222222; */ 5 | background-color: #fa99f0; 6 | } 7 | -------------------------------------------------------------------------------- /public/graphs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Graphs 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /public/graphs/js/notes.js: -------------------------------------------------------------------------------- 1 | var nodes = null; 2 | var edges = null; 3 | var network = null; 4 | var selectedNodeId = 1; 5 | 6 | var NOTES = ['C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4']; 7 | 8 | var G = { 9 | '0': [0, 0, 2, 4], 10 | '1': [1, 1, 3, 5], 11 | '2': [2, 2, 4, 6], 12 | '3': [4], 13 | '4': [4, 0], 14 | '5': [0], 15 | '6': [1, 0], 16 | }; 17 | 18 | var MC = new MarkovChain(G, NOTES, 0); 19 | 20 | // create an array with nodes 21 | function makeNodes(obj, labels) { 22 | var nodeStyle = { 23 | font: { 24 | color: '#000000', 25 | face: 'sans-serif', 26 | size: 20, 27 | }, 28 | color: { 29 | highlight: { 30 | border: '#ccfa99', 31 | background: '#faf099', 32 | }, 33 | }, 34 | }; 35 | const ids = Object.keys(obj).map(Number); 36 | return ids.map(id => { 37 | return { 38 | id, 39 | value: obj[id].length, // how many edges 40 | label: labels[id], 41 | ...nodeStyle, 42 | }; 43 | }); 44 | } 45 | 46 | function makeEdges(obj) { 47 | const edgeOptions = { 48 | arrows: { 49 | to: { 50 | enabled: true, 51 | }, 52 | }, 53 | color: { 54 | highlight: '#99f9fa', 55 | }, 56 | }; 57 | 58 | const ids = Object.keys(obj).map(Number); 59 | const edges = []; 60 | 61 | // Make matrix 62 | ids.forEach(id => { 63 | let values = {}; 64 | obj[id].forEach(edge => { 65 | if (!values[edge]) { 66 | values[edge] = 1; 67 | } else { 68 | values[edge] += 1; 69 | } 70 | }); 71 | 72 | Object.keys(values).forEach(k => { 73 | edges.push({ 74 | from: id, 75 | to: Number(k), 76 | value: values[k], 77 | ...edgeOptions, 78 | }); 79 | }); 80 | }); 81 | 82 | return edges; 83 | } 84 | 85 | function draw() { 86 | nodes = makeNodes(G, NOTES); 87 | 88 | edges = makeEdges(G); 89 | 90 | // create a network 91 | var container = document.getElementById('graph'); 92 | var data = { 93 | nodes: nodes, 94 | edges: edges, 95 | }; 96 | var options = { 97 | nodes: { 98 | shape: 'dot', 99 | size: 10, 100 | }, 101 | }; 102 | network = new vis.Network(container, data, options); 103 | } 104 | 105 | draw(); 106 | 107 | network.fit(nodes.map(n => n.id)); 108 | 109 | network.selectNodes([MC.peekID()]); 110 | 111 | setInterval(() => { 112 | selectedNodeId = MC.nextID(); 113 | network.selectNodes([selectedNodeId]); 114 | }, 1000); 115 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | osc.js Web Socket Demo 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |

OSC.js Visualizers

13 |
14 |
15 |
16 | Matrix 16x8 17 | P5.js Demo 18 |
19 |
20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /public/js/slides-setup.js: -------------------------------------------------------------------------------- 1 | var BLANK_PAGE_URL = '/public/blank.html'; 2 | var slideshow = remark.create({ 3 | sourceUrl: '/public/sqcr-demo/md/generative-hip-hop-slides.md', 4 | highlightStyle: 'monokai', 5 | touch: false, 6 | }); 7 | 8 | function killIframes() { 9 | // Blank out all iframes 10 | const iframes = document.querySelectorAll('iframe'); 11 | Array.from(iframes).forEach(el => { 12 | el.src = BLANK_PAGE_URL; 13 | }); 14 | } 15 | 16 | slideshow.on('showSlide', function(slide) { 17 | if (!!window.DISABLE_IFRAMES) return; 18 | const { 19 | properties = { iframeURL: null, iframeSelector: null }, 20 | properties: { iframeURL, iframeSelector }, 21 | } = slide; 22 | 23 | if (iframeSelector) { 24 | const el = document.querySelector(iframeSelector); 25 | if (el) { 26 | el.src = iframeURL; 27 | } 28 | } 29 | // Slide is the slide being navigated to 30 | }); 31 | 32 | slideshow.on('hideSlide', function(slide) { 33 | killIframes(); 34 | }); 35 | 36 | // Prevent duplicate iframes in presenter + child window mode 37 | if (window.opener) { 38 | window.opener.postMessage('mute', window.location.origin); 39 | console.log('Muting parent window by disabling iframes.'); 40 | } 41 | 42 | window.addEventListener( 43 | 'message', 44 | e => { 45 | if (e && e.data === 'mute') { 46 | window.DISABLE_IFRAMES = true; 47 | console.log('Muting window and disabling iframes.'); 48 | killIframes(); 49 | } 50 | }, 51 | false, 52 | ); 53 | -------------------------------------------------------------------------------- /public/matrix-16x8/example-sonic-pi-driver.rb: -------------------------------------------------------------------------------- 1 | ############################################################# 2 | ### ### 3 | ### Made using SonicPi 3.1 ### 4 | ### ### 5 | ### by Omar Delarosa (https://omardelarosa.com) ### 6 | ### ### 7 | ############################################################# 8 | 9 | use_bpm 70 10 | T = 4.0 11 | 12 | use_osc "192.168.1.7", 57121 # this synchronizes with the OSC server in the window. <- 13 | 14 | # State machine utility functions 15 | define :markov do |a, h| h[a].sample; end # Chooses the next state at random from hash 16 | define :g do |k| get[k]; end # simplified root note in scale getter 17 | define :s do |k, n| set k, n; end # simplified root note setter 18 | define :mnote do |key,chain| s key, (markov (g key), chain); g key; end 19 | 20 | # Pattern parsing 21 | define :ringify do |p| p.gsub(' ', '') .split('').map(&:to_i).ring; end 22 | 23 | # Melody builder 24 | define :make_melody do |len = 16, rng = 2| 25 | (1..len).map{|n| ((rng*-1)..rng).to_a.sample }.ring 26 | end 27 | 28 | # Initialize states 29 | set :k, 1 30 | set :b, 0 31 | set :s, 0 32 | set :y, 0 33 | set :v, 4 34 | set :h, 0 35 | set :l_lead, 0 36 | set :l_hats, 0 37 | set :l_kick, 0 38 | set :l_snare, 0 39 | set :l_vox, 1 40 | 41 | # Note playing abstraction 42 | define :pplay do |n, d = 0.1, m = 0.0, r = 0.0, syn = :fm| 43 | with_fx :reverb, mix: r do 44 | use_synth syn 45 | play n, release: d, sustain: 0.1 46 | end 47 | end 48 | 49 | # Scale 50 | sc_root = :F2 51 | sc_type = :major 52 | sc = scale(sc_root, sc_type) 53 | 54 | # Chords in scale -- chords are defined here. 55 | chords = (1..7).map {|n| chord_degree n, sc_root, sc_type }.ring 56 | 57 | # Samples 58 | SAMPLE_PATH = '~/Dropbox/Code/Music/SonicPi/Samples/Songs/Drake/' 59 | 60 | # Elaboration/Arrangement 61 | elaboration_complexities = (knit, 2, 4, 3, 2, 4, 2) 62 | 63 | # Levels 64 | L = { 65 | ppiano: [0.0, 0.35].ring, 66 | kick: [0.0, 0.75].ring, 67 | snare: [0.0, 0.75].ring, 68 | hats: [0.0, 1.5].ring, 69 | vox: [0.0, 5.0].ring 70 | } 71 | 72 | # Levels --- This is a markov chain controlling track/instrument levels. 73 | LV = { 74 | 0 => [0,0,1,1], 75 | 1 => [1,1,1,0] 76 | } 77 | 78 | # Tone family 79 | TONES = [:pulse, :fm, :pretty_bell] 80 | 81 | # Rhythm patterns -- kick drum patterns, each one is a single "state" 82 | p1 = [ 83 | (ringify '4000 0000 0040 0000'), 84 | (ringify '4020 0000 4000 0000'), 85 | (ringify '4023 0010 4020 0000'), 86 | (ringify '4030 0020 4020 1000') 87 | ] 88 | 89 | # Kicks -- The markov chain controlling kick drum state transitions. 90 | B = { 91 | 0 => [0, 1, 0, 2], 92 | 1 => [0], 93 | 2 => [1, 0, 3], 94 | 3 => [0] 95 | } 96 | 97 | # Snare drum patterns. Each one is also a single state. 98 | p2 = [ 99 | (ringify '0000 4000'), 100 | (ringify '0100 4000'), 101 | (ringify '1000 4000'), 102 | (ringify '0100 4100'), 103 | (ringify '0001 3001') 104 | ] 105 | 106 | # Snares - markov chain (but really just a hash representation of transitions) 107 | S = { 108 | 0 => [0, 0, 0, 0, 1, 2, 3], 109 | 1 => [0, 0, 1], 110 | 2 => [1, 0, 4, 3], 111 | 3 => [0], 112 | 4 => [0] 113 | } 114 | 115 | # Chords -- chord transitions 116 | K = { 117 | 1 => [7], 118 | 2 => [1], 119 | 7 => [2, 5], 120 | 5 => [1, 6], 121 | 6 => [2], 122 | 2 => [5] 123 | } 124 | 125 | melodies = (1..4).map{|n| make_melody(16,2)}.ring 126 | 127 | # Melodies 128 | Y = { 129 | 0 => [1], 130 | 1 => [0, 1, 2], 131 | 2 => [1, 2], 132 | 3 => [1] 133 | } 134 | 135 | # Vox 136 | V = { 137 | 0 => [0,0,1], 138 | 1 => [1,1,2], 139 | 2 => [2,2,3], 140 | 3 => [3,3,4], 141 | 4 => [4,4,0] 142 | } 143 | 144 | # Hats 145 | hats_durations = [ 146 | (knit, T/16, 4), 147 | (knit, T/24, 6), 148 | (knit, T/16, 4), 149 | (knit, T/24, 6), 150 | (knit, T/16, 4), 151 | (knit, T/64, 8) 152 | ].ring 153 | 154 | H = { 155 | 0 => [1], 156 | 1 => [2], 157 | 2 => [3], 158 | 3 => [4], 159 | 4 => [5], 160 | 5 => [0], 161 | } 162 | 163 | define :pchord do |chr, d = T| 164 | with_fx :level, amp: 0.3 do 165 | ##| pplay chr[0], d, 0.8 166 | ##| pplay chr[1] + 24, d, 0.4 167 | ##| pplay chr[2], d, 0.8 168 | ##| pplay chr[3] + 12, d, 0.4 169 | end 170 | end 171 | 172 | live_loop :vox do 173 | s = mnote :v, V 174 | # Control vocal levels 175 | lv = mnote :l_vox, LV 176 | l = L[:vox][lv] 177 | with_fx :level, amp: l do 178 | with_fx :reverb, mix: 0.5 do 179 | sample SAMPLE_PATH, s 180 | end 181 | end 182 | sleep (T/1) * 2 183 | end 184 | 185 | live_loop :ppiano do 186 | chr = chords[mnote :k, K] 187 | pchord chr # Chord (left hand) 188 | complexity = elaboration_complexities.tick 189 | sleep T/(2**complexity) 190 | lv = mnote :l_lead, LV 191 | l = L[:ppiano][lv] 192 | t = TONES.sample 193 | melody = mnote :y, Y 194 | with_fx :echo, mix: 0.8 do 195 | (3*(complexity-1)-1).times do 196 | with_fx :level, amp: l do 197 | i = melodies[melody].tick 198 | pplay sc[i] + 24, (T/complexity-1), nil, nil, t 199 | 16.times do |n| 200 | # This controls the visualization synced to the lead. 201 | osc "/blink/#{(8 + i) % 8}/#{n}/0" if l > 0.0 202 | end 203 | sleep T/(2**complexity) 204 | end 205 | end 206 | end 207 | 208 | chr2 = chords[mnote :k, K] 209 | pchord chr2, T/4 # Chord 210 | with_fx :echo, mix: 0.8 do 211 | (1*(complexity-1)).times do 212 | with_fx :level, amp: l do 213 | i = melodies[melody].tick 214 | pplay sc[i] + 24, (T/complexity-1), nil, nil, t # Melody (right hand) 215 | 16.times do |n| 216 | osc "/blink/#{(8 + i) % 8}/#{n}/0" if l > 0.0 217 | end 218 | sleep T/(2**complexity) 219 | end 220 | end 221 | end 222 | end 223 | 224 | 225 | live_loop :kick do 226 | p = p1[mnote :b, B] 227 | lv = mnote :l_kick, LV 228 | l = L[:kick][lv] 229 | c = 0 230 | density (p.length / 2) do 231 | with_fx :level, amp: l do 232 | sample :bd_fat, amp: 3.5 if p.tick >= 1 233 | 8.times do |n| 234 | osc "/blink/#{n}/#{c}/1" if p.look >= 1 && l > 0.0 235 | end 236 | c+=1 237 | sleep 1 238 | end 239 | end 240 | end 241 | 242 | live_loop :hats do 243 | p = hats_durations[mnote :h, H] 244 | lv = mnote :l_hats, LV 245 | l = L[:hats][lv] 246 | c = 0 247 | density (p.length) do 248 | with_fx :level, amp: l do 249 | sample :elec_tick, amp: 1.5, decay: 0.1 250 | 8.times do |n| 251 | osc "/blink/#{n}/#{c}/2" if l > 0.0 252 | end 253 | c+=1 254 | sleep 1 255 | end 256 | end 257 | end 258 | 259 | # This controls the snares. 260 | live_loop :snare do 261 | # This mnote call fetches a pattern from the snare states. 262 | p = p2[mnote :s, S] 263 | # This mnote call fetches the level from the levels state for snare track. 264 | lv = mnote :l_snare, LV 265 | l = L[:snare][lv] 266 | c = 0 267 | density (p.length / 2) do 268 | with_fx :level, amp: l do 269 | sample :sn_dolf if p.tick >= 1 270 | 16.times do |n| 271 | osc "/blink/#{n}/#{c}/3" if p.look >= 1 && l > 0.0 272 | end 273 | c+=1 274 | sleep 1 275 | end 276 | end 277 | end -------------------------------------------------------------------------------- /public/matrix-16x8/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Matrix 16x8 - osc.js demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /public/matrix-16x8/matrix-visualizer.js: -------------------------------------------------------------------------------- 1 | let oscPort = {}; 2 | 3 | const handleMessage = (msg) => { 4 | console.log('message', msg); 5 | } 6 | 7 | const LightEvent = (data) => new CustomEvent('light', { detail: data }); 8 | 9 | const elementMatrix = []; 10 | 11 | const mget = (mat, x, y) => { 12 | return mat[x] && mat[x][y] || undefined; 13 | } 14 | 15 | const MAX_COLS = 16; 16 | const MAX_ROWS = 8; 17 | 18 | const blink = (el, dur = 100, color) => { 19 | const colorClass = `colorize-${color}`; 20 | // TODO: add random color 21 | el.classList.add(colorClass); 22 | 23 | setTimeout(() => { 24 | el.classList.remove(colorClass); 25 | }, dur); 26 | } 27 | 28 | // For debugging purposes. 29 | const ticker = () => { 30 | let n = 0; 31 | let m = 0; 32 | 33 | return (e) => { 34 | const el = elementMatrix[n][m]; 35 | blink(el); 36 | m++; 37 | if (m >= MAX_COLS) { 38 | m = 0; 39 | n++; 40 | } 41 | 42 | if (n >= MAX_ROWS) { 43 | n = 0; 44 | } 45 | }; 46 | } 47 | 48 | const initOSC = () => { 49 | // Init container 50 | const $container = document.querySelector('.container'); 51 | 52 | const $rows = document.querySelectorAll('.k-row'); 53 | 54 | // TODO: make this more widely compatible and avoid .forEach 55 | $rows.forEach(($row) => { 56 | const rowRefs = []; 57 | $row.querySelectorAll('.k-col').forEach($cell => { 58 | rowRefs.push($cell); 59 | }); 60 | elementMatrix.push(rowRefs); 61 | }); 62 | 63 | $container.addEventListener('light', (e) => { 64 | const detail = e.detail || {}; 65 | const { address = '', args = [] } = detail; 66 | const addressParts = address.split('/'); 67 | if (addressParts[1] === 'blink') { 68 | const [,,m, n, color] = addressParts; 69 | const mNum = Number(m); 70 | const nNum = Number(n); 71 | const el = mget(elementMatrix,mNum,nNum); 72 | el && blink(el, 100, color); 73 | } 74 | }); 75 | 76 | // Init port 77 | oscPort = new osc.WebSocketPort({ 78 | url: "ws://localhost:8081" 79 | }); 80 | 81 | 82 | // listen 83 | oscPort.on('message', (msg) => { 84 | // handleMessage(msg); // Debugging 85 | 86 | $container.dispatchEvent(LightEvent(msg)); 87 | }); 88 | 89 | // open port 90 | oscPort.open(); 91 | }; 92 | 93 | window.initOSC = initOSC; 94 | -------------------------------------------------------------------------------- /public/matrix-16x8/styles.css: -------------------------------------------------------------------------------- 1 | div { 2 | min-height: 80px; 3 | } 4 | 5 | .container { 6 | margin-left: auto; 7 | margin-right: auto; 8 | } 9 | 10 | .k-row { 11 | margin-left: auto; 12 | margin-right: auto; 13 | max-width: 1600px; 14 | } 15 | 16 | .k-col { 17 | color: white; 18 | height: 80px; 19 | margin-bottom: 10px; 20 | margin-top: 10px; 21 | } 22 | 23 | .red-box { 24 | background-color: red; 25 | filter: blur(2px); 26 | } 27 | 28 | .blank-box { 29 | background-color: white; 30 | } 31 | 32 | .colorize { 33 | background-color: yellow; 34 | } 35 | 36 | .colorize-0 { 37 | background-color: red; 38 | /* filter: blur(2px); */ 39 | } 40 | 41 | .colorize-1 { 42 | background-color: green; 43 | /* filter: blur(2px); */ 44 | } 45 | 46 | .colorize-2 { 47 | background-color: yellow; 48 | /* filter: blur(2px); */ 49 | } 50 | 51 | .colorize-3 { 52 | background-color: blue; 53 | /* filter: blur(2px); */ 54 | } 55 | 56 | .colorize-4 { 57 | background-color: pink; 58 | /* filter: blur(2px); */ 59 | } 60 | 61 | .colorize-1.colorize-3 { 62 | background-color: orange; 63 | } 64 | 65 | .colorize-2.colorize-3 { 66 | background-color: green; 67 | } 68 | 69 | .colorize-4.colorize-1 { 70 | background-color: teal; 71 | } 72 | 73 | .colorize-2.colorize-1 { 74 | background-color: orange; 75 | } 76 | -------------------------------------------------------------------------------- /public/samples/BD.WAV: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omardelarosa/markov-music-js/e7de4956ff0fcf81874b1cb9d6a44f5a9172e025/public/samples/BD.WAV -------------------------------------------------------------------------------- /public/samples/CB.WAV: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omardelarosa/markov-music-js/e7de4956ff0fcf81874b1cb9d6a44f5a9172e025/public/samples/CB.WAV -------------------------------------------------------------------------------- /public/samples/CH.WAV: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omardelarosa/markov-music-js/e7de4956ff0fcf81874b1cb9d6a44f5a9172e025/public/samples/CH.WAV -------------------------------------------------------------------------------- /public/samples/CL.WAV: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omardelarosa/markov-music-js/e7de4956ff0fcf81874b1cb9d6a44f5a9172e025/public/samples/CL.WAV -------------------------------------------------------------------------------- /public/samples/CP.WAV: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omardelarosa/markov-music-js/e7de4956ff0fcf81874b1cb9d6a44f5a9172e025/public/samples/CP.WAV -------------------------------------------------------------------------------- /public/samples/OH.WAV: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omardelarosa/markov-music-js/e7de4956ff0fcf81874b1cb9d6a44f5a9172e025/public/samples/OH.WAV -------------------------------------------------------------------------------- /public/samples/SD.WAV: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omardelarosa/markov-music-js/e7de4956ff0fcf81874b1cb9d6a44f5a9172e025/public/samples/SD.WAV -------------------------------------------------------------------------------- /public/samples/hotline-1-4s.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omardelarosa/markov-music-js/e7de4956ff0fcf81874b1cb9d6a44f5a9172e025/public/samples/hotline-1-4s.wav -------------------------------------------------------------------------------- /public/samples/hotline-2-4s.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omardelarosa/markov-music-js/e7de4956ff0fcf81874b1cb9d6a44f5a9172e025/public/samples/hotline-2-4s.wav -------------------------------------------------------------------------------- /public/samples/hotline-3-7s.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omardelarosa/markov-music-js/e7de4956ff0fcf81874b1cb9d6a44f5a9172e025/public/samples/hotline-3-7s.wav -------------------------------------------------------------------------------- /public/samples/hotline-4-3.5s.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omardelarosa/markov-music-js/e7de4956ff0fcf81874b1cb9d6a44f5a9172e025/public/samples/hotline-4-3.5s.wav -------------------------------------------------------------------------------- /public/samples/hotline-5-3.5s.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omardelarosa/markov-music-js/e7de4956ff0fcf81874b1cb9d6a44f5a9172e025/public/samples/hotline-5-3.5s.wav -------------------------------------------------------------------------------- /public/samples/thug-01.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omardelarosa/markov-music-js/e7de4956ff0fcf81874b1cb9d6a44f5a9172e025/public/samples/thug-01.wav -------------------------------------------------------------------------------- /public/samples/thug-02.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omardelarosa/markov-music-js/e7de4956ff0fcf81874b1cb9d6a44f5a9172e025/public/samples/thug-02.wav -------------------------------------------------------------------------------- /public/samples/thug-03.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omardelarosa/markov-music-js/e7de4956ff0fcf81874b1cb9d6a44f5a9172e025/public/samples/thug-03.wav -------------------------------------------------------------------------------- /public/sqcr-demo/buffers/init.js: -------------------------------------------------------------------------------- 1 | setTempo(60); 2 | sqcr.stop(); 3 | 4 | // Global pulse counter 5 | pulse = 0; 6 | 7 | // Format pattern shorthand 8 | fmt = s => 9 | s 10 | .replace(/\s/g, '') 11 | .split('') 12 | .map(Number); 13 | // Random element from arr 14 | _sample = arr => arr[parseInt(Math.random() * arr.length)]; 15 | 16 | // Utility for generating infinite counters 17 | nextOf = max => { 18 | let i = 0; 19 | return () => { 20 | return ++i % max; 21 | }; 22 | }; 23 | 24 | beatFromTick = t => Math.floor((t / (T / 4)) % 16); 25 | tickToMS = t => SQCR.Transport.cl; 26 | expectedMS = ticks => sqcr.tickToMS() * ticks; 27 | 28 | next4 = nextOf(4); 29 | 30 | kicks = [ 31 | fmt('4040 0000 0004 0000'), 32 | fmt('4000 0040 0040 0000'), 33 | // fmt('4020 0000 4000 0000'), 34 | // fmt('4020 0010 4020 0020'), 35 | // fmt('4030 0020 4020 1000') 36 | ]; 37 | 38 | kick_pattern = _sample(kicks); 39 | 40 | hats = [ 41 | [M / 16, 4], 42 | [M / 12, 3], 43 | [M / 24, 6], 44 | [M / 32, 4], 45 | [M / 48, 6], 46 | [M / 64, 8], 47 | ]; 48 | 49 | // Create markov chain for hats 50 | MC_HATS = new MarkovChain( 51 | { 52 | '0': [0, 0, 0, 0, 0, 0, 1, 2, 3, 4], 53 | '1': [0, 0, 0, 3], 54 | '2': [0, 0, 0, 3], 55 | '3': [2, 5], 56 | '4': [2, 3, 4, 1], 57 | '5': [3, 2, 4, 2, 2], 58 | }, 59 | hats, 60 | 0, 61 | ); 62 | 63 | // Keeps track of hi-hat hits (or tick substate) 64 | h_counter = 0; 65 | 66 | snares = [ 67 | fmt('0000 4000 0000 4000'), 68 | // fmt('0100 4000'), 69 | // fmt('1000 4000'), 70 | // fmt('0100 4100'), 71 | // fmt('0001 3011'), 72 | ]; 73 | 74 | snares_pattern = _sample(snares); 75 | 76 | LETTERS = 'ABCDEFG'; 77 | 78 | KEY = ['F4', 'major']; 79 | 80 | makeScale = note => Tonal.scale(KEY[1]).map(Tonal.transpose(note)); 81 | 82 | noteParse = note => { 83 | const parts = note.split(''); 84 | let letter; 85 | let isSharp = false; 86 | let oct = 0; 87 | // Has flat 88 | if (parts.length === 3) { 89 | let idx = LETTERS.indexOf(parts[0]); 90 | if (idx === -1) throw new Error('Invalid note: ' + note); 91 | else if (idx === 0) { 92 | idx = LETTERS.length - 1; 93 | } else { 94 | idx = idx - 1; 95 | } 96 | letter = LETTERS[idx]; 97 | isSharp = true; 98 | oct = parseInt(parts[2]); 99 | } else { 100 | letter = parts[0]; 101 | oct = parseInt(parts[1]); 102 | } 103 | return { 104 | note: letter + (isSharp ? '#' : ''), 105 | oct, 106 | }; 107 | }; 108 | 109 | // Make an array of notes 110 | scale = [ 111 | ...makeScale('F3'), 112 | ...makeScale('F4'), 113 | ...makeScale('F5'), 114 | ...makeScale('F6'), 115 | ]; 116 | chords = [0, 3, 4, 3, 2]; 117 | chord = 0; 118 | notes = [0, 2, 4, 6]; 119 | 120 | // Get MIDI outputs 121 | NOTE_KICK = 'A0'; 122 | NOTE_SNARE = 'A1'; 123 | NOTE_HAT = 'A2'; 124 | NOTE_CLAP = 'B1'; 125 | 126 | DRAKE = ['C2', 'D2', 'E2', 'F2', 'G2']; 127 | THUG = ['C3', 'D3', 'E4']; 128 | 129 | playInst = (inst, note, dur = 50) => { 130 | let timer; 131 | try { 132 | inst.triggerAttack(note); 133 | timer = setTimeout(() => { 134 | inst.triggerRelease(note); 135 | }, dur); 136 | } catch (e) { 137 | clearTimeout(timer); 138 | console.log('Instrument error!', e.message); 139 | } 140 | }; 141 | -------------------------------------------------------------------------------- /public/sqcr-demo/buffers/loops.js: -------------------------------------------------------------------------------- 1 | // Edit this file while running the server to update loops in real time 2 | 3 | colorLoopMapping = { 4 | synth: 3, 5 | leadSynth: 1, 6 | kicks: 2, 7 | hats: 0, 8 | snares: 4, 9 | }; 10 | 11 | loop('synth', async ctx => { 12 | // // Update chord state 13 | chord = chords[next4()]; 14 | const chr = notes.map(n => scale[n + chord]); 15 | chr.forEach(n => { 16 | const dur = 2 * 1000; 17 | playInst(synth, n, dur); 18 | const scaleNote = scale.indexOf(n) % 8; 19 | // Visualizer.blink( 20 | // `/blink/${scaleNote}/${pulse}/${colorLoopMapping[ctx.name]}/${dur}`, 21 | // ); 22 | for (let i = 0; i < 16; i++) { 23 | Visualizer.blink( 24 | `/blink/${scaleNote}/${i}/${colorLoopMapping[ctx.name]}/${dur}`, 25 | ); 26 | } 27 | }); 28 | 29 | ctx.sleep((M / 1) * 2); 30 | }); 31 | 32 | since = Date.now(); 33 | 34 | loop('leadSynth', async ctx => { 35 | const pulse = beatFromTick(ctx.tick); 36 | const n = _sample( 37 | notes.map(n => { 38 | return scale[n + chord]; 39 | }), 40 | ); 41 | const now = Date.now(); 42 | console.log('since', now - since); 43 | since = now; 44 | const scaleNote = scale.indexOf(n) % 8; 45 | Visualizer.blink( 46 | `/blink/${scaleNote}/${pulse}/${colorLoopMapping[ctx.name]}/100`, 47 | ); 48 | // console.log('N', scale.indexOf(n)); 49 | playInst(leadSynth, n, 100); 50 | ctx.sleep(T / 4); 51 | }); 52 | 53 | loop('blinker', async ctx => { 54 | const pulse = beatFromTick(ctx.tick); 55 | for (let i = 0; i < 8; i++) { 56 | Visualizer.blink(`/blink/${i}/${pulse}/0/${sqcr.tickToMS() * (T / 4)}`); 57 | } 58 | ctx.sleep(T / 4); 59 | }); 60 | 61 | loop('kicks', async ctx => { 62 | const pulse = beatFromTick(ctx.tick); 63 | // Switch pattern on the 0 64 | if (pulse === 0) kick_pattern = _sample(kicks); 65 | if (!kick_pattern[pulse]) return ctx.sleep(T / 4); 66 | 67 | // Play kick beat 68 | playInst(sampler, NOTE_KICK); 69 | 70 | ctx.sleep(T / 4); 71 | }); 72 | 73 | loop('hats', async ctx => { 74 | hats_pattern = hats[MC.get('HATS')]; 75 | max = hats_pattern[1]; 76 | t = hats_pattern[0]; 77 | 78 | playInst(sampler, NOTE_HAT); 79 | 80 | // Visualizer 81 | for (let i = 0; i < 8; i++) { 82 | Visualizer.blink(`/blink/${i}/${h_counter}/2/${sqcr.tickToMS() * t}`); 83 | } 84 | 85 | if (h_counter >= max - 1) { 86 | MC.set('HATS'); 87 | h_counter = 0; 88 | } else { 89 | h_counter++; 90 | } 91 | 92 | ctx.sleep(t); 93 | }); 94 | 95 | loop('snares', async ctx => { 96 | const pulse = beatFromTick(ctx.tick); 97 | // // Switch pattern on the 0 98 | if (pulse === 0) snares_pattern = _sample(snares); 99 | if (!snares_pattern[pulse]) return ctx.sleep(T / 4); 100 | playInst(sampler, NOTE_CLAP); 101 | ctx.sleep(T / 4); 102 | }); 103 | -------------------------------------------------------------------------------- /public/sqcr-demo/buffers/synths.js: -------------------------------------------------------------------------------- 1 | // Tone.js stuff 2 | freeverb = new Tone.Freeverb({ 3 | wet: 0.8, 4 | decay: '8n', 5 | }).toMaster(); 6 | 7 | reverb = new Tone.Reverb({ 8 | wet: 0.2, 9 | decay: '8n', 10 | }).toMaster(); 11 | 12 | delay = new Tone.FeedbackDelay(0.1); 13 | 14 | tremolo = new Tone.Tremolo(9, 0.75).toMaster().start(); 15 | 16 | feedbackDelay = new Tone.PingPongDelay({ 17 | delayTime: '8n', 18 | feedback: 0.6, 19 | wet: 0.2, 20 | }).toMaster(); 21 | 22 | synth = new Tone.PolySynth(6, Tone.Synth, { 23 | volume: 0.5, 24 | oscillator: { 25 | partials: [0, 2, 3, 4], 26 | }, 27 | }) 28 | .set({ 29 | filter: { 30 | type: 'highpass', 31 | }, 32 | envelope: { 33 | attack: 0.1, 34 | }, 35 | }) 36 | // .chain(tremolo, freeverb) 37 | .toMaster(); 38 | 39 | leadSynth = new Tone.PolySynth(6, Tone.Synth, { 40 | oscillator: { 41 | partials: [0, 2, 3, 4], 42 | }, 43 | }) 44 | .set({ 45 | filter: { 46 | type: 'highpass', 47 | }, 48 | envelope: { 49 | attack: 0.1, 50 | }, 51 | }) 52 | // .chain(delay, freeverb) 53 | // .connect(reverb) 54 | // .connect(feedbackDelay) 55 | .toMaster(); 56 | 57 | sampler = new Tone.Sampler( 58 | { 59 | [NOTE_KICK]: 'BD.WAV', // Kick 60 | [NOTE_SNARE]: 'SD.WAV', // Snare 61 | [NOTE_HAT]: 'CH.WAV', // Closed Hats 62 | [NOTE_CLAP]: 'CP.WAV', // Clap 63 | [DRAKE[0]]: 'hotline-1-4s.wav', 64 | [DRAKE[1]]: 'hotline-2-4s.wav', 65 | [DRAKE[2]]: 'hotline-3-7s.wav', 66 | [DRAKE[3]]: 'hotline-4-3.5s.wav', 67 | [DRAKE[4]]: 'hotline-5-3.5s.wav', 68 | [THUG[0]]: 'thug-01.wav', 69 | [THUG[1]]: 'thug-02.wav', 70 | [THUG[2]]: 'thug-03.wav', 71 | }, 72 | { 73 | release: 1, 74 | baseUrl: '/public/samples/', 75 | }, 76 | ) 77 | .connect(reverb) 78 | .toMaster(); 79 | -------------------------------------------------------------------------------- /public/sqcr-demo/config/matrix.json: -------------------------------------------------------------------------------- 1 | { 2 | "libs": [ 3 | "/node_modules/webmidi/webmidi.min.js", 4 | "/node_modules/osc/dist/osc-browser.js", 5 | "/node_modules/tonal/build/transpiled.js", 6 | "/node_modules/tone/build/Tone.min.js", 7 | "/node_modules/sqcr/lib/browser.js", 8 | "/public/sqcr-demo/sqcr-setup.js", 9 | "/public/sqcr-demo/buffers/init.js", 10 | "/public/sqcr-demo/buffers/synths.js", 11 | "/public/sqcr-demo/buffers/loops.js", 12 | "/public/sqcr-demo/visualizer.js" 13 | ], 14 | "rendererPath": "public/sqcr-demo/renderers/matrix-renderer.js" 15 | } 16 | -------------------------------------------------------------------------------- /public/sqcr-demo/config/presentation.json: -------------------------------------------------------------------------------- 1 | { 2 | "libs": [ 3 | "/node_modules/webmidi/webmidi.min.js", 4 | "/node_modules/osc/dist/osc-browser.js", 5 | "/node_modules/tonal/build/transpiled.js", 6 | "/node_modules/tone/build/Tone.min.js", 7 | "/node_modules/sqcr/lib/standalone.js", 8 | "/public/sqcr-demo/sqcr-setup.js", 9 | "/public/sqcr-demo/buffers/init.js", 10 | "/public/sqcr-demo/buffers/synths.js", 11 | "/public/sqcr-demo/buffers/loops.js", 12 | "/public/sqcr-demo/visualizer.js" 13 | ], 14 | "rendererPath": "public/sqcr-demo/renderers/slider.js", 15 | "locals": { 16 | "MARKDOWN_PATH": "/public/sqcr-demo/md/generative-hip-hop-slides.md" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /public/sqcr-demo/css/slides.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Arimo:400,400i,700,700i|Roboto+Mono:400,400i,700,700i'); 2 | 3 | /** 4 | * Color Pallette: 5 | * 1 - #ccfa99 6 | * 2 - #faf099 7 | * 3 - #99f0fa 8 | * 4 - #fa99f0 9 | * 5 - #cc99fa 10 | * 11 | */ 12 | 13 | :root { 14 | /* --main-bg-color: #000000; */ 15 | --main-bg-color: #fa99f0; 16 | /* --main-text-color: #ffffff; */ 17 | --main-text-color: #000000; 18 | --code-bg-color: #222222; 19 | --code-text-color: #ffffff; 20 | } 21 | 22 | body { 23 | font-family: 'Arimo', sans-serif; 24 | color: var(--main-text-color); 25 | background-color: var(--main-bg-color); 26 | } 27 | 28 | .remark-container, 29 | .remark-slide, 30 | .remark-slide-content { 31 | background-color: var(--main-bg-color); 32 | } 33 | 34 | .remark-slide-scaler { 35 | box-shadow: unset; 36 | -webkit-box-shadow: unset; 37 | } 38 | 39 | h1, 40 | h2, 41 | h3 { 42 | font-family: 'Arimo', sans-serif; 43 | } 44 | .remark-code, 45 | .remark-inline-code { 46 | font-family: 'Roboto Mono', monospace; 47 | background-color: var(--code-bg-color); 48 | color: var(--code-text-color); 49 | } 50 | -------------------------------------------------------------------------------- /public/sqcr-demo/html/808.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Untitled - osc.js demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 31 | 32 | 33 |
34 | 35 |
36 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /public/sqcr-demo/html/akai.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Untitled - osc.js demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 32 | 33 | 34 |
35 | 36 |
37 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /public/sqcr-demo/html/beats-graph.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Graphs 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /public/sqcr-demo/html/chords-graph.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Graphs 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /public/sqcr-demo/html/matrix-16x8.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Untitled - osc.js demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 | 173 |
174 |
175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /public/sqcr-demo/html/notes-graph.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Graphs 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /public/sqcr-demo/html/random-tones.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Untitled - osc.js demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 19 | 20 | 21 |
22 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /public/sqcr-demo/html/scale-tones.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Untitled - osc.js demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 19 | 20 | 21 |
22 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /public/sqcr-demo/js/chords-graph-synth.js: -------------------------------------------------------------------------------- 1 | var nodes = null; 2 | var edges = null; 3 | var network = null; 4 | var selectedNodeId = 1; 5 | 6 | var note = null; 7 | var duration = 2000; 8 | 9 | var CHORDS = [ 10 | 'C Major', 11 | 'D minor', 12 | 'E minor', 13 | 'F major', 14 | 'G major', 15 | 'A minor', 16 | 'B diminished', 17 | ]; 18 | 19 | var NOTES = [ 20 | 'C4', 21 | 'D4', 22 | 'E4', 23 | 'F4', 24 | 'G4', 25 | 'A4', 26 | 'B4', 27 | 'C4', 28 | 'D4', 29 | 'E4', 30 | 'F4', 31 | 'G4', 32 | 'A4', 33 | 'B4', 34 | ]; 35 | 36 | function rand() { 37 | return CHORDS[Math.round(Math.random() * (CHORDS.length - 1))]; 38 | } 39 | 40 | var synth = new Tone.PolySynth(3, Tone.Synth, { 41 | oscillator: { 42 | type: 'amtriangle', 43 | harmonicity: 0.5, 44 | modulationType: 'sine', 45 | }, 46 | envelope: { 47 | attackCurve: 'exponential', 48 | attack: 0.05, 49 | decay: 0.2, 50 | sustain: 0.2, 51 | release: 1.5, 52 | }, 53 | portamento: 0.05, 54 | }).toMaster(); 55 | 56 | var G = { 57 | '0': [3, 3, 3, 5], 58 | '1': [2, 5], 59 | '2': [3], 60 | '3': [4, 4, 4, 1, 1], 61 | '4': [0, 0, 0, 5], 62 | '5': [1, 6], 63 | '6': [4], 64 | }; 65 | 66 | var MC = new MarkovChain(G, CHORDS, 0); 67 | 68 | // create an array with nodes 69 | function makeNodes(obj, labels) { 70 | var nodeStyle = { 71 | font: { 72 | color: '#000000', 73 | face: 'sans-serif', 74 | size: 20, 75 | }, 76 | color: { 77 | highlight: { 78 | border: '#ccfa99', 79 | background: '#faf099', 80 | }, 81 | }, 82 | }; 83 | const ids = Object.keys(obj).map(Number); 84 | return ids.map(id => { 85 | return { 86 | id, 87 | value: obj[id].length, // how many edges 88 | label: labels[id], 89 | ...nodeStyle, 90 | }; 91 | }); 92 | } 93 | 94 | function makeEdges(obj) { 95 | const edgeOptions = { 96 | arrows: { 97 | to: { 98 | enabled: true, 99 | }, 100 | }, 101 | color: { 102 | highlight: '#99f9fa', 103 | }, 104 | }; 105 | 106 | const ids = Object.keys(obj).map(Number); 107 | const edges = []; 108 | 109 | // Make matrix 110 | ids.forEach(id => { 111 | let values = {}; 112 | obj[id].forEach(edge => { 113 | if (!values[edge]) { 114 | values[edge] = 1; 115 | } else { 116 | values[edge] += 1; 117 | } 118 | }); 119 | 120 | Object.keys(values).forEach(k => { 121 | edges.push({ 122 | from: id, 123 | to: Number(k), 124 | value: values[k], 125 | ...edgeOptions, 126 | }); 127 | }); 128 | }); 129 | 130 | return edges; 131 | } 132 | 133 | function draw() { 134 | nodes = makeNodes(G, CHORDS); 135 | 136 | edges = makeEdges(G); 137 | 138 | // create a network 139 | var container = document.getElementById('graph'); 140 | var data = { 141 | nodes: nodes, 142 | edges: edges, 143 | }; 144 | var options = { 145 | nodes: { 146 | shape: 'dot', 147 | size: 10, 148 | }, 149 | }; 150 | network = new vis.Network(container, data, options); 151 | } 152 | 153 | draw(); 154 | 155 | network.fit(nodes.map(n => n.id)); 156 | 157 | network.selectNodes([MC.peekID()]); 158 | 159 | setInterval(() => { 160 | selectedNodeId = MC.nextID(); 161 | network.selectNodes([selectedNodeId]); 162 | note = NOTES[selectedNodeId]; 163 | note2 = NOTES[selectedNodeId + 2]; 164 | note3 = NOTES[selectedNodeId + 4]; 165 | const notes = [note, note2, note3]; 166 | console.log('NOTES', notes, duration); 167 | synth.triggerAttackRelease(notes, duration); 168 | }, duration); 169 | -------------------------------------------------------------------------------- /public/sqcr-demo/js/hats-graph-synth.js: -------------------------------------------------------------------------------- 1 | var nodes = null; 2 | var edges = null; 3 | var network = null; 4 | var selectedNodeId = 1; 5 | 6 | var note = null; 7 | 8 | var HATS = [ 9 | [M / 16, 4], 10 | [M / 12, 3], 11 | [M / 24, 6], 12 | [M / 32, 4], 13 | [M / 48, 6], 14 | [M / 64, 8], 15 | ]; 16 | 17 | var HATS_STR = ['1/16', '1/12', '1/24', '1/32', '1/48', '1/64']; 18 | 19 | var G = { 20 | '0': [0, 0, 0, 0, 0, 0, 1, 2, 3, 4], 21 | '1': [0, 0, 0, 3], 22 | '2': [0, 0, 0, 3], 23 | '3': [2, 5], 24 | '4': [2, 3, 4, 1], 25 | '5': [3, 2, 4, 2, 2], 26 | }; 27 | 28 | var synth = new Tone.PolySynth(3, Tone.Synth, { 29 | oscillator: { 30 | type: 'amtriangle', 31 | harmonicity: 0.5, 32 | modulationType: 'sine', 33 | }, 34 | envelope: { 35 | attackCurve: 'exponential', 36 | attack: 0.05, 37 | decay: 0.2, 38 | sustain: 0.2, 39 | release: 1.5, 40 | }, 41 | portamento: 0.05, 42 | }).toMaster(); 43 | 44 | var MC = new MarkovChain(G, HATS, 0); 45 | 46 | // create an array with nodes 47 | function makeNodes(obj, labels) { 48 | var nodeStyle = { 49 | font: { 50 | color: '#000000', 51 | face: 'sans-serif', 52 | size: 20, 53 | }, 54 | color: { 55 | highlight: { 56 | border: '#ccfa99', 57 | background: '#faf099', 58 | }, 59 | }, 60 | }; 61 | const ids = Object.keys(obj).map(Number); 62 | return ids.map(id => { 63 | return { 64 | id, 65 | value: obj[id].length, // how many edges 66 | label: labels[id], 67 | ...nodeStyle, 68 | }; 69 | }); 70 | } 71 | 72 | function makeEdges(obj) { 73 | const edgeOptions = { 74 | arrows: { 75 | to: { 76 | enabled: true, 77 | }, 78 | }, 79 | color: { 80 | highlight: '#99f9fa', 81 | }, 82 | }; 83 | 84 | const ids = Object.keys(obj).map(Number); 85 | const edges = []; 86 | 87 | // Make matrix 88 | ids.forEach(id => { 89 | let values = {}; 90 | obj[id].forEach(edge => { 91 | if (!values[edge]) { 92 | values[edge] = 1; 93 | } else { 94 | values[edge] += 1; 95 | } 96 | }); 97 | 98 | Object.keys(values).forEach(k => { 99 | edges.push({ 100 | from: id, 101 | to: Number(k), 102 | value: values[k], 103 | ...edgeOptions, 104 | }); 105 | }); 106 | }); 107 | 108 | return edges; 109 | } 110 | 111 | function draw() { 112 | nodes = makeNodes(G, HATS_STR); 113 | 114 | edges = makeEdges(G); 115 | 116 | // create a network 117 | var container = document.getElementById('graph'); 118 | var data = { 119 | nodes: nodes, 120 | edges: edges, 121 | }; 122 | var options = { 123 | nodes: { 124 | shape: 'dot', 125 | size: 10, 126 | }, 127 | }; 128 | network = new vis.Network(container, data, options); 129 | } 130 | 131 | draw(); 132 | 133 | network.fit(nodes.map(n => n.id)); 134 | 135 | network.selectNodes([MC.peekID()]); 136 | 137 | // SQCR Stuff 138 | setTempo(60); 139 | 140 | // Get MIDI outputs 141 | NOTE_HAT = 'A2'; 142 | 143 | sampler = new Tone.Sampler( 144 | { 145 | [NOTE_HAT]: 'CH.WAV', // Closed Hats 146 | }, 147 | { 148 | release: 1, 149 | baseUrl: '/public/samples/', 150 | }, 151 | ).toMaster(); 152 | 153 | h_counter = 0; 154 | 155 | playInst = (inst, note, dur = 50) => { 156 | let timer; 157 | try { 158 | inst.triggerAttack(note); 159 | } catch (e) { 160 | console.log('Instrument error!', e.message); 161 | } 162 | }; 163 | 164 | hats = [ 165 | [M / 16, 4], 166 | [M / 12, 3], 167 | [M / 24, 6], 168 | [M / 32, 4], 169 | [M / 48, 6], 170 | [M / 64, 8], 171 | ]; 172 | 173 | loop('hats', async ctx => { 174 | hats_pattern = hats[MC.peekID()]; 175 | max = hats_pattern[1]; 176 | t = hats_pattern[0]; 177 | 178 | playInst(sampler, NOTE_HAT); 179 | 180 | if (h_counter >= max - 1) { 181 | const nid = MC.nextID(); 182 | network.selectNodes([nid]); 183 | h_counter = 0; 184 | } else { 185 | h_counter++; 186 | } 187 | 188 | ctx.sleep(t); 189 | }); 190 | -------------------------------------------------------------------------------- /public/sqcr-demo/js/matrix-16x8-init.js: -------------------------------------------------------------------------------- 1 | setTempo(60); 2 | 3 | // Global pulse counter 4 | pulse = 0; 5 | 6 | // Format pattern shorthand 7 | fmt = s => 8 | s 9 | .replace(/\s/g, '') 10 | .split('') 11 | .map(Number); 12 | // Random element from arr 13 | _sample = arr => arr[parseInt(Math.random() * arr.length)]; 14 | 15 | // Utility for generating infinite counters 16 | nextOf = max => { 17 | let i = 0; 18 | return () => { 19 | return ++i % max; 20 | }; 21 | }; 22 | 23 | beatFromTick = t => Math.floor((t / (T / 4)) % 16); 24 | tickToMS = t => SQCR.Transport.cl; 25 | expectedMS = ticks => sqcr.tickToMS() * ticks; 26 | 27 | next4 = nextOf(4); 28 | 29 | LOOPS = ['synth', 'leadSynth', 'kicks', 'snares', 'hats', 'vocals']; 30 | LEVELS = { 31 | synth: 1, 32 | leadSynth: 1, 33 | kicks: 1, 34 | snares: 0, 35 | hats: 1, 36 | vocals: 0, 37 | }; 38 | 39 | MC_LEVELS = new MarkovChain( 40 | { 41 | '1': [1, 1, 1, 0, 0], 42 | '0': [0, 0, 1, 1, 1], 43 | }, 44 | [1, 0], 45 | 1, 46 | ); 47 | 48 | kicks = [ 49 | fmt('4000 0000 0040 0000'), 50 | fmt('4020 0000 4000 0000'), 51 | fmt('4023 0010 4020 0000'), 52 | fmt('4030 0020 4020 1000'), 53 | ]; 54 | 55 | MC_KICKS = new MarkovChain( 56 | { 57 | '0': [0, 1, 0, 2], 58 | '1': [0], 59 | '2': [1, 0, 3], 60 | '3': [0], 61 | }, 62 | kicks, 63 | 0, 64 | ); 65 | 66 | hats = [ 67 | [M / 16, 4], 68 | [M / 12, 3], 69 | [M / 24, 6], 70 | [M / 32, 4], 71 | [M / 48, 6], 72 | [M / 64, 8], 73 | ]; 74 | 75 | // Create markov chain for hats 76 | MC_HATS = new MarkovChain( 77 | { 78 | '0': [0, 0, 0, 0, 0, 0, 1, 2, 3, 4], 79 | '1': [0, 0, 0, 3], 80 | '2': [0, 0, 0, 3], 81 | '3': [2, 5], 82 | '4': [2, 3, 4, 1], 83 | '5': [3, 2, 4, 2, 2], 84 | }, 85 | hats, 86 | 0, 87 | ); 88 | 89 | // Keeps track of hi-hat hits (or tick substate) 90 | h_counter = 0; 91 | 92 | snares = [ 93 | fmt('0000 4000'), 94 | fmt('0100 4000'), 95 | fmt('1000 4000'), 96 | fmt('0100 4100'), 97 | fmt('0001 3001'), 98 | ]; 99 | 100 | MC_SNARES = new MarkovChain( 101 | { 102 | '0': [0, 0, 0, 0, 1, 2, 3], 103 | '1': [0, 0, 1], 104 | '2': [1, 0, 4, 3], 105 | '3': [0], 106 | '4': [0], 107 | }, 108 | snares, 109 | 0, 110 | ); 111 | 112 | LETTERS = 'ABCDEFG'; 113 | 114 | KEY = ['F4', 'major']; 115 | 116 | makeScale = note => Tonal.scale(KEY[1]).map(Tonal.transpose(note)); 117 | 118 | // Make an array of notes 119 | scale = [ 120 | ...makeScale('F3'), 121 | ...makeScale('F4'), 122 | ...makeScale('F5'), 123 | ...makeScale('F6'), 124 | ]; 125 | 126 | chordNotesOf = deg => [ 127 | scale[deg], 128 | scale[deg + 2], 129 | scale[deg + 4], 130 | scale[deg + 6], 131 | ]; 132 | 133 | chords = [0, 1, 2, 3, 4, 5, 6, 7, 8]; 134 | 135 | MC_CHORDS = new MarkovChain( 136 | { 137 | '0': [4], 138 | '1': [7], 139 | '2': [1], 140 | '3': [2, 2, 4, 4, 0], 141 | '4': [3, 3, 6, 6, 0], 142 | '7': [2, 5], 143 | '5': [1, 6], 144 | '6': [2], 145 | }, 146 | chords, 147 | 0, 148 | ); 149 | 150 | melody = []; 151 | 152 | for (let i = 0; i < 16; i++) { 153 | melody.push(Math.round(Math.random() * 16) % 8); 154 | } 155 | 156 | console.log('melody', melody); 157 | 158 | notes = [0, 2, 4, 6]; 159 | 160 | // Get MIDI outputs 161 | NOTE_KICK = 'A0'; 162 | NOTE_SNARE = 'A1'; 163 | NOTE_HAT = 'A2'; 164 | NOTE_CLAP = 'B1'; 165 | 166 | DRAKE = ['C2', 'D2', 'E2', 'F2', 'G2']; 167 | THUG = ['C3', 'D3', 'E4']; 168 | 169 | playInst = (inst, note, dur = 50) => { 170 | let timer; 171 | try { 172 | inst.triggerAttack(note); 173 | timer = setTimeout(() => { 174 | inst.triggerRelease(note); 175 | }, dur); 176 | } catch (e) { 177 | clearTimeout(timer); 178 | console.log('Instrument error!', e.message); 179 | } 180 | }; 181 | -------------------------------------------------------------------------------- /public/sqcr-demo/js/matrix-16x8-synths.js: -------------------------------------------------------------------------------- 1 | // Tone.js stuff 2 | freeverb = new Tone.Freeverb({ 3 | wet: 0.8, 4 | decay: '8n', 5 | }).toMaster(); 6 | 7 | reverb = new Tone.Reverb({ 8 | wet: 0.2, 9 | decay: '8n', 10 | }).toMaster(); 11 | 12 | delay = new Tone.FeedbackDelay(0.1); 13 | 14 | tremolo = new Tone.Tremolo(9, 0.75).toMaster().start(); 15 | 16 | feedbackDelay = new Tone.PingPongDelay({ 17 | delayTime: '8n', 18 | feedback: 0.6, 19 | wet: 0.2, 20 | }).toMaster(); 21 | 22 | synth = new Tone.PolySynth(6, Tone.Synth, { 23 | volume: 1, 24 | oscillator: { 25 | partials: [0, 2, 3, 4], 26 | }, 27 | }) 28 | .set({ 29 | filter: { 30 | type: 'highpass', 31 | }, 32 | envelope: { 33 | attack: 0.1, 34 | }, 35 | }) 36 | // .chain(tremolo, freeverb) 37 | .toMaster(); 38 | 39 | leadSynth = new Tone.PolySynth(6, Tone.Synth, { 40 | oscillator: { 41 | partials: [0, 2, 3, 4], 42 | }, 43 | }) 44 | .set({ 45 | filter: { 46 | type: 'highpass', 47 | }, 48 | envelope: { 49 | attack: 0.1, 50 | }, 51 | }) 52 | // .chain(delay, freeverb) 53 | // .connect(reverb) 54 | // .connect(feedbackDelay) 55 | .toMaster(); 56 | 57 | sampler = new Tone.Sampler( 58 | { 59 | [NOTE_KICK]: 'BD.WAV', // Kick 60 | [NOTE_SNARE]: 'SD.WAV', // Snare 61 | [NOTE_HAT]: 'CH.WAV', // Closed Hats 62 | [NOTE_CLAP]: 'CP.WAV', // Clap 63 | [DRAKE[0]]: 'hotline-1-4s.wav', 64 | [DRAKE[1]]: 'hotline-2-4s.wav', 65 | [DRAKE[2]]: 'hotline-3-7s.wav', 66 | [DRAKE[3]]: 'hotline-4-3.5s.wav', 67 | [DRAKE[4]]: 'hotline-5-3.5s.wav', 68 | [THUG[0]]: 'thug-01.wav', 69 | [THUG[1]]: 'thug-02.wav', 70 | [THUG[2]]: 'thug-03.wav', 71 | }, 72 | { 73 | release: 1, 74 | baseUrl: '/public/samples/', 75 | }, 76 | ) 77 | .connect(reverb) 78 | .toMaster(); 79 | -------------------------------------------------------------------------------- /public/sqcr-demo/js/matrix-16x8.js: -------------------------------------------------------------------------------- 1 | colorLoopMapping = { 2 | synth: 3, 3 | leadSynth: 1, 4 | kicks: 2, 5 | hats: 0, 6 | snares: 4, 7 | }; 8 | 9 | loop('levels', async ctx => { 10 | LOOPS.forEach(l => { 11 | // Update levels of each track 12 | LEVELS[l] = MC_LEVELS.next(); 13 | }); 14 | 15 | // Every measure 16 | ctx.sleep(M * 4); 17 | }); 18 | 19 | loop('synth', async ctx => { 20 | // Update chord state 21 | chordId = MC_CHORDS.next(); 22 | chordNotes = chordNotesOf(chordId); 23 | 24 | chordNotes.forEach(n => { 25 | const dur = 4 * 1000; 26 | playInst(synth, n, dur); 27 | const scaleNote = scale.indexOf(n) % 8; 28 | // Visualizer.blink( 29 | // `/blink/${scaleNote}/${pulse}/${colorLoopMapping[ctx.name]}/${dur}`, 30 | // ); 31 | for (let i = 0; i < 16; i++) { 32 | Visualizer.blink( 33 | `/blink/${scaleNote}/${i}/${colorLoopMapping[ctx.name]}/${dur}`, 34 | ); 35 | } 36 | }); 37 | 38 | ctx.sleep((M / 1) * 1); 39 | }); 40 | 41 | since = Date.now(); 42 | 43 | loop('leadSynth', async ctx => { 44 | const pulse = beatFromTick(ctx.tick); 45 | const chordOffset = MC_CHORDS.peekID(); 46 | const mNote = melody[pulse] + chordOffset; 47 | const scaleNote = scale[mNote]; // octave scaled 48 | if (LEVELS[ctx.name]) { 49 | Visualizer.blink( 50 | `/blink/${mNote % 8}/${pulse}/${colorLoopMapping[ctx.name]}/100`, 51 | ); 52 | playInst(leadSynth, scaleNote, 100); 53 | ctx.sleep(T / 4); 54 | } else { 55 | playInst(leadSynth, scaleNote, 100); 56 | ctx.sleep(T / 2); 57 | } 58 | }); 59 | 60 | loop('blinker', async ctx => { 61 | const pulse = beatFromTick(ctx.tick); 62 | for (let i = 0; i < 8; i++) { 63 | Visualizer.blink(`/blink/${i}/${pulse}/0/${sqcr.tickToMS() * (T / 4)}`); 64 | } 65 | ctx.sleep(T / 4); 66 | }); 67 | 68 | loop('kicks', async ctx => { 69 | const pulse = beatFromTick(ctx.tick); 70 | // Switch pattern on the 0 71 | if (pulse === 0) MC_KICKS.next(); 72 | if (!MC_KICKS.peek()[pulse]) return ctx.sleep(T / 4); 73 | 74 | // Play kick beat 75 | playInst(sampler, NOTE_KICK); 76 | 77 | ctx.sleep(T / 4); 78 | }); 79 | 80 | loop('hats', async ctx => { 81 | hats_pattern = hats[MC_HATS.peekID()]; 82 | max = hats_pattern[1]; 83 | t = hats_pattern[0]; 84 | 85 | // Never muted 86 | playInst(sampler, NOTE_HAT); 87 | 88 | // Visualizer 89 | for (let i = 0; i < 8; i++) { 90 | Visualizer.blink(`/blink/${i}/${h_counter}/2/${sqcr.tickToMS() * t}`); 91 | } 92 | 93 | if (h_counter >= max - 1) { 94 | MC_HATS.next(); 95 | h_counter = 0; 96 | } else { 97 | h_counter++; 98 | } 99 | 100 | ctx.sleep(t); 101 | }); 102 | 103 | loop('snares', async ctx => { 104 | const pulse = beatFromTick(ctx.tick); 105 | // // Switch pattern on the 0 106 | if (pulse % 8 === 0) MC_SNARES.next(); 107 | if (!MC_SNARES.peek()[pulse] || !LEVELS[ctx.name]) return ctx.sleep(T / 4); 108 | playInst(sampler, NOTE_CLAP); 109 | ctx.sleep(T / 4); 110 | }); 111 | 112 | loop('vocals', async ctx => { 113 | if (LEVELS.vocals) playInst(sampler, _sample(DRAKE), 8 * 1000); 114 | // Update a bit more frequently 115 | LEVELS.vocals = MC_LEVELS.next(); 116 | ctx.sleep(M * 4); 117 | }); 118 | 119 | // Init dataviz 120 | window.initViz(); 121 | -------------------------------------------------------------------------------- /public/sqcr-demo/js/notes-graph-synth.js: -------------------------------------------------------------------------------- 1 | var nodes = null; 2 | var edges = null; 3 | var network = null; 4 | var selectedNodeId = 1; 5 | 6 | var note = null; 7 | var duration = 500; 8 | 9 | var NOTES = ['C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4']; 10 | 11 | function rand() { 12 | return NOTES[Math.round(Math.random() * (NOTES.length - 1))]; 13 | } 14 | 15 | var synth = new Tone.Synth({ 16 | oscillator: { 17 | type: 'amtriangle', 18 | harmonicity: 0.5, 19 | modulationType: 'sine', 20 | }, 21 | envelope: { 22 | attackCurve: 'exponential', 23 | attack: 0.05, 24 | decay: 0.2, 25 | sustain: 0.2, 26 | release: 1.5, 27 | }, 28 | portamento: 0.05, 29 | }).toMaster(); 30 | 31 | var G = { 32 | '0': [0, 0, 2, 4], 33 | '1': [1, 1, 3, 5], 34 | '2': [2, 2, 4, 6], 35 | '3': [4], 36 | '4': [4, 0], 37 | '5': [0], 38 | '6': [1, 0], 39 | }; 40 | 41 | var MC = new MarkovChain(G, NOTES, 0); 42 | 43 | // create an array with nodes 44 | function makeNodes(obj, labels) { 45 | var nodeStyle = { 46 | font: { 47 | color: '#000000', 48 | face: 'sans-serif', 49 | size: 20, 50 | }, 51 | color: { 52 | highlight: { 53 | border: '#ccfa99', 54 | background: '#faf099', 55 | }, 56 | }, 57 | }; 58 | const ids = Object.keys(obj).map(Number); 59 | return ids.map(id => { 60 | return { 61 | id, 62 | value: obj[id].length, // how many edges 63 | label: labels[id], 64 | ...nodeStyle, 65 | }; 66 | }); 67 | } 68 | 69 | function makeEdges(obj) { 70 | const edgeOptions = { 71 | arrows: { 72 | to: { 73 | enabled: true, 74 | }, 75 | }, 76 | color: { 77 | highlight: '#99f9fa', 78 | }, 79 | }; 80 | 81 | const ids = Object.keys(obj).map(Number); 82 | const edges = []; 83 | 84 | // Make matrix 85 | ids.forEach(id => { 86 | let values = {}; 87 | obj[id].forEach(edge => { 88 | if (!values[edge]) { 89 | values[edge] = 1; 90 | } else { 91 | values[edge] += 1; 92 | } 93 | }); 94 | 95 | Object.keys(values).forEach(k => { 96 | edges.push({ 97 | from: id, 98 | to: Number(k), 99 | value: values[k], 100 | ...edgeOptions, 101 | }); 102 | }); 103 | }); 104 | 105 | return edges; 106 | } 107 | 108 | function draw() { 109 | nodes = makeNodes(G, NOTES); 110 | 111 | edges = makeEdges(G); 112 | 113 | // create a network 114 | var container = document.getElementById('graph'); 115 | var data = { 116 | nodes: nodes, 117 | edges: edges, 118 | }; 119 | var options = { 120 | nodes: { 121 | shape: 'dot', 122 | size: 10, 123 | }, 124 | }; 125 | network = new vis.Network(container, data, options); 126 | } 127 | 128 | draw(); 129 | 130 | network.fit(nodes.map(n => n.id)); 131 | 132 | network.selectNodes([MC.peekID()]); 133 | 134 | setInterval(() => { 135 | selectedNodeId = MC.nextID(); 136 | network.selectNodes([selectedNodeId]); 137 | note = NOTES[selectedNodeId]; 138 | synth.triggerAttackRelease(note, duration); 139 | }, 500); 140 | -------------------------------------------------------------------------------- /public/sqcr-demo/lib/audiosynth.js: -------------------------------------------------------------------------------- 1 | var Synth, AudioSynth, AudioSynthInstrument; 2 | !function(){ 3 | 4 | var URL = window.URL || window.webkitURL; 5 | var Blob = window.Blob; 6 | 7 | if(!URL || !Blob) { 8 | throw new Error('This browser does not support AudioSynth'); 9 | } 10 | 11 | var _encapsulated = false; 12 | var AudioSynthInstance = null; 13 | var pack = function(c,arg){ return [new Uint8Array([arg, arg >> 8]), new Uint8Array([arg, arg >> 8, arg >> 16, arg >> 24])][c]; }; 14 | var setPrivateVar = function(n,v,w,e){Object.defineProperty(this,n,{value:v,writable:!!w,enumerable:!!e});}; 15 | var setPublicVar = function(n,v,w){setPrivateVar.call(this,n,v,w,true);}; 16 | AudioSynthInstrument = function AudioSynthInstrument(){this.__init__.apply(this,arguments);}; 17 | var setPriv = setPrivateVar.bind(AudioSynthInstrument.prototype); 18 | var setPub = setPublicVar.bind(AudioSynthInstrument.prototype); 19 | setPriv('__init__', function(a,b,c) { 20 | if(!_encapsulated) { throw new Error('AudioSynthInstrument can only be instantiated from the createInstrument method of the AudioSynth object.'); } 21 | setPrivateVar.call(this, '_parent', a); 22 | setPublicVar.call(this, 'name', b); 23 | setPrivateVar.call(this, '_soundID', c); 24 | }); 25 | setPub('play', function(note, octave, duration) { 26 | return this._parent.play(this._soundID, note, octave, duration); 27 | }); 28 | setPub('generate', function(note, octave, duration) { 29 | return this._parent.generate(this._soundID, note, octave, duration); 30 | }); 31 | AudioSynth = function AudioSynth(){if(AudioSynthInstance instanceof AudioSynth){return AudioSynthInstance;}else{ this.__init__(); return this; }}; 32 | setPriv = setPrivateVar.bind(AudioSynth.prototype); 33 | setPub = setPublicVar.bind(AudioSynth.prototype); 34 | setPriv('_debug',false,true); 35 | setPriv('_bitsPerSample',16); 36 | setPriv('_channels',1); 37 | setPriv('_sampleRate',44100,true); 38 | setPub('setSampleRate', function(v) { 39 | this._sampleRate = Math.max(Math.min(v|0,44100), 4000); 40 | this._clearCache(); 41 | return this._sampleRate; 42 | }); 43 | setPub('getSampleRate', function() { return this._sampleRate; }); 44 | setPriv('_volume',32768,true); 45 | setPub('setVolume', function(v) { 46 | v = parseFloat(v); if(isNaN(v)) { v = 0; } 47 | v = Math.round(v*32768); 48 | this._volume = Math.max(Math.min(v|0,32768), 0); 49 | this._clearCache(); 50 | return this._volume; 51 | }); 52 | setPub('getVolume', function() { return Math.round(this._volume/32768*10000)/10000; }); 53 | setPriv('_notes',{'C':261.63,'C#':277.18,'D':293.66,'D#':311.13,'E':329.63,'F':346.23,'F#':369.99,'G':392.00,'G#':415.30,'A':440.00,'A#':466.16,'B':493.88}); 54 | setPriv('_fileCache',[],true); 55 | setPriv('_temp',{},true); 56 | setPriv('_sounds',[],true); 57 | setPriv('_mod',[function(i,s,f,x){return Math.sin((2 * Math.PI)*(i/s)*f+x);}]); 58 | setPriv('_resizeCache', function() { 59 | var f = this._fileCache; 60 | var l = this._sounds.length; 61 | while(f.length> 8; 121 | 122 | } 123 | 124 | for (; i !== decayLen; i++) { 125 | 126 | val = volume * Math.pow((1-((i-(sampleRate*attack))/(sampleRate*(time-attack)))),dampen) * waveFunc.call(waveBind, i, sampleRate, frequency, volume); 127 | 128 | data[i << 1] = val; 129 | data[(i << 1) + 1] = val >> 8; 130 | 131 | } 132 | 133 | var out = [ 134 | 'RIFF', 135 | pack(1, 4 + (8 + 24/* chunk 1 length */) + (8 + 8/* chunk 2 length */)), // Length 136 | 'WAVE', 137 | // chunk 1 138 | 'fmt ', // Sub-chunk identifier 139 | pack(1, 16), // Chunk length 140 | pack(0, 1), // Audio format (1 is linear quantization) 141 | pack(0, channels), 142 | pack(1, sampleRate), 143 | pack(1, sampleRate * channels * bitsPerSample / 8), // Byte rate 144 | pack(0, channels * bitsPerSample / 8), 145 | pack(0, bitsPerSample), 146 | // chunk 2 147 | 'data', // Sub-chunk identifier 148 | pack(1, data.length * channels * bitsPerSample / 8), // Chunk length 149 | data 150 | ]; 151 | var blob = new Blob(out, {type: 'audio/wav'}); 152 | var dataURI = URL.createObjectURL(blob); 153 | this._fileCache[sound][octave-1][note][time] = dataURI; 154 | if(this._debug) { console.log((new Date).valueOf() - t, 'ms to generate'); } 155 | return dataURI; 156 | } 157 | }); 158 | setPub('play', function(sound, note, octave, duration) { 159 | var src = this.generate(sound, note, octave, duration); 160 | var audio = new Audio(src); 161 | audio.play(); 162 | return true; 163 | }); 164 | setPub('debug', function() { this._debug = true; }); 165 | setPub('createInstrument', function(sound) { 166 | var n = 0; 167 | var found = false; 168 | if(typeof(sound)=='string') { 169 | for(var i=0;i=(valueTable.length-1)?0:playVal+1] + valueTable[playVal]) * 0.5; 296 | 297 | if(playVal>=Math.floor(period)) { 298 | if(playVal=p_hundredth) { 300 | // Reset 301 | resetPlay = true; 302 | valueTable[playVal+1] = (valueTable[0] + valueTable[playVal+1]) * 0.5; 303 | vars.periodCount++; 304 | } 305 | } else { 306 | resetPlay = true; 307 | } 308 | } 309 | 310 | var _return = valueTable[playVal]; 311 | if(resetPlay) { vars.playVal = 0; } else { vars.playVal++; } 312 | 313 | return _return; 314 | 315 | } 316 | } 317 | }, 318 | { 319 | name: 'edm', 320 | attack: function() { return 0.002; }, 321 | dampen: function() { return 1; }, 322 | wave: function(i, sampleRate, frequency) { 323 | var base = this.modulate[0]; 324 | var mod = this.modulate.slice(1); 325 | return mod[0]( 326 | i, 327 | sampleRate, 328 | frequency, 329 | mod[9]( 330 | i, 331 | sampleRate, 332 | frequency, 333 | mod[2]( 334 | i, 335 | sampleRate, 336 | frequency, 337 | Math.pow(base(i, sampleRate, frequency, 0), 3) + 338 | Math.pow(base(i, sampleRate, frequency, 0.5), 5) + 339 | Math.pow(base(i, sampleRate, frequency, 1), 7) 340 | ) 341 | ) + 342 | mod[8]( 343 | i, 344 | sampleRate, 345 | frequency, 346 | base(i, sampleRate, frequency, 1.75) 347 | ) 348 | ); 349 | } 350 | }); 351 | -------------------------------------------------------------------------------- /public/sqcr-demo/lib/tonal.js: -------------------------------------------------------------------------------- 1 | !function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(n.Tonal={})}(this,function(n){"use strict";function t(n){"string"!=typeof n&&(n="");var t=T.exec(n);return t?[t[1].toUpperCase(),t[2].replace(/x/g,"##"),t[3],t[4]]:null}function r(n,t){return n=Math.round(n),(!0===t?G:I)[n%12]+(Math.floor(n/12)-1)}function e(n,t){for(var r=[];t--;r[t]=t+n);return r}function m(n,t){for(var r=[];t--;r[t]=n-t);return r}function i(n,t){return null===n||null===t?[]:nan(t)})}function P(n){return o(n).filter(function(n,t,r){return 0===t||n!==r[t-1]})}function M(n){return"string"!=typeof n?An:_n[n]||(_n[n]=xn(n))}function a(n){var t=(n+1)%7;return t<0?7+t:t}function l(n,t){if(1===arguments.length)return function(t){return l(n,t)};var r=Kn(n),e=Qn(t);if(null===r||null===e)return null;var m=1===r.length?[r[0]+e[0]]:[r[0]+e[0],r[1]+e[1]];return mn(Un(m[0],m[1]))}function c(n,t){if(1===arguments.length)return function(t){return c(n,t)};var r=Kn(n);return null===r?null:mn(Un(r[0]+t))}function s(n,t){if(1===arguments.length)return function(t){return s(n,t)};var r=Kn(n),e=Kn(t);return null===e||null===r?null:e[0]-r[0]}function f(n,t){return 1===arguments.length?function(t){return l(t,n)}:l(t,n)}function d(n,t,r){var e=Qn(n),m=Qn(t);if(null===e||null===m)return null;var i=[e[0]+r*m[0],e[1]+r*m[1]];return Dn(Wn(i))}function p(n,t){return 1===arguments.length?function(t){return p(n,t)}:d(n,t,1)}function b(n,t){return 1===arguments.length?function(t){return p(n,t)}:d(n,t,-1)}function h(n,t){if(1===arguments.length)return function(t){return h(n,t)};var r=Kn(n),e=Kn(t);if(null===r||null===e||r.length!==e.length)return null;var m=1===r.length?[e[0]-r[0],-Math.floor(7*(e[0]-r[0])/12)]:[e[0]-r[0],e[1]-r[1]];return Dn(Wn(m))}function v(n,t){if(1===arguments.length)return function(t){return v(n,t)};var r=L(n),e=L(t);return null!==r.midi&&null!==e.midi?e.midi-r.midi:null!==r.chroma&&null!==e.chroma?(e.chroma-r.chroma+12)%12:null}function A(n){if(y(n))return n;if(!Array.isArray(n))return"";var t=[0,0,0,0,0,0,0,0,0,0,0,0];return n.map(nt).forEach(function(n){t[n]=1}),t.join("")}function g(n){return et=et||i(2048,4095).map(function(n){return n.toString(2)}),"number"==typeof n?et.filter(function(t){return rt(t)===n}):et.slice()}function j(n,t){t=!1!==t;var r=A(n).split("");return Mn(r.map(function(n,e){var m=u(e,r);return t&&"0"===m[0]?null:m.join("")}))}function y(n){return mt.test(n)}function O(n){return y(n)?Mn(n.split("").map(function(n,t){return"1"===n?it[t]:null})):[]}function x(n,t){return 1===arguments.length?function(t){return x(n,t)}:A(n)===A(t)}function _(n,t){return arguments.length>1?_(n)(t):(n=tt(n),function(t){return(t=tt(t))!==n&&(t&n)===t})}function z(n,t){return arguments.length>1?z(n)(t):(n=tt(n),function(t){return(t=tt(t))!==n&&(t|n)===t})}function q(n,t){return arguments.length>1?q(n)(t):(n=A(n),function(t){return"1"===n[nt(t)]})}function k(n,t){return 1===arguments.length?function(t){return k(n,t)}:t.filter(q(n))}function S(n,t){var r=D(n);return t=t||r[1],pt(t).map(l(r[0]))}function w(n){var t=D(n);return void 0!==Mt(t[1])}function D(n){if("string"!=typeof n)return["",""];var t=n.indexOf(" "),r=R(n.substring(0,t))||R(n)||"",e=""!==r?n.substring(r.length+1):n;return[r,e.length?e:""]}function E(n,t){var r=C(n);return t=t||r[1],xt(t).intervals.map(l(r[0]))}function C(n){var r=t(n);return""===r[0]?["",n]:"A"===r[0]&&"ug"===r[3]?["","aug"]:St.test(r[2])?[r[0]+r[1],r[2]+r[3]]:[r[0]+r[1]+r[2],r[3]]}var $="C C# Db D D# Eb E F F# Gb G G# Ab A A# Bb B".split(" "),F=function(n){return"string"!=typeof n?$.slice():$.filter(function(t){var r=t[1]||" ";return-1!==n.indexOf(r)})},G=F(" #"),I=F(" b"),T=/^([a-gA-G]?)(#{1,}|b{1,}|x{1,}|)(-?\d*)\s*(.*)$/,B=Object.freeze({pc:null,name:null,step:null,alt:null,oct:null,octStr:null,chroma:null,midi:null,freq:null}),N=[0,2,4,5,7,9,11],L=function(n,t){return void 0===t&&(t={}),function(r){return t[r]||(t[r]=n(r))}}(function(n){var r=t(n);if(""===r[0]||""!==r[3])return B;var e=r[0],m=r[1],i=r[2],u={letter:e,acc:m,octStr:i};return u.pc=u.letter+u.acc,u.name=u.pc+i,u.step=(u.letter.charCodeAt(0)+3)%7,u.alt="b"===u.acc[0]?-u.acc.length:u.acc.length,u.oct=i.length?+i:null,u.chroma=(N[u.step]+u.alt+120)%12,u.midi=null!==u.oct?N[u.step]+u.alt+12*(u.oct+1):null,u.freq=J(u.midi),Object.freeze(u)}),R=function(n){return L(n).name},U=function(n){return L(n).pc},H=function(n){return L(n).midi||+n||null},J=function(n,t){return void 0===t&&(t=440),"number"==typeof n?Math.pow(2,(n-69)/12)*t:null},K=function(n){return L(n).freq||J(n)},Q=Math.log(2),V=Math.log(440),W=function(n){var t=12*(Math.log(n)-V)/Q+69;return Math.round(100*t)/100},X=function(n){return L(n).chroma},Y=function(n){return L(n).oct},Z=function(n){return"CDEFGAB"[n]},nn=function(n,t){return Array(t+1).join(n)},tn=function(n,t){return"number"!=typeof n?"":t(n)},rn=function(n){return tn(n,function(n){return n<0?nn("b",-n):nn("#",n)})},en=function(n,t){void 0===n&&(n={}),void 0===t&&(t=null);var r=t?Object.assign({},L(t),n):n,e=r.step,m=r.alt,i=r.oct,u=Z(e);if(!u)return null;var o=u+rn(m);return i||0===i?o+i:o},mn=en,un=function(n,t){var e=L(n),m=e.alt,i=e.chroma,u=e.midi;if(null===i)return null;var o=!1===t?m<0:m>0;return null===u?U(r(i,o)):r(u,o)},on=function(n){return un(n,!1)},Pn=Object.freeze({names:F,tokenize:t,props:L,name:R,pc:U,midi:H,midiToFreq:J,freq:K,freqToMidi:W,chroma:X,oct:Y,stepToLetter:Z,altToAcc:rn,from:en,build:mn,fromMidi:r,simplify:un,enharmonic:on}),Mn=function(n){return n.filter(function(n){return 0===n||n})},an=function(n){var t=H(n);return null!==t?t:H(n+"-100")},ln=function(n,t){void 0===t&&(t=Math.random);for(var r,e,m=n.length;m;)r=t()*m--|0,e=n[m],n[m]=n[r],n[r]=e;return n},cn=function(n){return 0===n.length?[[]]:cn(n.slice(1)).reduce(function(t,r){return t.concat(n.map(function(t,e){var m=r.slice();return m.splice(e,0,n[0]),m}))},[])},sn=Object.freeze({range:i,rotate:u,compact:Mn,sort:o,unique:P,shuffle:ln,permutations:cn}),fn=new RegExp("^([-+]?\\d+)(d{1,4}|m|M|P|A{1,4})|(AA|A|P|M|m|d|dd)([-+]?\\d+)$"),dn=[0,2,4,5,7,9,11],pn=[0,1,2,3,4,5,6,5,4,3,2,1],bn="1P 2m 2M 3m 3M 4P 5P 6m 6M 7m 7M 8P".split(" "),hn=function(n){return"string"!=typeof n?bn.slice():bn.filter(function(t){return-1!==n.indexOf(t[1])})},vn=function(n){var t=fn.exec(n);return null===t?null:t[1]?[t[1],t[2]]:[t[4],t[3]]},An=Object.freeze({name:null,num:null,q:null,step:null,alt:null,dir:null,type:null,simple:null,semitones:null,chroma:null}),gn=function(n,t){return Array(Math.abs(t)+1).join(n)},jn=function(n,t){return"M"===t&&"M"===n?0:"P"===t&&"P"===n?0:"m"===t&&"M"===n?-1:/^A+$/.test(t)?t.length:/^d+$/.test(t)?"P"===n?-t.length:-t.length-1:null},yn=function(n,t){return 0===t?"M"===n?"M":"P":-1===t&&"M"===n?"m":t>0?gn("A",t):t<0?gn("d","P"===n?t:t+1):null},On=function(n){return(Math.abs(n)-1)%7},xn=function(n){var t=vn(n);if(null===t)return An;var r={num:+t[0],q:t[1]};return r.step=On(r.num),r.type="PMMPPMM"[r.step],"M"===r.type&&"P"===r.q?An:(r.name=""+r.num+r.q,r.dir=r.num<0?-1:1,r.simple=8===r.num||-8===r.num?r.num:r.dir*(r.step+1),r.alt=jn(r.type,r.q),r.oct=Math.floor((Math.abs(r.num)-1)/7),r.semitones=r.dir*(dn[r.step]+r.alt+12*r.oct),r.chroma=(r.dir*(dn[r.step]+r.alt)%12+12)%12,Object.freeze(r))},_n={},zn=function(n){return M(n).num},qn=function(n){return M(n).name},kn=function(n){return M(n).semitones},Sn=function(n){return M(n).chroma},wn=function(n){return"string"==typeof n&&(n=M(n).chroma),"number"==typeof n?pn[n%12]:null},Dn=function(n){void 0===n&&(n={});var t=n.num,r=n.step,e=n.alt,m=n.oct;void 0===m&&(m=1);var i=n.dir;if(void 0!==r&&(t=r+1+7*m),void 0===t)return null;var u=i<0?"-":"",o="PMMPPMM"[On(t)];return u+t+yn(o,e)},En=function(n){var t=M(n);return t===An?null:t.simple+t.q},Cn=function(n){var t=M(n);if(t===An)return null;var r=(7-t.step)%7,e="P"===t.type?-t.alt:-(t.alt+1);return Dn({step:r,alt:e,oct:t.oct,dir:t.dir})},$n=[1,2,2,3,3,4,5,5,6,6,7,7],Fn="P m M m M P d P m M m M".split(" "),Gn=function(n){var t=n<0?-1:1,r=Math.abs(n),e=r%12,m=Math.floor(r/12);return t*($n[e]+7*m)+Fn[e]},In=Object.freeze({names:hn,tokenize:vn,props:M,num:zn,name:qn,semitones:kn,chroma:Sn,ic:wn,build:Dn,simplify:En,invert:Cn,fromSemitones:Gn}),Tn=[0,2,4,-1,1,3,5],Bn=function(n){return Math.floor(7*n/12)},Nn=Tn.map(Bn),Ln=function(n){var t=n.step,r=n.alt,e=n.oct,m=n.dir;void 0===m&&(m=1);var i=Tn[t]+7*r;return null===e?[m*i]:[m*i,m*(e-Nn[t]-4*r)]},Rn=[3,0,4,1,5,2,6],Un=function(n,t,r){var e=Rn[a(n)],m=Math.floor((n+1)/7);return void 0===t?{step:e,alt:m,dir:r}:{step:e,alt:m,oct:t+4*m+Nn[e],dir:r}},Hn=function(n,t){return void 0===t&&(t={}),function(r){return t[r]||(t[r]=n(r))}},Jn=function(n){return Hn(function(t){var r=n(t);return null===r.name?null:Ln(r)})},Kn=Jn(L),Qn=Jn(M),Vn=function(n){return 7*n[0]+12*n[1]<0},Wn=function(n){return Vn(n)?Un(-n[0],-n[1],-1):Un(n[0],n[1],1)},Xn=Object.freeze({transpose:l,trFifths:c,fifths:s,transposeBy:f,addIntervals:d,add:p,subtract:b,interval:h,semitones:v}),Yn={chromatic:["1P 2m 2M 3m 3M 4P 4A 5P 6m 6M 7m 7M"],lydian:["1P 2M 3M 4A 5P 6M 7M"],major:["1P 2M 3M 4P 5P 6M 7M",["ionian"]],mixolydian:["1P 2M 3M 4P 5P 6M 7m",["dominant"]],dorian:["1P 2M 3m 4P 5P 6M 7m"],aeolian:["1P 2M 3m 4P 5P 6m 7m",["minor"]],phrygian:["1P 2m 3m 4P 5P 6m 7m"],locrian:["1P 2m 3m 4P 5d 6m 7m"],altered:["1P 2m 3m 3M 5d 6m 7m",["super locrian","diminished whole tone","pomeroy"]],iwato:["1P 2m 4P 5d 7m"],hirajoshi:["1P 2M 3m 5P 6m"],kumoijoshi:["1P 2m 4P 5P 6m"],pelog:["1P 2m 3m 5P 6m"],prometheus:["1P 2M 3M 4A 6M 7m"],ritusen:["1P 2M 4P 5P 6M"],scriabin:["1P 2m 3M 5P 6M"],piongio:["1P 2M 4P 5P 6M 7m"],augmented:["1P 2A 3M 5P 5A 7M"],neopolitan:["1P 2m 3m 4P 5P 6m 7M"],diminished:["1P 2M 3m 4P 5d 6m 6M 7M"],egyptian:["1P 2M 4P 5P 7m"],oriental:["1P 2m 3M 4P 5d 6M 7m"],spanish:["1P 2m 3M 4P 5P 6m 7m",["phrygian major"]],flamenco:["1P 2m 3m 3M 4A 5P 7m"],balinese:["1P 2m 3m 4P 5P 6m 7M"],persian:["1P 2m 3M 4P 5d 6m 7M"],bebop:["1P 2M 3M 4P 5P 6M 7m 7M"],enigmatic:["1P 2m 3M 5d 6m 7m 7M"],ichikosucho:["1P 2M 3M 4P 5d 5P 6M 7M"],"melodic minor":["1P 2M 3m 4P 5P 6M 7M"],"melodic minor second mode":["1P 2m 3m 4P 5P 6M 7m"],"lydian augmented":["1P 2M 3M 4A 5A 6M 7M"],"lydian dominant":["1P 2M 3M 4A 5P 6M 7m",["lydian b7"]],"melodic minor fifth mode":["1P 2M 3M 4P 5P 6m 7m",["hindu","mixolydian b6M"]],"locrian #2":["1P 2M 3m 4P 5d 6m 7m"],"locrian major":["1P 2M 3M 4P 5d 6m 7m",["arabian"]],"major pentatonic":["1P 2M 3M 5P 6M",["pentatonic"]],"lydian pentatonic":["1P 3M 4A 5P 7M",["chinese"]],"mixolydian pentatonic":["1P 3M 4P 5P 7m",["indian"]],"locrian pentatonic":["1P 3m 4P 5d 7m",["minor seven flat five pentatonic"]],"minor pentatonic":["1P 3m 4P 5P 7m"],"minor six pentatonic":["1P 3m 4P 5P 6M"],"minor hexatonic":["1P 2M 3m 4P 5P 7M"],"flat three pentatonic":["1P 2M 3m 5P 6M",["kumoi"]],"flat six pentatonic":["1P 2M 3M 5P 6m"],"major flat two pentatonic":["1P 2m 3M 5P 6M"],"whole tone pentatonic":["1P 3M 5d 6m 7m"],"ionian pentatonic":["1P 3M 4P 5P 7M"],"lydian #5P pentatonic":["1P 3M 4A 5A 7M"],"lydian dominant pentatonic":["1P 3M 4A 5P 7m"],"minor #7M pentatonic":["1P 3m 4P 5P 7M"],"super locrian pentatonic":["1P 3m 4d 5d 7m"],"in-sen":["1P 2m 4P 5P 7m"],"vietnamese 1":["1P 3m 4P 5P 6m"],"vietnamese 2":["1P 3m 4P 5P 7m"],"prometheus neopolitan":["1P 2m 3M 4A 6M 7m"],"major blues":["1P 2M 3m 3M 5P 6M"],"minor blues":["1P 3m 4P 5d 5P 7m",["blues"]],"composite blues":["1P 2M 3m 3M 4P 5d 5P 6M 7m"],"augmented heptatonic":["1P 2A 3M 4P 5P 5A 7M"],"dorian #4":["1P 2M 3m 4A 5P 6M 7m"],"lydian diminished":["1P 2M 3m 4A 5P 6M 7M"],"whole tone":["1P 2M 3M 4A 5A 7m"],"leading whole tone":["1P 2M 3M 4A 5A 7m 7M"],"harmonic minor":["1P 2M 3m 4P 5P 6m 7M"],"lydian minor":["1P 2M 3M 4A 5P 6m 7m"],"neopolitan minor":["1P 2m 3m 4P 5P 6m 7M"],"neopolitan major":["1P 2m 3m 4P 5P 6M 7M",["dorian b2"]],"neopolitan major pentatonic":["1P 3M 4P 5d 7m"],"romanian minor":["1P 2M 3m 5d 5P 6M 7m"],"double harmonic lydian":["1P 2m 3M 4A 5P 6m 7M"],"harmonic major":["1P 2M 3M 4P 5P 6m 7M"],"double harmonic major":["1P 2m 3M 4P 5P 6m 7M",["gypsy"]],"hungarian minor":["1P 2M 3m 4A 5P 6m 7M"],"hungarian major":["1P 2A 3M 4A 5P 6M 7m"],"spanish heptatonic":["1P 2m 3m 3M 4P 5P 6m 7m"],"todi raga":["1P 2m 3m 4A 5P 6m 7M"],"malkos raga":["1P 3m 4P 6m 7m"],"kafi raga":["1P 3m 3M 4P 5P 6M 7m 7M"],"purvi raga":["1P 2m 3M 4P 4A 5P 6m 7M"],"bebop dominant":["1P 2M 3M 4P 5P 6M 7m 7M"],"bebop minor":["1P 2M 3m 3M 4P 5P 6M 7m"],"bebop major":["1P 2M 3M 4P 5P 5A 6M 7M"],"bebop locrian":["1P 2m 3m 4P 5d 5P 6m 7m"],"minor bebop":["1P 2M 3m 4P 5P 6m 7m 7M"],"mystery #1":["1P 2m 3M 5d 6m 7m"],"minor six diminished":["1P 2M 3m 4P 5P 6m 6M 7M"],"ionian augmented":["1P 2M 3M 4P 5A 6M 7M"],"lydian #9":["1P 2m 3M 4A 5P 6M 7M"],"six tone symmetric":["1P 2m 3M 4P 5A 6M"]},Zn={M:["1P 3M 5P",["Major",""]],M13:["1P 3M 5P 7M 9M 13M",["maj13","Maj13"]],M6:["1P 3M 5P 13M",["6"]],M69:["1P 3M 5P 6M 9M",["69"]],M7add13:["1P 3M 5P 6M 7M 9M"],M7b5:["1P 3M 5d 7M"],M7b6:["1P 3M 6m 7M"],M7b9:["1P 3M 5P 7M 9m"],M7sus4:["1P 4P 5P 7M"],M9:["1P 3M 5P 7M 9M",["maj9","Maj9"]],M9b5:["1P 3M 5d 7M 9M"],M9sus4:["1P 4P 5P 7M 9M"],Madd9:["1P 3M 5P 9M",["2","add9","add2"]],Maj7:["1P 3M 5P 7M",["maj7","M7"]],Mb5:["1P 3M 5d"],Mb6:["1P 3M 13m"],Msus2:["1P 2M 5P",["add9no3","sus2"]],Msus4:["1P 4P 5P",["sus","sus4"]],Maddb9:["1P 3M 5P 9m"],m:["1P 3m 5P"],m11:["1P 3m 5P 7m 9M 11P",["_11"]],m11b5:["1P 3m 7m 12d 2M 4P",["h11","_11b5"]],m13:["1P 3m 5P 7m 9M 11P 13M",["_13"]],m6:["1P 3m 4P 5P 13M",["_6"]],m69:["1P 3m 5P 6M 9M",["_69"]],m7:["1P 3m 5P 7m",["minor7","_","_7"]],m7add11:["1P 3m 5P 7m 11P",["m7add4"]],m7b5:["1P 3m 5d 7m",["half-diminished","h7","_7b5"]],m9:["1P 3m 5P 7m 9M",["_9"]],m9b5:["1P 3m 7m 12d 2M",["h9","-9b5"]],mMaj7:["1P 3m 5P 7M",["mM7","_M7"]],mMaj7b6:["1P 3m 5P 6m 7M",["mM7b6"]],mM9:["1P 3m 5P 7M 9M",["mMaj9","-M9"]],mM9b6:["1P 3m 5P 6m 7M 9M",["mMaj9b6"]],mb6M7:["1P 3m 6m 7M"],mb6b9:["1P 3m 6m 9m"],o:["1P 3m 5d",["mb5","dim"]],o7:["1P 3m 5d 13M",["diminished","m6b5","dim7"]],o7M7:["1P 3m 5d 6M 7M"],oM7:["1P 3m 5d 7M"],sus24:["1P 2M 4P 5P",["sus4add9"]],madd4:["1P 3m 4P 5P"],madd9:["1P 3m 5P 9M"],4:["1P 4P 7m 10m",["quartal"]],5:["1P 5P"],7:["1P 3M 5P 7m",["Dominant","Dom"]],9:["1P 3M 5P 7m 9M",["79"]],11:["1P 5P 7m 9M 11P"],13:["1P 3M 5P 7m 9M 13M",["13_"]],64:["5P 8P 10M"],"M#5":["1P 3M 5A",["augmented","maj#5","Maj#5","+","aug"]],"M#5add9":["1P 3M 5A 9M",["+add9"]],"M13#11":["1P 3M 5P 7M 9M 11A 13M",["maj13#11","Maj13#11","M13+4","M13#4"]],"M6#11":["1P 3M 5P 6M 11A",["M6b5","6#11","6b5"]],"M69#11":["1P 3M 5P 6M 9M 11A"],"M7#11":["1P 3M 5P 7M 11A",["maj7#11","Maj7#11","M7+4","M7#4"]],"M7#5":["1P 3M 5A 7M",["maj7#5","Maj7#5","maj9#5","M7+"]],"M7#5sus4":["1P 4P 5A 7M"],"M7#9#11":["1P 3M 5P 7M 9A 11A"],"M9#11":["1P 3M 5P 7M 9M 11A",["maj9#11","Maj9#11","M9+4","M9#4"]],"M9#5":["1P 3M 5A 7M 9M",["Maj9#5"]],"M9#5sus4":["1P 4P 5A 7M 9M"],"11b9":["1P 5P 7m 9m 11P"],"13#11":["1P 3M 5P 7m 9M 11A 13M",["13+4","13#4"]],"13#9":["1P 3M 5P 7m 9A 13M",["13#9_"]],"13#9#11":["1P 3M 5P 7m 9A 11A 13M"],"13b5":["1P 3M 5d 6M 7m 9M"],"13b9":["1P 3M 5P 7m 9m 13M"],"13b9#11":["1P 3M 5P 7m 9m 11A 13M"],"13no5":["1P 3M 7m 9M 13M"],"13sus4":["1P 4P 5P 7m 9M 13M",["13sus"]],"69#11":["1P 3M 5P 6M 9M 11A"],"7#11":["1P 3M 5P 7m 11A",["7+4","7#4","7#11_","7#4_"]],"7#11b13":["1P 3M 5P 7m 11A 13m",["7b5b13"]],"7#5":["1P 3M 5A 7m",["+7","7aug","aug7"]],"7#5#9":["1P 3M 5A 7m 9A",["7alt","7#5#9_","7#9b13_"]],"7#5b9":["1P 3M 5A 7m 9m"],"7#5b9#11":["1P 3M 5A 7m 9m 11A"],"7#5sus4":["1P 4P 5A 7m"],"7#9":["1P 3M 5P 7m 9A",["7#9_"]],"7#9#11":["1P 3M 5P 7m 9A 11A",["7b5#9"]],"7#9#11b13":["1P 3M 5P 7m 9A 11A 13m"],"7#9b13":["1P 3M 5P 7m 9A 13m"],"7add6":["1P 3M 5P 7m 13M",["67","7add13"]],"7b13":["1P 3M 7m 13m"],"7b5":["1P 3M 5d 7m"],"7b6":["1P 3M 5P 6m 7m"],"7b9":["1P 3M 5P 7m 9m"],"7b9#11":["1P 3M 5P 7m 9m 11A",["7b5b9"]],"7b9#9":["1P 3M 5P 7m 9m 9A"],"7b9b13":["1P 3M 5P 7m 9m 13m"],"7b9b13#11":["1P 3M 5P 7m 9m 11A 13m",["7b9#11b13","7b5b9b13"]],"7no5":["1P 3M 7m"],"7sus4":["1P 4P 5P 7m",["7sus"]],"7sus4b9":["1P 4P 5P 7m 9m",["susb9","7susb9","7b9sus","7b9sus4","phryg"]],"7sus4b9b13":["1P 4P 5P 7m 9m 13m",["7b9b13sus4"]],"9#11":["1P 3M 5P 7m 9M 11A",["9+4","9#4","9#11_","9#4_"]],"9#11b13":["1P 3M 5P 7m 9M 11A 13m",["9b5b13"]],"9#5":["1P 3M 5A 7m 9M",["9+"]],"9#5#11":["1P 3M 5A 7m 9M 11A"],"9b13":["1P 3M 7m 9M 13m"],"9b5":["1P 3M 5d 7m 9M"],"9no5":["1P 3M 7m 9M"],"9sus4":["1P 4P 5P 7m 9M",["9sus"]],"m#5":["1P 3m 5A",["m+","mb6"]],"m11A 5":["1P 3m 6m 7m 9M 11P"],"m7#5":["1P 3m 6m 7m"],"m9#5":["1P 3m 6m 7m 9M"],"+add#9":["1P 3M 5A 9A"]},nt=function(n){return X(n)||Sn(n)||0},tt=function(n){return parseInt(A(n),2)},rt=function(n){return n.replace(/0/g,"").length},et=null,mt=/^[01]{12}$/,it="1P 2m 2M 3m 3M 4P 5d 5P 6m 6M 7m 7M".split(" "),ut=Object.freeze({chroma:A,chromas:g,modes:j,isChroma:y,intervals:O,isEqual:x,isSubsetOf:_,isSupersetOf:z,includes:q,filter:k}),ot=function(n){var t=Object.keys(n).sort(),r=[],e=[],m=function(n,t,m){r[n]=t,e[m]=e[m]||[],e[m].push(n)};t.forEach(function(t){var r=n[t][0].split(" "),e=n[t][1],i=A(r);m(t,r,i),e&&e.forEach(function(n){return m(n,r,i)})});var i=Object.keys(r).sort(),u=function(n){return r[n]};return u.names=function(n){return"string"==typeof n?(e[n]||[]).slice():(!0===n?i:t).slice()},u},Pt=function(n,t){var r=function(r){return n(r)||t(r)};return r.names=function(r){return n.names(r).concat(t.names(r))},r},Mt=ot(Yn),at=ot(Zn),lt=Pt(Mt,at),ct=Object.freeze({dictionary:ot,combine:Pt,scale:Mt,chord:at,pcset:lt}),st=Object.freeze({name:null,intervals:[],names:[],chroma:null,setnum:null}),ft=function(n,t){return function(r){return t[r]||(t[r]=n(r))}}(function(n){var t=Mt(n);if(!t)return st;var r={intervals:t,name:n};return r.chroma=A(t),r.setnum=parseInt(r.chroma,2),r.names=Mt.names(r.chroma),Object.freeze(r)},{}),dt=Mt.names,pt=function(n){var t=D(n);return ft(t[1]).intervals},bt=function(n){var t=pt(n),r=S(n);return j(t).map(function(n,e){var m=Mt.names(n)[0];if(m)return[r[e]||t[e],m]}).filter(function(n){return n})},ht=function(n){var t=_(pt(n));return at.names().filter(function(n){return t(at(n))})},vt=function(n){var t=Mn(n.map(U));if(!t.length)return t;var r=t[0],e=P(t);return u(e.indexOf(r),e)},At=function(n){if(!pt(n).length)return[];var t=z(pt(n));return Mt.names().filter(function(n){return t(Mt(n))})},gt=function(n){var t=_(pt(n));return Mt.names().filter(function(n){return t(Mt(n))})},jt=Object.freeze({props:ft,names:dt,intervals:pt,notes:S,exists:w,tokenize:D,modeNames:bt,chords:ht,toScale:vt,supersets:At,subsets:gt}),yt=at.names,Ot=Object.freeze({name:null,names:[],intervals:[],chroma:null,setnum:null}),xt=function(n,t){return void 0===t&&(t={}),function(r){return t[r]||(t[r]=n(r))}}(function(n){var t=at(n);if(!t)return Ot;var r={intervals:t,name:n};return r.chroma=A(t),r.setnum=parseInt(r.chroma,2),r.names=at.names(r.chroma),r}),_t=function(n){return xt(C(n)[1]).intervals},zt=function(n){return void 0!==at(C(n)[1])},qt=function(n){if(!_t(n).length)return[];var t=z(_t(n));return at.names().filter(function(n){return t(at(n))})},kt=function(n){var t=_(_t(n));return at.names().filter(function(n){return t(at(n))})},St=/^(6|64|7|9|11|13)$/,wt=Object.freeze({names:yt,props:xt,intervals:_t,notes:E,exists:zt,supersets:qt,subsets:kt,tokenize:C}),Dt=l,Et=h,Ct=L,$t=H,Ft=K,Gt=at,It=Mt;n.Array=sn,n.Note=Pn,n.Interval=In,n.Distance=Xn,n.Scale=jt,n.Chord=wt,n.PcSet=ut,n.Dictionary=ct,n.transpose=Dt,n.interval=Et,n.note=Ct,n.midi=$t,n.freq=Ft,n.chord=Gt,n.scale=It,Object.defineProperty(n,"__esModule",{value:!0})}); 2 | //# sourceMappingURL=tonal.min.js.map 3 | -------------------------------------------------------------------------------- /public/sqcr-demo/lib/transpiled.js: -------------------------------------------------------------------------------- 1 | !function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(n.Tonal={})}(this,function(n){"use strict";function t(n){"string"!=typeof n&&(n="");var t=T.exec(n);return t?[t[1].toUpperCase(),t[2].replace(/x/g,"##"),t[3],t[4]]:null}function r(n,t){return n=Math.round(n),(!0===t?G:I)[n%12]+(Math.floor(n/12)-1)}function e(n,t){for(var r=[];t--;r[t]=t+n);return r}function m(n,t){for(var r=[];t--;r[t]=n-t);return r}function i(n,t){return null===n||null===t?[]:nan(t)})}function P(n){return o(n).filter(function(n,t,r){return 0===t||n!==r[t-1]})}function M(n){return"string"!=typeof n?An:_n[n]||(_n[n]=xn(n))}function a(n){var t=(n+1)%7;return t<0?7+t:t}function l(n,t){if(1===arguments.length)return function(t){return l(n,t)};var r=Kn(n),e=Qn(t);if(null===r||null===e)return null;var m=1===r.length?[r[0]+e[0]]:[r[0]+e[0],r[1]+e[1]];return mn(Un(m[0],m[1]))}function c(n,t){if(1===arguments.length)return function(t){return c(n,t)};var r=Kn(n);return null===r?null:mn(Un(r[0]+t))}function s(n,t){if(1===arguments.length)return function(t){return s(n,t)};var r=Kn(n),e=Kn(t);return null===e||null===r?null:e[0]-r[0]}function f(n,t){return 1===arguments.length?function(t){return l(t,n)}:l(t,n)}function d(n,t,r){var e=Qn(n),m=Qn(t);if(null===e||null===m)return null;var i=[e[0]+r*m[0],e[1]+r*m[1]];return Dn(Wn(i))}function p(n,t){return 1===arguments.length?function(t){return p(n,t)}:d(n,t,1)}function b(n,t){return 1===arguments.length?function(t){return p(n,t)}:d(n,t,-1)}function h(n,t){if(1===arguments.length)return function(t){return h(n,t)};var r=Kn(n),e=Kn(t);if(null===r||null===e||r.length!==e.length)return null;var m=1===r.length?[e[0]-r[0],-Math.floor(7*(e[0]-r[0])/12)]:[e[0]-r[0],e[1]-r[1]];return Dn(Wn(m))}function v(n,t){if(1===arguments.length)return function(t){return v(n,t)};var r=L(n),e=L(t);return null!==r.midi&&null!==e.midi?e.midi-r.midi:null!==r.chroma&&null!==e.chroma?(e.chroma-r.chroma+12)%12:null}function A(n){if(y(n))return n;if(!Array.isArray(n))return"";var t=[0,0,0,0,0,0,0,0,0,0,0,0];return n.map(nt).forEach(function(n){t[n]=1}),t.join("")}function g(n){return et=et||i(2048,4095).map(function(n){return n.toString(2)}),"number"==typeof n?et.filter(function(t){return rt(t)===n}):et.slice()}function j(n,t){t=!1!==t;var r=A(n).split("");return Mn(r.map(function(n,e){var m=u(e,r);return t&&"0"===m[0]?null:m.join("")}))}function y(n){return mt.test(n)}function O(n){return y(n)?Mn(n.split("").map(function(n,t){return"1"===n?it[t]:null})):[]}function x(n,t){return 1===arguments.length?function(t){return x(n,t)}:A(n)===A(t)}function _(n,t){return arguments.length>1?_(n)(t):(n=tt(n),function(t){return(t=tt(t))!==n&&(t&n)===t})}function z(n,t){return arguments.length>1?z(n)(t):(n=tt(n),function(t){return(t=tt(t))!==n&&(t|n)===t})}function q(n,t){return arguments.length>1?q(n)(t):(n=A(n),function(t){return"1"===n[nt(t)]})}function k(n,t){return 1===arguments.length?function(t){return k(n,t)}:t.filter(q(n))}function S(n,t){var r=D(n);return t=t||r[1],pt(t).map(l(r[0]))}function w(n){var t=D(n);return void 0!==Mt(t[1])}function D(n){if("string"!=typeof n)return["",""];var t=n.indexOf(" "),r=R(n.substring(0,t))||R(n)||"",e=""!==r?n.substring(r.length+1):n;return[r,e.length?e:""]}function E(n,t){var r=C(n);return t=t||r[1],xt(t).intervals.map(l(r[0]))}function C(n){var r=t(n);return""===r[0]?["",n]:"A"===r[0]&&"ug"===r[3]?["","aug"]:St.test(r[2])?[r[0]+r[1],r[2]+r[3]]:[r[0]+r[1]+r[2],r[3]]}var $="C C# Db D D# Eb E F F# Gb G G# Ab A A# Bb B".split(" "),F=function(n){return"string"!=typeof n?$.slice():$.filter(function(t){var r=t[1]||" ";return-1!==n.indexOf(r)})},G=F(" #"),I=F(" b"),T=/^([a-gA-G]?)(#{1,}|b{1,}|x{1,}|)(-?\d*)\s*(.*)$/,B=Object.freeze({pc:null,name:null,step:null,alt:null,oct:null,octStr:null,chroma:null,midi:null,freq:null}),N=[0,2,4,5,7,9,11],L=function(n,t){return void 0===t&&(t={}),function(r){return t[r]||(t[r]=n(r))}}(function(n){var r=t(n);if(""===r[0]||""!==r[3])return B;var e=r[0],m=r[1],i=r[2],u={letter:e,acc:m,octStr:i};return u.pc=u.letter+u.acc,u.name=u.pc+i,u.step=(u.letter.charCodeAt(0)+3)%7,u.alt="b"===u.acc[0]?-u.acc.length:u.acc.length,u.oct=i.length?+i:null,u.chroma=(N[u.step]+u.alt+120)%12,u.midi=null!==u.oct?N[u.step]+u.alt+12*(u.oct+1):null,u.freq=J(u.midi),Object.freeze(u)}),R=function(n){return L(n).name},U=function(n){return L(n).pc},H=function(n){return L(n).midi||+n||null},J=function(n,t){return void 0===t&&(t=440),"number"==typeof n?Math.pow(2,(n-69)/12)*t:null},K=function(n){return L(n).freq||J(n)},Q=Math.log(2),V=Math.log(440),W=function(n){var t=12*(Math.log(n)-V)/Q+69;return Math.round(100*t)/100},X=function(n){return L(n).chroma},Y=function(n){return L(n).oct},Z=function(n){return"CDEFGAB"[n]},nn=function(n,t){return Array(t+1).join(n)},tn=function(n,t){return"number"!=typeof n?"":t(n)},rn=function(n){return tn(n,function(n){return n<0?nn("b",-n):nn("#",n)})},en=function(n,t){void 0===n&&(n={}),void 0===t&&(t=null);var r=t?Object.assign({},L(t),n):n,e=r.step,m=r.alt,i=r.oct,u=Z(e);if(!u)return null;var o=u+rn(m);return i||0===i?o+i:o},mn=en,un=function(n,t){var e=L(n),m=e.alt,i=e.chroma,u=e.midi;if(null===i)return null;var o=!1===t?m<0:m>0;return null===u?U(r(i,o)):r(u,o)},on=function(n){return un(n,!1)},Pn=Object.freeze({names:F,tokenize:t,props:L,name:R,pc:U,midi:H,midiToFreq:J,freq:K,freqToMidi:W,chroma:X,oct:Y,stepToLetter:Z,altToAcc:rn,from:en,build:mn,fromMidi:r,simplify:un,enharmonic:on}),Mn=function(n){return n.filter(function(n){return 0===n||n})},an=function(n){var t=H(n);return null!==t?t:H(n+"-100")},ln=function(n,t){void 0===t&&(t=Math.random);for(var r,e,m=n.length;m;)r=t()*m--|0,e=n[m],n[m]=n[r],n[r]=e;return n},cn=function(n){return 0===n.length?[[]]:cn(n.slice(1)).reduce(function(t,r){return t.concat(n.map(function(t,e){var m=r.slice();return m.splice(e,0,n[0]),m}))},[])},sn=Object.freeze({range:i,rotate:u,compact:Mn,sort:o,unique:P,shuffle:ln,permutations:cn}),fn=new RegExp("^([-+]?\\d+)(d{1,4}|m|M|P|A{1,4})|(AA|A|P|M|m|d|dd)([-+]?\\d+)$"),dn=[0,2,4,5,7,9,11],pn=[0,1,2,3,4,5,6,5,4,3,2,1],bn="1P 2m 2M 3m 3M 4P 5P 6m 6M 7m 7M 8P".split(" "),hn=function(n){return"string"!=typeof n?bn.slice():bn.filter(function(t){return-1!==n.indexOf(t[1])})},vn=function(n){var t=fn.exec(n);return null===t?null:t[1]?[t[1],t[2]]:[t[4],t[3]]},An=Object.freeze({name:null,num:null,q:null,step:null,alt:null,dir:null,type:null,simple:null,semitones:null,chroma:null}),gn=function(n,t){return Array(Math.abs(t)+1).join(n)},jn=function(n,t){return"M"===t&&"M"===n?0:"P"===t&&"P"===n?0:"m"===t&&"M"===n?-1:/^A+$/.test(t)?t.length:/^d+$/.test(t)?"P"===n?-t.length:-t.length-1:null},yn=function(n,t){return 0===t?"M"===n?"M":"P":-1===t&&"M"===n?"m":t>0?gn("A",t):t<0?gn("d","P"===n?t:t+1):null},On=function(n){return(Math.abs(n)-1)%7},xn=function(n){var t=vn(n);if(null===t)return An;var r={num:+t[0],q:t[1]};return r.step=On(r.num),r.type="PMMPPMM"[r.step],"M"===r.type&&"P"===r.q?An:(r.name=""+r.num+r.q,r.dir=r.num<0?-1:1,r.simple=8===r.num||-8===r.num?r.num:r.dir*(r.step+1),r.alt=jn(r.type,r.q),r.oct=Math.floor((Math.abs(r.num)-1)/7),r.semitones=r.dir*(dn[r.step]+r.alt+12*r.oct),r.chroma=(r.dir*(dn[r.step]+r.alt)%12+12)%12,Object.freeze(r))},_n={},zn=function(n){return M(n).num},qn=function(n){return M(n).name},kn=function(n){return M(n).semitones},Sn=function(n){return M(n).chroma},wn=function(n){return"string"==typeof n&&(n=M(n).chroma),"number"==typeof n?pn[n%12]:null},Dn=function(n){void 0===n&&(n={});var t=n.num,r=n.step,e=n.alt,m=n.oct;void 0===m&&(m=1);var i=n.dir;if(void 0!==r&&(t=r+1+7*m),void 0===t)return null;var u=i<0?"-":"",o="PMMPPMM"[On(t)];return u+t+yn(o,e)},En=function(n){var t=M(n);return t===An?null:t.simple+t.q},Cn=function(n){var t=M(n);if(t===An)return null;var r=(7-t.step)%7,e="P"===t.type?-t.alt:-(t.alt+1);return Dn({step:r,alt:e,oct:t.oct,dir:t.dir})},$n=[1,2,2,3,3,4,5,5,6,6,7,7],Fn="P m M m M P d P m M m M".split(" "),Gn=function(n){var t=n<0?-1:1,r=Math.abs(n),e=r%12,m=Math.floor(r/12);return t*($n[e]+7*m)+Fn[e]},In=Object.freeze({names:hn,tokenize:vn,props:M,num:zn,name:qn,semitones:kn,chroma:Sn,ic:wn,build:Dn,simplify:En,invert:Cn,fromSemitones:Gn}),Tn=[0,2,4,-1,1,3,5],Bn=function(n){return Math.floor(7*n/12)},Nn=Tn.map(Bn),Ln=function(n){var t=n.step,r=n.alt,e=n.oct,m=n.dir;void 0===m&&(m=1);var i=Tn[t]+7*r;return null===e?[m*i]:[m*i,m*(e-Nn[t]-4*r)]},Rn=[3,0,4,1,5,2,6],Un=function(n,t,r){var e=Rn[a(n)],m=Math.floor((n+1)/7);return void 0===t?{step:e,alt:m,dir:r}:{step:e,alt:m,oct:t+4*m+Nn[e],dir:r}},Hn=function(n,t){return void 0===t&&(t={}),function(r){return t[r]||(t[r]=n(r))}},Jn=function(n){return Hn(function(t){var r=n(t);return null===r.name?null:Ln(r)})},Kn=Jn(L),Qn=Jn(M),Vn=function(n){return 7*n[0]+12*n[1]<0},Wn=function(n){return Vn(n)?Un(-n[0],-n[1],-1):Un(n[0],n[1],1)},Xn=Object.freeze({transpose:l,trFifths:c,fifths:s,transposeBy:f,addIntervals:d,add:p,subtract:b,interval:h,semitones:v}),Yn={chromatic:["1P 2m 2M 3m 3M 4P 4A 5P 6m 6M 7m 7M"],lydian:["1P 2M 3M 4A 5P 6M 7M"],major:["1P 2M 3M 4P 5P 6M 7M",["ionian"]],mixolydian:["1P 2M 3M 4P 5P 6M 7m",["dominant"]],dorian:["1P 2M 3m 4P 5P 6M 7m"],aeolian:["1P 2M 3m 4P 5P 6m 7m",["minor"]],phrygian:["1P 2m 3m 4P 5P 6m 7m"],locrian:["1P 2m 3m 4P 5d 6m 7m"],altered:["1P 2m 3m 3M 5d 6m 7m",["super locrian","diminished whole tone","pomeroy"]],iwato:["1P 2m 4P 5d 7m"],hirajoshi:["1P 2M 3m 5P 6m"],kumoijoshi:["1P 2m 4P 5P 6m"],pelog:["1P 2m 3m 5P 6m"],prometheus:["1P 2M 3M 4A 6M 7m"],ritusen:["1P 2M 4P 5P 6M"],scriabin:["1P 2m 3M 5P 6M"],piongio:["1P 2M 4P 5P 6M 7m"],augmented:["1P 2A 3M 5P 5A 7M"],neopolitan:["1P 2m 3m 4P 5P 6m 7M"],diminished:["1P 2M 3m 4P 5d 6m 6M 7M"],egyptian:["1P 2M 4P 5P 7m"],oriental:["1P 2m 3M 4P 5d 6M 7m"],spanish:["1P 2m 3M 4P 5P 6m 7m",["phrygian major"]],flamenco:["1P 2m 3m 3M 4A 5P 7m"],balinese:["1P 2m 3m 4P 5P 6m 7M"],persian:["1P 2m 3M 4P 5d 6m 7M"],bebop:["1P 2M 3M 4P 5P 6M 7m 7M"],enigmatic:["1P 2m 3M 5d 6m 7m 7M"],ichikosucho:["1P 2M 3M 4P 5d 5P 6M 7M"],"melodic minor":["1P 2M 3m 4P 5P 6M 7M"],"melodic minor second mode":["1P 2m 3m 4P 5P 6M 7m"],"lydian augmented":["1P 2M 3M 4A 5A 6M 7M"],"lydian dominant":["1P 2M 3M 4A 5P 6M 7m",["lydian b7"]],"melodic minor fifth mode":["1P 2M 3M 4P 5P 6m 7m",["hindu","mixolydian b6M"]],"locrian #2":["1P 2M 3m 4P 5d 6m 7m"],"locrian major":["1P 2M 3M 4P 5d 6m 7m",["arabian"]],"major pentatonic":["1P 2M 3M 5P 6M",["pentatonic"]],"lydian pentatonic":["1P 3M 4A 5P 7M",["chinese"]],"mixolydian pentatonic":["1P 3M 4P 5P 7m",["indian"]],"locrian pentatonic":["1P 3m 4P 5d 7m",["minor seven flat five pentatonic"]],"minor pentatonic":["1P 3m 4P 5P 7m"],"minor six pentatonic":["1P 3m 4P 5P 6M"],"minor hexatonic":["1P 2M 3m 4P 5P 7M"],"flat three pentatonic":["1P 2M 3m 5P 6M",["kumoi"]],"flat six pentatonic":["1P 2M 3M 5P 6m"],"major flat two pentatonic":["1P 2m 3M 5P 6M"],"whole tone pentatonic":["1P 3M 5d 6m 7m"],"ionian pentatonic":["1P 3M 4P 5P 7M"],"lydian #5P pentatonic":["1P 3M 4A 5A 7M"],"lydian dominant pentatonic":["1P 3M 4A 5P 7m"],"minor #7M pentatonic":["1P 3m 4P 5P 7M"],"super locrian pentatonic":["1P 3m 4d 5d 7m"],"in-sen":["1P 2m 4P 5P 7m"],"vietnamese 1":["1P 3m 4P 5P 6m"],"vietnamese 2":["1P 3m 4P 5P 7m"],"prometheus neopolitan":["1P 2m 3M 4A 6M 7m"],"major blues":["1P 2M 3m 3M 5P 6M"],"minor blues":["1P 3m 4P 5d 5P 7m",["blues"]],"composite blues":["1P 2M 3m 3M 4P 5d 5P 6M 7m"],"augmented heptatonic":["1P 2A 3M 4P 5P 5A 7M"],"dorian #4":["1P 2M 3m 4A 5P 6M 7m"],"lydian diminished":["1P 2M 3m 4A 5P 6M 7M"],"whole tone":["1P 2M 3M 4A 5A 7m"],"leading whole tone":["1P 2M 3M 4A 5A 7m 7M"],"harmonic minor":["1P 2M 3m 4P 5P 6m 7M"],"lydian minor":["1P 2M 3M 4A 5P 6m 7m"],"neopolitan minor":["1P 2m 3m 4P 5P 6m 7M"],"neopolitan major":["1P 2m 3m 4P 5P 6M 7M",["dorian b2"]],"neopolitan major pentatonic":["1P 3M 4P 5d 7m"],"romanian minor":["1P 2M 3m 5d 5P 6M 7m"],"double harmonic lydian":["1P 2m 3M 4A 5P 6m 7M"],"harmonic major":["1P 2M 3M 4P 5P 6m 7M"],"double harmonic major":["1P 2m 3M 4P 5P 6m 7M",["gypsy"]],"hungarian minor":["1P 2M 3m 4A 5P 6m 7M"],"hungarian major":["1P 2A 3M 4A 5P 6M 7m"],"spanish heptatonic":["1P 2m 3m 3M 4P 5P 6m 7m"],"todi raga":["1P 2m 3m 4A 5P 6m 7M"],"malkos raga":["1P 3m 4P 6m 7m"],"kafi raga":["1P 3m 3M 4P 5P 6M 7m 7M"],"purvi raga":["1P 2m 3M 4P 4A 5P 6m 7M"],"bebop dominant":["1P 2M 3M 4P 5P 6M 7m 7M"],"bebop minor":["1P 2M 3m 3M 4P 5P 6M 7m"],"bebop major":["1P 2M 3M 4P 5P 5A 6M 7M"],"bebop locrian":["1P 2m 3m 4P 5d 5P 6m 7m"],"minor bebop":["1P 2M 3m 4P 5P 6m 7m 7M"],"mystery #1":["1P 2m 3M 5d 6m 7m"],"minor six diminished":["1P 2M 3m 4P 5P 6m 6M 7M"],"ionian augmented":["1P 2M 3M 4P 5A 6M 7M"],"lydian #9":["1P 2m 3M 4A 5P 6M 7M"],"six tone symmetric":["1P 2m 3M 4P 5A 6M"]},Zn={M:["1P 3M 5P",["Major",""]],M13:["1P 3M 5P 7M 9M 13M",["maj13","Maj13"]],M6:["1P 3M 5P 13M",["6"]],M69:["1P 3M 5P 6M 9M",["69"]],M7add13:["1P 3M 5P 6M 7M 9M"],M7b5:["1P 3M 5d 7M"],M7b6:["1P 3M 6m 7M"],M7b9:["1P 3M 5P 7M 9m"],M7sus4:["1P 4P 5P 7M"],M9:["1P 3M 5P 7M 9M",["maj9","Maj9"]],M9b5:["1P 3M 5d 7M 9M"],M9sus4:["1P 4P 5P 7M 9M"],Madd9:["1P 3M 5P 9M",["2","add9","add2"]],Maj7:["1P 3M 5P 7M",["maj7","M7"]],Mb5:["1P 3M 5d"],Mb6:["1P 3M 13m"],Msus2:["1P 2M 5P",["add9no3","sus2"]],Msus4:["1P 4P 5P",["sus","sus4"]],Maddb9:["1P 3M 5P 9m"],m:["1P 3m 5P"],m11:["1P 3m 5P 7m 9M 11P",["_11"]],m11b5:["1P 3m 7m 12d 2M 4P",["h11","_11b5"]],m13:["1P 3m 5P 7m 9M 11P 13M",["_13"]],m6:["1P 3m 4P 5P 13M",["_6"]],m69:["1P 3m 5P 6M 9M",["_69"]],m7:["1P 3m 5P 7m",["minor7","_","_7"]],m7add11:["1P 3m 5P 7m 11P",["m7add4"]],m7b5:["1P 3m 5d 7m",["half-diminished","h7","_7b5"]],m9:["1P 3m 5P 7m 9M",["_9"]],m9b5:["1P 3m 7m 12d 2M",["h9","-9b5"]],mMaj7:["1P 3m 5P 7M",["mM7","_M7"]],mMaj7b6:["1P 3m 5P 6m 7M",["mM7b6"]],mM9:["1P 3m 5P 7M 9M",["mMaj9","-M9"]],mM9b6:["1P 3m 5P 6m 7M 9M",["mMaj9b6"]],mb6M7:["1P 3m 6m 7M"],mb6b9:["1P 3m 6m 9m"],o:["1P 3m 5d",["mb5","dim"]],o7:["1P 3m 5d 13M",["diminished","m6b5","dim7"]],o7M7:["1P 3m 5d 6M 7M"],oM7:["1P 3m 5d 7M"],sus24:["1P 2M 4P 5P",["sus4add9"]],madd4:["1P 3m 4P 5P"],madd9:["1P 3m 5P 9M"],4:["1P 4P 7m 10m",["quartal"]],5:["1P 5P"],7:["1P 3M 5P 7m",["Dominant","Dom"]],9:["1P 3M 5P 7m 9M",["79"]],11:["1P 5P 7m 9M 11P"],13:["1P 3M 5P 7m 9M 13M",["13_"]],64:["5P 8P 10M"],"M#5":["1P 3M 5A",["augmented","maj#5","Maj#5","+","aug"]],"M#5add9":["1P 3M 5A 9M",["+add9"]],"M13#11":["1P 3M 5P 7M 9M 11A 13M",["maj13#11","Maj13#11","M13+4","M13#4"]],"M6#11":["1P 3M 5P 6M 11A",["M6b5","6#11","6b5"]],"M69#11":["1P 3M 5P 6M 9M 11A"],"M7#11":["1P 3M 5P 7M 11A",["maj7#11","Maj7#11","M7+4","M7#4"]],"M7#5":["1P 3M 5A 7M",["maj7#5","Maj7#5","maj9#5","M7+"]],"M7#5sus4":["1P 4P 5A 7M"],"M7#9#11":["1P 3M 5P 7M 9A 11A"],"M9#11":["1P 3M 5P 7M 9M 11A",["maj9#11","Maj9#11","M9+4","M9#4"]],"M9#5":["1P 3M 5A 7M 9M",["Maj9#5"]],"M9#5sus4":["1P 4P 5A 7M 9M"],"11b9":["1P 5P 7m 9m 11P"],"13#11":["1P 3M 5P 7m 9M 11A 13M",["13+4","13#4"]],"13#9":["1P 3M 5P 7m 9A 13M",["13#9_"]],"13#9#11":["1P 3M 5P 7m 9A 11A 13M"],"13b5":["1P 3M 5d 6M 7m 9M"],"13b9":["1P 3M 5P 7m 9m 13M"],"13b9#11":["1P 3M 5P 7m 9m 11A 13M"],"13no5":["1P 3M 7m 9M 13M"],"13sus4":["1P 4P 5P 7m 9M 13M",["13sus"]],"69#11":["1P 3M 5P 6M 9M 11A"],"7#11":["1P 3M 5P 7m 11A",["7+4","7#4","7#11_","7#4_"]],"7#11b13":["1P 3M 5P 7m 11A 13m",["7b5b13"]],"7#5":["1P 3M 5A 7m",["+7","7aug","aug7"]],"7#5#9":["1P 3M 5A 7m 9A",["7alt","7#5#9_","7#9b13_"]],"7#5b9":["1P 3M 5A 7m 9m"],"7#5b9#11":["1P 3M 5A 7m 9m 11A"],"7#5sus4":["1P 4P 5A 7m"],"7#9":["1P 3M 5P 7m 9A",["7#9_"]],"7#9#11":["1P 3M 5P 7m 9A 11A",["7b5#9"]],"7#9#11b13":["1P 3M 5P 7m 9A 11A 13m"],"7#9b13":["1P 3M 5P 7m 9A 13m"],"7add6":["1P 3M 5P 7m 13M",["67","7add13"]],"7b13":["1P 3M 7m 13m"],"7b5":["1P 3M 5d 7m"],"7b6":["1P 3M 5P 6m 7m"],"7b9":["1P 3M 5P 7m 9m"],"7b9#11":["1P 3M 5P 7m 9m 11A",["7b5b9"]],"7b9#9":["1P 3M 5P 7m 9m 9A"],"7b9b13":["1P 3M 5P 7m 9m 13m"],"7b9b13#11":["1P 3M 5P 7m 9m 11A 13m",["7b9#11b13","7b5b9b13"]],"7no5":["1P 3M 7m"],"7sus4":["1P 4P 5P 7m",["7sus"]],"7sus4b9":["1P 4P 5P 7m 9m",["susb9","7susb9","7b9sus","7b9sus4","phryg"]],"7sus4b9b13":["1P 4P 5P 7m 9m 13m",["7b9b13sus4"]],"9#11":["1P 3M 5P 7m 9M 11A",["9+4","9#4","9#11_","9#4_"]],"9#11b13":["1P 3M 5P 7m 9M 11A 13m",["9b5b13"]],"9#5":["1P 3M 5A 7m 9M",["9+"]],"9#5#11":["1P 3M 5A 7m 9M 11A"],"9b13":["1P 3M 7m 9M 13m"],"9b5":["1P 3M 5d 7m 9M"],"9no5":["1P 3M 7m 9M"],"9sus4":["1P 4P 5P 7m 9M",["9sus"]],"m#5":["1P 3m 5A",["m+","mb6"]],"m11A 5":["1P 3m 6m 7m 9M 11P"],"m7#5":["1P 3m 6m 7m"],"m9#5":["1P 3m 6m 7m 9M"],"+add#9":["1P 3M 5A 9A"]},nt=function(n){return X(n)||Sn(n)||0},tt=function(n){return parseInt(A(n),2)},rt=function(n){return n.replace(/0/g,"").length},et=null,mt=/^[01]{12}$/,it="1P 2m 2M 3m 3M 4P 5d 5P 6m 6M 7m 7M".split(" "),ut=Object.freeze({chroma:A,chromas:g,modes:j,isChroma:y,intervals:O,isEqual:x,isSubsetOf:_,isSupersetOf:z,includes:q,filter:k}),ot=function(n){var t=Object.keys(n).sort(),r=[],e=[],m=function(n,t,m){r[n]=t,e[m]=e[m]||[],e[m].push(n)};t.forEach(function(t){var r=n[t][0].split(" "),e=n[t][1],i=A(r);m(t,r,i),e&&e.forEach(function(n){return m(n,r,i)})});var i=Object.keys(r).sort(),u=function(n){return r[n]};return u.names=function(n){return"string"==typeof n?(e[n]||[]).slice():(!0===n?i:t).slice()},u},Pt=function(n,t){var r=function(r){return n(r)||t(r)};return r.names=function(r){return n.names(r).concat(t.names(r))},r},Mt=ot(Yn),at=ot(Zn),lt=Pt(Mt,at),ct=Object.freeze({dictionary:ot,combine:Pt,scale:Mt,chord:at,pcset:lt}),st=Object.freeze({name:null,intervals:[],names:[],chroma:null,setnum:null}),ft=function(n,t){return function(r){return t[r]||(t[r]=n(r))}}(function(n){var t=Mt(n);if(!t)return st;var r={intervals:t,name:n};return r.chroma=A(t),r.setnum=parseInt(r.chroma,2),r.names=Mt.names(r.chroma),Object.freeze(r)},{}),dt=Mt.names,pt=function(n){var t=D(n);return ft(t[1]).intervals},bt=function(n){var t=pt(n),r=S(n);return j(t).map(function(n,e){var m=Mt.names(n)[0];if(m)return[r[e]||t[e],m]}).filter(function(n){return n})},ht=function(n){var t=_(pt(n));return at.names().filter(function(n){return t(at(n))})},vt=function(n){var t=Mn(n.map(U));if(!t.length)return t;var r=t[0],e=P(t);return u(e.indexOf(r),e)},At=function(n){if(!pt(n).length)return[];var t=z(pt(n));return Mt.names().filter(function(n){return t(Mt(n))})},gt=function(n){var t=_(pt(n));return Mt.names().filter(function(n){return t(Mt(n))})},jt=Object.freeze({props:ft,names:dt,intervals:pt,notes:S,exists:w,tokenize:D,modeNames:bt,chords:ht,toScale:vt,supersets:At,subsets:gt}),yt=at.names,Ot=Object.freeze({name:null,names:[],intervals:[],chroma:null,setnum:null}),xt=function(n,t){return void 0===t&&(t={}),function(r){return t[r]||(t[r]=n(r))}}(function(n){var t=at(n);if(!t)return Ot;var r={intervals:t,name:n};return r.chroma=A(t),r.setnum=parseInt(r.chroma,2),r.names=at.names(r.chroma),r}),_t=function(n){return xt(C(n)[1]).intervals},zt=function(n){return void 0!==at(C(n)[1])},qt=function(n){if(!_t(n).length)return[];var t=z(_t(n));return at.names().filter(function(n){return t(at(n))})},kt=function(n){var t=_(_t(n));return at.names().filter(function(n){return t(at(n))})},St=/^(6|64|7|9|11|13)$/,wt=Object.freeze({names:yt,props:xt,intervals:_t,notes:E,exists:zt,supersets:qt,subsets:kt,tokenize:C}),Dt=l,Et=h,Ct=L,$t=H,Ft=K,Gt=at,It=Mt;n.Array=sn,n.Note=Pn,n.Interval=In,n.Distance=Xn,n.Scale=jt,n.Chord=wt,n.PcSet=ut,n.Dictionary=ct,n.transpose=Dt,n.interval=Et,n.note=Ct,n.midi=$t,n.freq=Ft,n.chord=Gt,n.scale=It,Object.defineProperty(n,"__esModule",{value:!0})}); 2 | //# sourceMappingURL=tonal.min.js.map 3 | -------------------------------------------------------------------------------- /public/sqcr-demo/main.js: -------------------------------------------------------------------------------- 1 | // DATA container 2 | let data = []; 3 | 4 | 5 | // OSC.js stuff 6 | const handleMessage = (msg) => { 7 | // console.log('MSG', msg); 8 | data = msg.address.split('/'); 9 | } 10 | 11 | const initOSC = () => { 12 | // Init container 13 | 14 | // Init port 15 | oscPort = new osc.WebSocketPort({ 16 | url: "ws://localhost:8081" 17 | }); 18 | 19 | 20 | // listen 21 | oscPort.on('message', (msg) => { 22 | handleMessage(msg); // Debugging 23 | }); 24 | 25 | // open port 26 | oscPort.open(); 27 | }; 28 | 29 | // used later to start OSC 30 | window.initOSC = initOSC(); 31 | 32 | // Additional code below 33 | -------------------------------------------------------------------------------- /public/sqcr-demo/md/generative-hip-hop-slides.md: -------------------------------------------------------------------------------- 1 | class: center, middle 2 | iframeURL: /public/sqcr-demo/html/808.html 3 | iframeSelector: .frame-808-1 4 | 5 | # Making Self-Generating Hip Hop in JS 6 | 7 | 8 | 9 | ??? 10 | Sound check. Reset clock before starting. 11 | 12 | --- 13 | 14 | class: center, middle 15 | 16 | # Who Am I? 17 | 18 | --- 19 | 20 | # Who Am I? 21 | 22 | -- 23 | 24 | - I'm **[omar delarosa](https://omardelarosa.com)**. 25 | 26 | -- 27 | 28 | - I'm a Lead Software Engineer at Grubhub Seamless. (We're hiring.) 29 | 30 | -- 31 | 32 | - I play music in my spare time. 33 | 34 | --- 35 | 36 | class: center, middle 37 | 38 | # What is Music? 39 | 40 | --- 41 | 42 | class: center, middle 43 | 44 | ## Organized Sound Waves 45 | 46 | 47 | 48 | --- 49 | 50 | class: center, middle 51 | 52 | ### One Way To Do Music Randomly... 53 | 54 | --- 55 | 56 | class: center, middle 57 | iframeURL: /public/sqcr-demo/html/random-tones.html 58 | iframeSelector: .random-tones-frame 59 | 60 | ### Random Tone Frequencies 61 | 62 | 63 | 64 | --- 65 | 66 | class: center, middle 67 | 68 | ### Yuck. 69 | 70 | --- 71 | 72 | class: center, middle 73 | 74 | ## Can We Do Better? 75 | 76 | --- 77 | 78 | class: center, middle 79 | 80 | iframeURL: /public/sqcr-demo/html/scale-tones.html 81 | iframeSelector: .scale-tones-frame 82 | 83 | ### Random Tones from a Scale 84 | 85 | 86 | 87 | ??? 88 | Avoid talking over synths 89 | 90 | --- 91 | 92 | class: center, middle 93 | 94 | ### Better. 95 | 96 | --- 97 | 98 | class: center, middle 99 | 100 | ### We can do better yet. 101 | 102 | --- 103 | 104 | class: center, middle 105 | 106 | ### By Using _Markov Chains_ 107 | 108 | --- 109 | 110 | class: center, middle 111 | 112 | # Markov Chain 113 | 114 | ![](https://upload.wikimedia.org/wikipedia/commons/thumb/2/2b/Markovkate_01.svg/220px-Markovkate_01.svg.png) 115 | 116 | _A Markov chain is "a stochastic model describing a sequence of possible events in which the probability of each event depends only on the state attained in the previous event"_ 117 | 118 | [from Wikipedia](https://en.wikipedia.org/wiki/Markov_chain) 119 | 120 | --- 121 | 122 | class: center, top 123 | iframeURL: /public/sqcr-demo/html/notes-graph.html 124 | iframeSelector: .scale-tones-graph-frame 125 | 126 | #### Markov Chaining of Tones in a Scale 127 | 128 | 129 | 130 | ??? 131 | Avoid talking over synths 132 | 133 | --- 134 | 135 | # Markov Chain 136 | 137 | - Is like a state machine for gamblers 138 | 139 | -- 140 | 141 | - Markov chain models can be "generated" ML-style from a corpus text (or a MIDI file) 142 | 143 | -- 144 | 145 | - Data can be represented and stored easily as structured data formats such as JSON 146 | 147 | --- 148 | 149 | class: center, middle 150 | 151 | ## Slide Scope Creep 152 | 153 | #### (Not what this talk is about) 154 | 155 | --- 156 | 157 | class: center, middle 158 | iframeURL: /public/sqcr-demo/html/808.html 159 | iframeSelector: .frame-808-geez 160 | 161 | ## What Does This Have To Do With Hip Hop? 162 | 163 | 164 | 165 | --- 166 | 167 | class: middle, center 168 | 169 | # Rhythm, Harmony and Computation: 170 | 171 | #### A Brief History 172 | 173 | --- 174 | 175 | class: center, middle 176 | 177 | # 2000 B.C. to 1980s: 178 | 179 | ### Harmony, Rhythm and Music Theory: 180 | 181 | --- 182 | 183 | ### Harmony 184 | 185 | -- 186 | 187 | - **tone** - a unit of sound (aka a **note**) 188 | 189 | -- 190 | 191 | - **scale** - a ranked _Set_ of pitched tones 192 | 193 | -- 194 | 195 | - **melody** - a sequence of tones over time 196 | 197 | --- 198 | 199 | ### Harmony 200 | 201 | -- 202 | 203 | - **chord** - a group of tones played in (or close to) unison 204 | 205 | -- 206 | 207 | - **progression** - a sequence of chords over time 208 | 209 | --- 210 | 211 | ### Rhythm 212 | 213 | - **beat** - a single unit of rhythm 214 | 215 | - **measure** - a regularly spaced group of beats 216 | 217 | - **duration** - how long a tone lasts 218 | 219 | --- 220 | 221 | ### Rhythm & Durations as Fractions 222 | 223 | - Durations are all described as fractions of a **measure** 224 | 225 | -- 226 | 227 | - **1/4** Note 228 | 229 | -- 230 | 231 | - **1/8** Note 232 | 233 | -- 234 | 235 | - **1/16** Note 236 | 237 | --- 238 | 239 | ### Durations as Fractions 240 | 241 | - Not all are multiples of 2. 242 | 243 | -- 244 | 245 | - Some interesting things happen when you mix up durations where the denominator of the fraction is a multiple of 3. 246 | 247 | 248 | 249 | -- 250 | 251 | - This is common in hip hop beats. 252 | 253 | --- 254 | 255 | class: middle, center 256 | 257 | # 1980s to Now 258 | 259 | ### Rhythm and Computation 260 | 261 | --- 262 | 263 | iframeURL: /public/sqcr-demo/html/808.html 264 | iframeSelector: .frame-808 265 | 266 | ### Roland TR-808 267 | 268 | #### The classic drum machine of hip hop 269 | 270 | 271 | 272 | --- 273 | 274 | class: middle, center 275 | iframeURL: /public/sqcr-demo/html/akai.html 276 | iframeSelector: .frame-akai 277 | 278 | ### Akai MPC-2000XL 279 | 280 | #### A classic sampler for hip hop production 281 | 282 | 283 | 284 | --- 285 | 286 | # Rhythm and Computation 287 | 288 | #### Beat Grids 289 | 290 | ![](https://i.stack.imgur.com/DTE8c.png) 291 | 292 | - Centered around 1/16th note ticks 293 | 294 | - Can be difficult to "escape the grid" with durations < 1/16. 295 | 296 | - Also tough to use subdivisions that are multiples of 3. 297 | 298 | --- 299 | 300 | # Rhythm and Computation 301 | 302 | #### Beat Grids as Code 303 | 304 | ```javascript 305 | // 16-element arrays can represent rhythm patterns, but are tough to read. 306 | const kicks = [1,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; // prettier-ignore 307 | const snares = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; // prettier-ignore 308 | const hats = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1]; // prettier-ignore 309 | const cowbell = [0,0,0,0, 0,0,0,0, 0,0,0,0, 1,0,1,0]; // prettier-ignore 310 | ``` 311 | 312 | --- 313 | 314 | # Rhythm Notation 315 | 316 | #### Beat Grids as "Words" 317 | 318 | ```javascript 319 | // Easier to read, CPU-trivial preprocessing 320 | const kicks = fmt('1010 0010 0010 0010'); // prettier-ignore 321 | const snares = fmt('0000 1000 0000 1000'); // prettier-ignore 322 | const hats = fmt('1111 1111 1111 1111'); // prettier-ignore 323 | const cowbell = fmt('0000 0000 0000 01010'); // prettier-ignore 324 | ``` 325 | 326 | --- 327 | 328 | # Rhythm Notation 329 | 330 | #### Beat Grids as Lists of Words (a Language?) 331 | 332 | ```javascript 333 | // A list of patterns 334 | const kick_patterns = [ 335 | fmt('1010 0010 0010 0010'), 336 | fmt('1001 0001 0101 0010'), 337 | fmt('1000 0101 0100 0010'), 338 | fmt('1000 0010 0000 0100'), 339 | ]; 340 | ``` 341 | 342 | --- 343 | 344 | ## Generative Beats 345 | 346 | #### We could make two pattern sets 347 | 348 | ```javascript 349 | const kick_patterns = [ 350 | fmt('1010 0010 0010 0010'), 351 | fmt('1001 0001 0101 0010'), 352 | fmt('1000 0101 0100 0010'), 353 | fmt('1000 0010 0000 0100'), 354 | ]; 355 | 356 | const snare_patterns = [ 357 | fmt('0000 1000 0000 1000'), 358 | fmt('0010 1000 0000 1010'), 359 | fmt('0000 1000 0010 1000'), 360 | ]; 361 | ``` 362 | 363 | --- 364 | 365 | ## Generative Beats 366 | 367 | #### And randomly combine them 368 | 369 | ```javascript 370 | const kicks_sequence = [ 371 | ..._.sample(kick_patterns), 372 | ..._.sample(kick_patterns), 373 | ..._.sample(kick_patterns), 374 | ..._.sample(kick_patterns), 375 | ]; 376 | 377 | const snare_sequence = [ 378 | ..._.sample(snare_patterns), 379 | ..._.sample(snare_patterns), 380 | ..._.sample(snare_patterns), 381 | ..._.sample(snare_patterns), 382 | ]; 383 | 384 | playParallel(kicks_sequence, snare_sequence); 385 | ``` 386 | 387 | --- 388 | 389 | class: center, middle 390 | 391 | # Or we can do better with Markov Chains 392 | 393 | ![](https://upload.wikimedia.org/wikipedia/commons/thumb/2/2b/Markovkate_01.svg/220px-Markovkate_01.svg.png) 394 | 395 | --- 396 | 397 | class: center, top 398 | iframeURL: /public/sqcr-demo/html/notes-graph.html 399 | iframeSelector: .scale-tones-graph-frame2 400 | 401 | #### Markov Chaining of Tones in a Scale 402 | 403 | 404 | 405 | ??? 406 | Avoid talking over synths 407 | 408 | --- 409 | 410 | class: center, top 411 | iframeURL: /public/sqcr-demo/html/chords-graph.html 412 | iframeSelector: .scale-chords-graph-frame 413 | 414 | #### Markov Chaining of Chords in a Scale 415 | 416 | 417 | 418 | ??? 419 | Avoid talking over synths 420 | 421 | --- 422 | 423 | class: center, top 424 | iframeURL: /public/sqcr-demo/html/beats-graph.html 425 | iframeSelector: .beats-graph-frame 426 | 427 | #### Markov Chaining of Beat Durations 428 | 429 | 430 | 431 | --- 432 | 433 | ### Simple Markov Chain Implementation 434 | 435 | ```javascript 436 | class MarkovChain { 437 | constructor(obj = {}, states = [], initialState = 0) { 438 | this.graph = { ...obj }; 439 | this.states = [...states]; 440 | this.currentState = initialState; 441 | } 442 | 443 | set() { 444 | const newState = this.sample(this.graph[this.currentState]); 445 | this.currentState = newState; 446 | } 447 | 448 | next() { 449 | this.set(); 450 | return this.states[this.currentState]; 451 | } 452 | 453 | sample(list) { 454 | return list[Math.floor(list.length * Math.random())]; 455 | } 456 | } 457 | ``` 458 | 459 | --- 460 | 461 | ## Markov Chain of Notes 462 | 463 | - Using an adjacency list instead of matrix (for readability, simplicity) 464 | 465 | ```javascript 466 | const NOTES = ['C', 'D', 'E', 'F', 'G', 'A', 'B']; 467 | 468 | const G = { 469 | // Repeated notes represent higher probabilities 470 | '0': [1, 1, 0, 3, 4, 5, 6], // 0 -> 1 is 2/7, the rest 1/7 471 | '1': [0, 0, 2, 3], // 1 -> 0 is 1/2 the others 1/4 472 | '2': [1, 3, 4], 473 | '3': [4], // 3 -> 4 means state 4 always follows 3 or 1/1 probability 474 | '4': [5], 475 | '5': [5, 4, 1, 0], 476 | '6': [2, 2, 2, 3, 3], 477 | }; 478 | 479 | const mc = new MarkovChain(G, NOTES); 480 | ``` 481 | 482 | --- 483 | 484 | ## Markov Chain of Chords 485 | 486 | ```javascript 487 | const CHORDS = ['C maj', 'D min', 'E min', 'F maj', 'G maj', 'A min', 'B dim']; 488 | 489 | // Favors I <-> IV, V -> I cadences 490 | const G = { 491 | '0': [3, 3, 3, 5], 492 | '1': [2, 5], 493 | '2': [3], 494 | '3': [4, 4, 4, 1, 1], 495 | '4': [0, 0, 0, 5], 496 | '5': [1, 6], 497 | '6': [4], 498 | }; 499 | 500 | const mc = new MarkovChain(G, CHORDS); 501 | ``` 502 | 503 | --- 504 | 505 | ## Markov Chain of Rhythm Patterns 506 | 507 | ```javascript 508 | const M = 96; // MIDI Ticks in a measure 509 | 510 | const HATS = [ 511 | [M / 16, 4], 512 | [M / 12, 3], 513 | [M / 24, 6], 514 | [M / 32, 4], 515 | [M / 48, 6], 516 | [M / 64, 8], 517 | ]; 518 | 519 | // Favors steady 1/16 notes -- common in hip hop 520 | const G = { 521 | '0': [0, 0, 0, 0, 0, 0, 1, 2, 3, 4], // 0 -> 0 has 3/5 odds 522 | '1': [0, 0, 0, 3], 523 | '2': [0, 0, 0, 3], 524 | '3': [2, 5], 525 | '4': [2, 3, 4, 1], 526 | '5': [3, 2, 4, 2, 2], 527 | }; 528 | ``` 529 | 530 | --- 531 | 532 | # And So... 533 | 534 | - Tones, Melodies Chords, Beats, Measures, etc. can be thought of as "states" in a markov chin 535 | 536 | -- 537 | 538 | - This can create more natural sounding compositions than brute force randomization. 539 | 540 | -- 541 | 542 | - `MarkovChain` is a data model and thus framework/library agnostic. 543 | 544 | --- 545 | 546 | iframeURL: /public/sqcr-demo/html/matrix-16x8.html 547 | iframeSelector: .matrix-16x8 548 | 549 | ### Thanks 550 | 551 | 552 | 553 | - [Tone.js - full features JS code library](https://tonejs.github.io/) 554 | - [vis.js - dataviz library](http://visjs.org/) 555 | - [sqcr - a JS sequencer server I made to run some of the code in these slides](https://github.com/omardelarosa/sqcr) 556 | - [Chris Wilson - "A Tale of Two Clocks"](https://www.html5rocks.com/en/tutorials/audio/scheduling/#toc-usingsettimeout) 557 | - [Andrew Sorensen - "The Concert Programmer"](https://www.youtube.com/watch?v=yY1FSsUV-8c) 558 | - [Sam Aaron - "Programming as Performance"](https://www.youtube.com/watch?v=TK1mBqKvIyU) 559 | -------------------------------------------------------------------------------- /public/sqcr-demo/renderers/matrix-renderer.js: -------------------------------------------------------------------------------- 1 | const makeRow = (cols, idx = 1) => { 2 | const colDivs = []; 3 | 4 | for (let i = 0; i < cols; i++) { 5 | colDivs.push( 6 | `
`, 7 | ); 8 | } 9 | 10 | return `
${colDivs.join('\n')}
`; 11 | }; 12 | 13 | const makeVideoPlayer = () => ` 14 |
15 | 16 |
17 | `; 18 | 19 | const makeGrid = (rows, cols) => { 20 | const rowsHTML = []; 21 | 22 | for (let i = 0; i < rows; i++) { 23 | rowsHTML.push(makeRow(cols, i + 1)); 24 | } 25 | 26 | return `
${rowsHTML.join('\n')}
`; 27 | }; 28 | 29 | module.exports = (locals, scripts, scriptTags) => ` 30 | 31 | 32 | 33 | Untitled - osc.js demo 34 | 35 | 36 | 37 | 38 | 39 | 40 | ${scriptTags.join('\n')} 41 | 42 | 43 | 44 | ${'' && makeVideoPlayer()} 45 | ${makeGrid(8, 16)} 46 |
47 | start 48 | stop 49 |
50 | 51 | 52 | 53 | `; 54 | -------------------------------------------------------------------------------- /public/sqcr-demo/renderers/slider.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const getMarkdown = pathToMarkdown => { 5 | const absPath = path.join(process.cwd(), pathToMarkdown); 6 | try { 7 | return fs.readFileSync(absPath).toString(); 8 | } catch (e) { 9 | console.error('Markdown read error: ', e.message); 10 | return ''; 11 | } 12 | }; 13 | 14 | module.exports = (locals, scripts, scriptTags) => ` 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | `; 30 | -------------------------------------------------------------------------------- /public/sqcr-demo/sqcr-setup.js: -------------------------------------------------------------------------------- 1 | // SQCR.OSC = osc; 2 | // SQCR.MIDI = WebMidi; 3 | 4 | // sqcr = new SQCR(); 5 | 6 | function sqcrToggle() { 7 | if (sqcr.hasStopped) { 8 | sqcr.start(); 9 | } else { 10 | sqcr.stop(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /public/sqcr-demo/styles.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Color Pallette: 3 | * 1 - #ccfa99 4 | * 2 - #faf099 5 | * 3 - #99f0fa 6 | * 4 - #fa99f0 7 | * 5 - #cc99fa 8 | * 9 | */ 10 | 11 | div { 12 | min-height: 15px; 13 | } 14 | 15 | html, 16 | body, 17 | div { 18 | background-color: #fa99f0; 19 | } 20 | 21 | .k-col { 22 | height: 15px; 23 | } 24 | 25 | .icon-link { 26 | text-decoration: none; 27 | } 28 | 29 | .colorize { 30 | background-color: #faf099; 31 | } 32 | 33 | .colorize-0 { 34 | background-color: #ff0086; 35 | } 36 | 37 | .colorize-1 { 38 | background-color: #ccfa99; 39 | } 40 | 41 | .colorize-2 { 42 | background-color: #faf099; 43 | } 44 | 45 | .colorize-3 { 46 | background-color: #99f0fa; 47 | } 48 | 49 | .colorize-4 { 50 | background-color: #fa99f0; 51 | /* filter: blur(2px); */ 52 | } 53 | 54 | .colorize-1.colorize-3 { 55 | background-color: #ff7900; 56 | } 57 | 58 | .colorize-2.colorize-3 { 59 | background-color: #39ff14; 60 | } 61 | 62 | .colorize-4.colorize-1 { 63 | background-color: #99f0fa; 64 | } 65 | 66 | .colorize-2.colorize-1 { 67 | background-color: #ff7900; 68 | } 69 | -------------------------------------------------------------------------------- /public/sqcr-demo/visualizer.js: -------------------------------------------------------------------------------- 1 | let oscPort = {}; 2 | 3 | const handleMessage = msg => { 4 | console.log('message', msg); 5 | }; 6 | 7 | const LightEvent = data => new CustomEvent('light', { detail: data }); 8 | 9 | const elementMatrix = []; 10 | 11 | const mget = (mat, x, y) => { 12 | return (mat[x] && mat[x][y]) || undefined; 13 | }; 14 | 15 | const MAX_COLS = 16; 16 | const MAX_ROWS = 8; 17 | 18 | const blink = (el, dur = 100, color) => { 19 | const colorClass = `colorize-${color}`; 20 | // TODO: add random color 21 | el.classList.add(colorClass); 22 | 23 | setTimeout(() => { 24 | el.classList.remove(colorClass); 25 | }, dur); 26 | }; 27 | 28 | // For debugging purposes. 29 | const ticker = () => { 30 | let n = 0; 31 | let m = 0; 32 | 33 | return e => { 34 | const el = elementMatrix[n][m]; 35 | blink(el); 36 | m++; 37 | if (m >= MAX_COLS) { 38 | m = 0; 39 | n++; 40 | } 41 | 42 | if (n >= MAX_ROWS) { 43 | n = 0; 44 | } 45 | }; 46 | }; 47 | 48 | const initViz = () => { 49 | // Init container 50 | const $container = document.querySelector('.container'); 51 | 52 | const $rows = document.querySelectorAll('.k-row'); 53 | 54 | // TODO: make this more widely compatible and avoid .forEach 55 | $rows.forEach($row => { 56 | const rowRefs = []; 57 | $row.querySelectorAll('.k-col').forEach($cell => { 58 | rowRefs.push($cell); 59 | }); 60 | elementMatrix.push(rowRefs); 61 | }); 62 | 63 | $container.addEventListener('light', e => { 64 | const detail = e.detail || {}; 65 | const { address = '', args = [] } = detail; 66 | const addressParts = address.split('/'); 67 | if (addressParts[1] === 'blink') { 68 | const [, , m, n, color, dur] = addressParts; 69 | const mNum = Number(m); 70 | const nNum = Number(n); 71 | const el = mget(elementMatrix, mNum, nNum); 72 | el && blink(el, parseInt(dur), color); 73 | } 74 | }); 75 | 76 | window.Visualizer = { 77 | el: $container, 78 | 79 | blink: msg => { 80 | $container.dispatchEvent(LightEvent({ address: msg })); 81 | }, 82 | }; 83 | }; 84 | 85 | window.initViz = initViz; 86 | -------------------------------------------------------------------------------- /src/Markov.js: -------------------------------------------------------------------------------- 1 | class MarkovChain { 2 | constructor(obj = {}, states = [], initialState = 0) { 3 | this.graph = { ...obj }; 4 | this.states = [...states]; 5 | this.currentState = initialState; 6 | } 7 | 8 | /** 9 | * 10 | * Checks current state value without changing it. 11 | * 12 | */ 13 | peek() { 14 | return this.states[this.currentState]; 15 | } 16 | 17 | /** 18 | * 19 | * Checks the id of the current state 20 | * 21 | */ 22 | peekID() { 23 | return this.currentState; 24 | } 25 | 26 | /** 27 | * 28 | * Sets next state 29 | * 30 | */ 31 | 32 | set() { 33 | const newState = this.sample(this.graph[this.currentState]); 34 | this.currentState = newState; 35 | } 36 | 37 | /** 38 | * 39 | * Sets and returns next state in chain 40 | * 41 | */ 42 | next() { 43 | this.set(); 44 | return this.states[this.currentState]; 45 | } 46 | /** 47 | * 48 | * Sets and returns ID of next state in chain 49 | * 50 | */ 51 | nextID() { 52 | this.set(); 53 | return this.currentState; 54 | } 55 | 56 | /** 57 | * 58 | * Gets random element from a list 59 | * 60 | */ 61 | sample(list) { 62 | return list[Math.floor(list.length * Math.random())]; 63 | } 64 | } 65 | --------------------------------------------------------------------------------