├── LICENSE.txt ├── README.md ├── app.yaml ├── archive ├── Speaker_Icon_gray.svg └── raven_file.egg ├── css ├── main.css ├── nodes.css └── reset.css ├── favicon.ico ├── fonts ├── proxima_nova_reg-webfont.eot ├── proxima_nova_reg-webfont.svg ├── proxima_nova_reg-webfont.ttf └── proxima_nova_reg-webfont.woff ├── img ├── analyser-bg.png ├── body-bkg.gif ├── ico-analyser.gif ├── ico-arrow.png ├── ico-close.gif ├── ico-play.gif ├── ico-plus.png ├── ico-speaker.png ├── ico-stop.gif ├── logo.png ├── output-bkg.gif ├── slider-bkg.gif └── slider-handle.png ├── index.html ├── js ├── analyser.js ├── dialcontrol.js ├── dragging.js ├── jquery-1.7.2.min.js ├── jquery-ui-1.8.21.custom.min.js ├── main.js ├── pointerevents.js └── ui.js └── sounds ├── bark.mp3 ├── bass.ogg ├── drums.ogg ├── glass-hit.ogg ├── guitar.ogg ├── irHall.ogg ├── laser.ogg ├── sine-440Hz.ogg ├── voice.ogg └── youre-on-the-right-track.ogg /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Chris Wilson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Web Audio Playground 2 | ========== 3 | 4 | This is the source code for the 5 | [Web Audio Playground](https://webaudioplayground.appspot.com/) 6 | application. 7 | 8 | ## Installation ## 9 | 10 | To install the application locally, 11 | [download a copy of the Python App Engine SDK](https://developers.google.com/appengine/downloads) 12 | for your operating system, install it, point it at a check-out of this 13 | repo and view the application on a local port using the latest version of 14 | Google Chrome. 15 | 16 | Alternately, you can simply host the directory structure on a local web server, and load index.html. 17 | (The app cannot be run from file:// as XMLHTTPRequest doesn't work.) 18 | -------------------------------------------------------------------------------- /app.yaml: -------------------------------------------------------------------------------- 1 | application: webaudioplayground 2 | version: 1 3 | runtime: python27 4 | api_version: 1 5 | threadsafe: yes 6 | 7 | handlers: 8 | - url: / 9 | static_files: index.html 10 | upload: index.html 11 | 12 | - url: /css 13 | static_dir: css 14 | 15 | - url: /fonts 16 | static_dir: fonts 17 | 18 | - url: /img 19 | static_dir: img 20 | 21 | - url: /favicon.ico 22 | static_files: favicon.ico 23 | upload: favicon.ico 24 | 25 | - url: /js 26 | static_dir: js 27 | 28 | - url: /sounds 29 | static_dir: sounds 30 | -------------------------------------------------------------------------------- /archive/Speaker_Icon_gray.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 28 | 31 | 34 | 42 | 45 | 48 | 49 | -------------------------------------------------------------------------------- /archive/raven_file.egg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwilso/WebAudio/21a39ca012199b56824a5d64c6f687e4e3532d0c/archive/raven_file.egg -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | /* main.css */ 2 | 3 | /* --------------------------------------------------------------------------- 4 | * :: Type 5 | * 6 | * - Fonts 7 | * - Base 8 | * - Links 9 | * -------------------------------------------------------------------------*/ 10 | 11 | div.link { 12 | position:fixed; 13 | font-style: italic; 14 | color: lightgrey; 15 | bottom: 0px; 16 | right: 120px; 17 | } 18 | 19 | div.link a { 20 | color: white; 21 | font: inherit; 22 | text-decoration: underline; 23 | } 24 | 25 | /* Fonts */ 26 | @font-face { 27 | font-family: 'ProximaNovaRgRegular'; 28 | src: url('../fonts/proxima_nova_reg-webfont.eot'); 29 | src: url('../fonts/proxima_nova_reg-webfont.eot?#iefix') format('embedded-opentype'), 30 | url('../fonts/proxima_nova_reg-webfont.woff') format('woff'), 31 | url('../fonts/proxima_nova_reg-webfont.ttf') format('truetype'), 32 | url('../fonts/proxima_nova_reg-webfont.svg#ProximaNovaRgRegular') format('svg'); 33 | font-weight: normal; 34 | font-style: normal; 35 | } 36 | 37 | /* Base */ 38 | html, 39 | button, 40 | input, 41 | select, 42 | textarea { 43 | font-family: 'ProximaNovaRgRegular', 'Helvetica Neue', Helvetica, Arial, sans-serif; 44 | color: #4c4c4c; 45 | } 46 | body { 47 | font-size: 1em; 48 | line-height: 1.4; 49 | } 50 | 51 | /* Links */ 52 | a, 53 | a:visited { 54 | color: #4c4c4c; 55 | font-size: .6875em; /* 11 / 16 */ 56 | text-decoration: none; 57 | } 58 | a:hover { 59 | color: #000; 60 | text-decoration: none; 61 | } 62 | 63 | 64 | /* --------------------------------------------------------------------------- 65 | * :: Raw elements 66 | * -------------------------------------------------------------------------*/ 67 | body { 68 | background: url('../img/body-bkg.gif'); 69 | } 70 | 71 | 72 | /* --------------------------------------------------------------------------- 73 | * :: Main 74 | * 75 | * - Header 76 | * - Nav 77 | * - Controls 78 | * - Dropdown 79 | * - Dropdown arrow 80 | * - Modules 81 | * -------------------------------------------------------------------------*/ 82 | #main { 83 | padding-right: 106px; 84 | position: relative; 85 | z-index: 1; 86 | } 87 | 88 | /* Header */ 89 | #header { 90 | margin: 0; 91 | padding: 2em 0 0 2em; 92 | } 93 | 94 | /* Nav */ 95 | #nav { 96 | margin: 0 0 1.3em; 97 | padding: 1.3em 0 0 2em; 98 | } 99 | 100 | /* Controls */ 101 | #controls { 102 | list-style: none; 103 | margin: 0; 104 | padding: 0; 105 | } 106 | #controls li { 107 | float: left; 108 | margin-right: .8em; 109 | position: relative; 110 | } 111 | #controls a { 112 | background: url('../img/ico-plus.png') .9em 50% no-repeat #fff; 113 | border-radius: 5px; 114 | color: #4c4c4c; 115 | display: block; 116 | font-size: .625em; /* 11 / 16 */ 117 | padding: .5em 1em .4em 2.6em; 118 | text-transform: uppercase; 119 | -webkit-transition: background-color 0.1s linear; 120 | transition: background-color 0.1s linear; 121 | } 122 | 123 | /* Dropdown */ 124 | #controls .sub-menu { 125 | display: none; 126 | left: 0; 127 | padding-top: 8px; 128 | position: absolute; 129 | z-index: 1000; 130 | } 131 | #controls ul { 132 | background-color: #fff; 133 | border: 1px solid #aaa; 134 | border-radius: 5px; 135 | box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); 136 | list-style: none; 137 | margin: 0; 138 | padding: .2em 0; 139 | position: relative; 140 | } 141 | #controls ul li { 142 | float: none; 143 | margin: 0; 144 | padding: 0; 145 | } 146 | #controls ul a { 147 | background: none; 148 | background-image: none; 149 | border-radius: 0; 150 | padding: .5em 1em .4em; 151 | white-space: nowrap; 152 | } 153 | #controls ul a:hover { 154 | background-color: #dbdbdb; 155 | } 156 | #controls li:hover .sub-menu { 157 | display: block; 158 | } 159 | 160 | /* Dropdown arrow */ 161 | #controls ul:after, 162 | #controls ul:before { 163 | bottom: 100%; 164 | border: solid transparent; 165 | content: " "; 166 | height: 0; 167 | width: 0; 168 | position: absolute; 169 | pointer-events: none; 170 | } 171 | #controls ul:after { 172 | border-bottom-color: #ffffff; 173 | border-width: 8px; 174 | left: 20%; 175 | margin-left: -8px; 176 | } 177 | #controls ul:before { 178 | border-bottom-color: #aaa; 179 | border-width: 9px; 180 | left: 20%; 181 | margin-left: -9px; 182 | } 183 | 184 | /* Modules */ 185 | #modules { 186 | padding-left: 2em; 187 | } 188 | 189 | /* --------------------------------------------------------------------------- 190 | * :: Output 191 | * -------------------------------------------------------------------------*/ 192 | #output { 193 | background: 194 | url('../img/ico-speaker.png') 50% 50% no-repeat, 195 | url('../img/output-bkg.gif') 0 0 repeat; 196 | border-left: 1px solid #363636; 197 | box-shadow: 0 0 3px rgba(0,0,0, .4); 198 | bottom: 0; 199 | display: block; 200 | height: 100%; 201 | position: absolute; 202 | right: 0; 203 | top: 0; 204 | width: 105px; 205 | z-index: 100; 206 | } 207 | #output .node { 208 | left: -18px; 209 | margin-top: -11px; 210 | position: absolute; 211 | top: 50%; 212 | } 213 | 214 | 215 | /* --------------------------------------------------------------------------- 216 | * :: Global classes and styles 217 | * 218 | * - Clearfix 219 | * - Nodes 220 | * - Modules 221 | * - Module nodes 222 | * - Module close 223 | * - Module footer 224 | * - Control group 225 | * - Slider 226 | * -------------------------------------------------------------------------*/ 227 | 228 | /* Clearfix */ 229 | .container:before, .container:after { content: ""; display: table; } 230 | .container:after { clear: both; } 231 | .container { *zoom: 1; } 232 | 233 | /* Nodes */ 234 | .node { 235 | background: #fff; 236 | border-radius: 50%; 237 | box-shadow: 0 0 3px rgba(0,0,0, .5); 238 | cursor: pointer; 239 | height: 34px; 240 | position: relative; 241 | width: 34px; 242 | } 243 | .node span { 244 | border-radius: 50%; 245 | display: block; 246 | height: 24px; 247 | left: 5px; 248 | position: absolute; 249 | top: 5px; 250 | width: 24px; 251 | } 252 | .node-input span { 253 | background: #67a90d; 254 | } 255 | .node-output span { 256 | background: #ec2f13; 257 | } 258 | .node:hover { 259 | box-shadow: 0 0 3px rgba(0,0,0, 1); 260 | } 261 | 262 | /* Modules */ 263 | .module { 264 | border-radius: 5px; 265 | box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); 266 | display: inline-block; 267 | position: absolute; 268 | } 269 | .module .content { 270 | background: #fff; 271 | border-radius: 5px; 272 | min-width: 10em; 273 | overflow: hidden; 274 | padding: 1em 1.25em 1.25em 1.25em; 275 | position: relative; 276 | } 277 | .module .content img { 278 | display: block; 279 | margin: 0 auto; 280 | } 281 | .module.has-footer .content { 282 | border-bottom: 1px solid #dbdbdb; 283 | border-radius: 5px 5px 0 0; 284 | } 285 | .module h6 { 286 | font-size: .75em; 287 | margin: 0 0 1em; 288 | text-align: center; 289 | text-transform: uppercase; 290 | } 291 | 292 | /* Module nodes */ 293 | .module .node { 294 | position: absolute; 295 | margin-top: -16px; 296 | top: 50%; 297 | } 298 | .module .node-input { 299 | left: -18px; 300 | } 301 | .module .node-output { 302 | right: -18px; 303 | } 304 | 305 | /* Module close */ 306 | .module .close { 307 | background: url('../img/ico-close.gif') 0 0 no-repeat; 308 | height: 8px; 309 | text-indent: -999em; 310 | position: absolute; 311 | right: 8px; 312 | top: 8px; 313 | width: 9px; 314 | } 315 | .module .close:hover { 316 | opacity: .8 317 | } 318 | 319 | .module.analyzer { 320 | } 321 | 322 | /* Module footer */ 323 | .module footer { 324 | background-color: #fff; 325 | background-image: -webkit-linear-gradient(top, rgb(255, 255, 255), rgb(219, 219, 219)); 326 | background-image: linear-gradient(top, rgb(255, 255, 255), rgb(219, 219, 219)); 327 | border-radius: 0 0 5px 5px; 328 | overflow: hidden; 329 | padding: 0em .4em .1em; 330 | } 331 | .module footer select { 332 | background: url('../img/ico-arrow.png') 100% 50% no-repeat; 333 | border: none; 334 | box-sizing: border-box; 335 | cursor: default; 336 | font-family: 'Droid Serif', Georgia, serif; 337 | font-size: .625em; 338 | font-style: italic; 339 | outline: none; 340 | white-space: pre; 341 | width: 100%; 342 | -webkit-appearance: none; 343 | -webkit-box-align: center; 344 | } 345 | .module.has-loop footer select { 346 | width: 70%; 347 | } 348 | .module footer .loop { 349 | border-left: 1px solid #dbdbdb; 350 | display: inline; 351 | float: right; 352 | font-size: .625em; 353 | padding: .4em 0 .4em .6em; 354 | text-transform: uppercase; 355 | width: 23%; 356 | } 357 | 358 | /* Control group */ 359 | .control-group { 360 | margin: 0 0 1.2em; 361 | } 362 | .control-group.last { 363 | margin: 0; 364 | } 365 | 366 | .control-group:last-of-type { 367 | margin: 0; 368 | } 369 | 370 | .control-group.disabled .slider-info { 371 | color: #e0e0e0; 372 | } 373 | /* Slider */ 374 | .slider-info { 375 | font-size: .625em; /* 10 / 16 */ 376 | margin-bottom: 1em; 377 | overflow: hidden; 378 | padding-bottom: .4em; 379 | position: relative; 380 | } 381 | .slider-info .label { 382 | display: inline; 383 | float: left; 384 | text-transform: uppercase; 385 | width: 49%; 386 | } 387 | .slider-info .value { 388 | display: inline; 389 | float: right; 390 | font-weight: bold; 391 | text-align: right; 392 | text-transform: uppercase; 393 | width: 49%; 394 | } 395 | .module .slider-info:last-child { 396 | margin-bottom: 0; 397 | } 398 | 399 | .ui-slider { 400 | background: url('../img/slider-bkg.gif') 0 0 repeat-x; 401 | border: 0 none; 402 | color: #fff; 403 | height: 11px; 404 | position: relative; 405 | text-align: left; 406 | } 407 | .ui-state-default, 408 | .ui-widget-content .ui-state-default, 409 | .ui-widget-header .ui-state-default { 410 | border: 1px solid #cccccc; 411 | background: #f6f6f6; 412 | font-weight: bold; 413 | color: #1c94c4; 414 | } 415 | .ui-slider .ui-slider-handle { 416 | background: url('../img/slider-handle.png') 0 1px no-repeat; 417 | border: 0 none; 418 | cursor: default; 419 | height: 18px; 420 | margin-left: -.6em; 421 | position: absolute; 422 | top: -.3em; 423 | width: 16px; 424 | z-index: 2; 425 | } 426 | .ui-slider .ui-slider-range { 427 | background-position: 0 0; 428 | border: 0; 429 | display: block; 430 | font-size: .7em; 431 | height: 100%; 432 | position: absolute; 433 | top: 0; 434 | z-index: 1; 435 | } 436 | .ui-slider .ui-slider-range-min { 437 | left: 0; 438 | } 439 | .ui-slider .ui-slider-range-max { 440 | right: 0; 441 | } 442 | 443 | input[type="range"] { 444 | -webkit-appearance: none; 445 | background-color: #303030; 446 | height: 2px; 447 | display: block; 448 | margin-top: 5px; 449 | margin-bottom: 15px; 450 | width: 100%; 451 | } 452 | 453 | input[type="range"]::-webkit-slider-thumb { 454 | -webkit-appearance: none; 455 | position: relative; 456 | top: 0px; 457 | z-index: 1; 458 | width: 20px; 459 | height: 20px; 460 | cursor: pointer; 461 | -webkit-box-shadow: 0px 6px 5px 0px rgba(0,0,0,0.6); 462 | -moz-box-shadow: 0px 6px 5px 0px rgba(0,0,0,0.6); 463 | box-shadow: 0px 6px 5px 0px rgba(0,0,0,0.6); 464 | -webkit-border-radius: 40px; 465 | -moz-border-radius: 40px; 466 | border-radius: 40px; 467 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ff7373), color-stop(50%,#a00000), color-stop(51%,#700000), color-stop(100%,#ff7373)); 468 | } 469 | -------------------------------------------------------------------------------- /css/nodes.css: -------------------------------------------------------------------------------- 1 | .node { 2 | position: absolute; 3 | background-color: #aaf; 4 | border: 4px solid blue; 5 | border-radius: 20px; 6 | color: #000000; 7 | padding: 10px; 8 | width: 150px; 9 | height: 150px; 10 | font: 14px/10px Geneva, sans-serif; 11 | vertical-align: text-top; 12 | } 13 | 14 | .inputconnector { 15 | position: absolute; 16 | left:-12px; top:75px; 17 | width:10px; height:10px; 18 | border:5px solid red; 19 | border-radius: 10px; 20 | background: gray; 21 | } 22 | 23 | .inputconnector.connected, .outputconnector.connected { background: lightgreen } 24 | .inputconnector.canConnect, .outputconnector.canConnect { background: pink } 25 | 26 | .outputconnector { 27 | position: absolute; 28 | right:-12px; top:75px; 29 | width:10px; height:10px; 30 | border:5px solid green; 31 | border-radius: 10px; 32 | background: gray; 33 | } 34 | 35 | .dialcontrol { 36 | position: absolute; 37 | } 38 | 39 | #code { font: 10pt Courier New, fixed; } -------------------------------------------------------------------------------- /css/reset.css: -------------------------------------------------------------------------------- 1 | /* reset.css */ 2 | 3 | article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { display: block; } 4 | audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; } 5 | audio:not([controls]) { display: none; } 6 | [hidden] { display: none; } 7 | 8 | html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } 9 | html, button, input, select, textarea { font-family: sans-serif; color: #222; } 10 | body { margin: 0; font-size: 1em; line-height: 1.4; } 11 | 12 | ::-moz-selection { background: #00e; color: #fff; text-shadow: none; } 13 | ::selection { background: #00e; color: #fff; text-shadow: none; } 14 | 15 | a { color: #00e; } 16 | a:visited { color: #551a8b; } 17 | a:hover { color: #06e; } 18 | a:focus { outline: thin dotted; } 19 | a:hover, a:active { outline: 0; } 20 | abbr[title] { border-bottom: 1px dotted; } 21 | b, strong { font-weight: bold; } 22 | blockquote { margin: 1em 40px; } 23 | dfn { font-style: italic; } 24 | hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; } 25 | ins { background: #ff9; color: #000; text-decoration: none; } 26 | mark { background: #ff0; color: #000; font-style: italic; font-weight: bold; } 27 | pre, code, kbd, samp { font-family: monospace, serif; _font-family: 'courier new', monospace; font-size: 1em; } 28 | pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; } 29 | 30 | q { quotes: none; } 31 | q:before, q:after { content: ""; content: none; } 32 | small { font-size: 85%; } 33 | sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } 34 | sup { top: -0.5em; } 35 | sub { bottom: -0.25em; } 36 | 37 | ul, ol { margin: 1em 0; padding: 0 0 0 40px; } 38 | dd { margin: 0 0 0 40px; } 39 | nav ul, nav ol { list-style: none; list-style-image: none; margin: 0; padding: 0; } 40 | 41 | img { border: 0; -ms-interpolation-mode: bicubic; vertical-align: middle; } 42 | svg:not(:root) { overflow: hidden; } 43 | figure { margin: 0; } 44 | 45 | form { margin: 0; } 46 | fieldset { border: 0; margin: 0; padding: 0; } 47 | 48 | label { cursor: pointer; } 49 | legend { border: 0; *margin-left: -7px; padding: 0; white-space: normal; } 50 | button, input, select, textarea { font-size: 100%; margin: 0; vertical-align: baseline; *vertical-align: middle; } 51 | button, input { line-height: normal; } 52 | button, input[type="button"], input[type="reset"], input[type="submit"] { cursor: pointer; -webkit-appearance: button; *overflow: visible; } 53 | button[disabled], input[disabled] { cursor: default; } 54 | input[type="checkbox"], input[type="radio"] { box-sizing: border-box; padding: 0; *width: 13px; *height: 13px; } 55 | input[type="search"] { -webkit-appearance: textfield; -moz-box-sizing: content-box; -webkit-box-sizing: content-box; box-sizing: content-box; } 56 | input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; } 57 | button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } 58 | textarea { overflow: auto; vertical-align: top; resize: vertical; } 59 | input:valid, textarea:valid { } 60 | input:invalid, textarea:invalid { background-color: #f0dddd; } 61 | 62 | table { border-collapse: collapse; border-spacing: 0; } 63 | td { vertical-align: top; } 64 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwilso/WebAudio/21a39ca012199b56824a5d64c6f687e4e3532d0c/favicon.ico -------------------------------------------------------------------------------- /fonts/proxima_nova_reg-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwilso/WebAudio/21a39ca012199b56824a5d64c6f687e4e3532d0c/fonts/proxima_nova_reg-webfont.eot -------------------------------------------------------------------------------- /fonts/proxima_nova_reg-webfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 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 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | -------------------------------------------------------------------------------- /fonts/proxima_nova_reg-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwilso/WebAudio/21a39ca012199b56824a5d64c6f687e4e3532d0c/fonts/proxima_nova_reg-webfont.ttf -------------------------------------------------------------------------------- /fonts/proxima_nova_reg-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwilso/WebAudio/21a39ca012199b56824a5d64c6f687e4e3532d0c/fonts/proxima_nova_reg-webfont.woff -------------------------------------------------------------------------------- /img/analyser-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwilso/WebAudio/21a39ca012199b56824a5d64c6f687e4e3532d0c/img/analyser-bg.png -------------------------------------------------------------------------------- /img/body-bkg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwilso/WebAudio/21a39ca012199b56824a5d64c6f687e4e3532d0c/img/body-bkg.gif -------------------------------------------------------------------------------- /img/ico-analyser.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwilso/WebAudio/21a39ca012199b56824a5d64c6f687e4e3532d0c/img/ico-analyser.gif -------------------------------------------------------------------------------- /img/ico-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwilso/WebAudio/21a39ca012199b56824a5d64c6f687e4e3532d0c/img/ico-arrow.png -------------------------------------------------------------------------------- /img/ico-close.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwilso/WebAudio/21a39ca012199b56824a5d64c6f687e4e3532d0c/img/ico-close.gif -------------------------------------------------------------------------------- /img/ico-play.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwilso/WebAudio/21a39ca012199b56824a5d64c6f687e4e3532d0c/img/ico-play.gif -------------------------------------------------------------------------------- /img/ico-plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwilso/WebAudio/21a39ca012199b56824a5d64c6f687e4e3532d0c/img/ico-plus.png -------------------------------------------------------------------------------- /img/ico-speaker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwilso/WebAudio/21a39ca012199b56824a5d64c6f687e4e3532d0c/img/ico-speaker.png -------------------------------------------------------------------------------- /img/ico-stop.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwilso/WebAudio/21a39ca012199b56824a5d64c6f687e4e3532d0c/img/ico-stop.gif -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwilso/WebAudio/21a39ca012199b56824a5d64c6f687e4e3532d0c/img/logo.png -------------------------------------------------------------------------------- /img/output-bkg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwilso/WebAudio/21a39ca012199b56824a5d64c6f687e4e3532d0c/img/output-bkg.gif -------------------------------------------------------------------------------- /img/slider-bkg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwilso/WebAudio/21a39ca012199b56824a5d64c6f687e4e3532d0c/img/slider-bkg.gif -------------------------------------------------------------------------------- /img/slider-handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwilso/WebAudio/21a39ca012199b56824a5d64c6f687e4e3532d0c/img/slider-handle.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Web Audio Playground 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | 24 |
25 | 28 | 57 |
58 | 59 |
60 | 61 |
62 | 63 |
64 |
 
65 |
66 | 67 | 68 | 69 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /js/analyser.js: -------------------------------------------------------------------------------- 1 | var animationRunning = false; 2 | var analysers = new Array; 3 | var CANVAS_WIDTH = 150; 4 | var CANVAS_HEIGHT = 120; 5 | 6 | function updateAnalyser(e) { 7 | var SPACER_WIDTH = 1; 8 | var BAR_WIDTH = 1; 9 | var OFFSET = 100; 10 | var CUTOFF = 23; 11 | var ctx = e.drawingContext; 12 | var canvas = ctx.canvas; 13 | var numBars = Math.round(canvas.width / SPACER_WIDTH); 14 | var freqByteData = new Uint8Array(e.audioNode.frequencyBinCount); 15 | 16 | e.audioNode.getByteFrequencyData(freqByteData); 17 | //analyser.getByteTimeDomainData(freqByteData); 18 | 19 | ctx.clearRect(0, 0, canvas.width, canvas.height); 20 | ctx.fillStyle = '#F6D565'; 21 | ctx.lineCap = 'round'; 22 | var multiplier = e.audioNode.frequencyBinCount / numBars; 23 | 24 | // Draw rectangle for each frequency bin. 25 | for (var i = 0; i < numBars; ++i) { 26 | var magnitude = 0; 27 | var offset = Math.floor( i * multiplier ); 28 | // gotta sum/average the block, or we miss narrow-bandwidth spikes 29 | for (var j = 0; j< multiplier; j++) 30 | magnitude += freqByteData[offset + j]; 31 | magnitude = magnitude / multiplier; 32 | var magnitude2 = freqByteData[i * multiplier]; 33 | ctx.fillStyle = "hsl( " + Math.round((i*360)/numBars) + ", 100%, 50%)"; 34 | ctx.fillRect(i * SPACER_WIDTH, canvas.height, BAR_WIDTH, -magnitude); 35 | } 36 | } 37 | 38 | function updateAnalysers(time) { 39 | var rAF = window.requestAnimationFrame || 40 | window.webkitRequestAnimationFrame || 41 | window.mozRequestAnimationFrame || 42 | window.msRequestAnimationFrame; 43 | rAF( updateAnalysers ); 44 | 45 | for (var i = 0; i < analysers.length; i++) 46 | updateAnalyser(analysers[i]); 47 | } 48 | 49 | -------------------------------------------------------------------------------- /js/dialcontrol.js: -------------------------------------------------------------------------------- 1 | function drawDialControl(e,val){ 2 | var ctx; 3 | var start = (e.width / 2) + 0.5; // The 0.5 is to kick it over half a pixel for anti-aliasing 4 | var arcrad = (e.width / 3 ); 5 | var textSize = Math.floor(e.width/25); 6 | 7 | if (e.getContext && (ctx = e.getContext('2d'))) { 8 | // Clear the canvas 9 | ctx.clearRect(0,0,e.width,e.height); 10 | 11 | // Draw knob background 12 | ctx.lineWidth = 5 + textSize; 13 | ctx.strokeStyle = "#ff0000"; 14 | ctx.lineCap = "round"; 15 | ctx.beginPath(); 16 | ctx.arc( start, start , arcrad,Math.PI*0.75,Math.PI*0.25,false); 17 | ctx.stroke(); 18 | 19 | // Draw knob foreground 20 | var end = Math.PI * (0.75 + (val * 0.015)); 21 | ctx.strokeStyle = "#0000ff"; 22 | ctx.beginPath(); 23 | ctx.arc(start, start , arcrad, Math.PI*0.75,end,false); 24 | ctx.stroke(); 25 | 26 | // Draw the text label 27 | ctx.font = (10 + textSize) + "px Geneva, sans-serif"; 28 | ctx.textAlign = "center"; 29 | ctx.textBaseline = "center"; 30 | ctx.fillText( Math.floor(val), start, start, 30 ); 31 | } 32 | } 33 | 34 | function updateDialControl(e,val){ 35 | e.setAttribute("val", val); 36 | drawDialControl(e, val); 37 | if (e.onUpdateDial) 38 | e.onUpdateDial(e); 39 | } 40 | 41 | function startDraggingDialControl(event) { 42 | dragObj.currentElement = event.target; 43 | 44 | // Save starting positions of cursor with respect to the page. 45 | dragObj.cursorStartX = event.clientX + window.scrollX; 46 | dragObj.cursorStartY = event.clientY + window.scrollY; 47 | dragObj.originalValue = parseInt( event.target.getAttribute("val")); 48 | 49 | // Capture mousemove and mouseup events on the page. 50 | document.addEventListener("mousemove", whileDraggingDialControl, true); 51 | document.addEventListener("mouseup", stopDraggingDialControl, true); 52 | event.stopPropagation(); 53 | event.preventDefault(); 54 | } 55 | 56 | function whileDraggingDialControl(event) { 57 | var x, y, v; 58 | 59 | // Get cursor position with respect to the page. 60 | x = event.clientX + window.scrollX; 61 | y = event.clientY + window.scrollY; 62 | 63 | // Change control value by the same amount the cursor has moved. 64 | v = Math.max( 0, Math.min( ((x - dragObj.cursorStartX) + dragObj.originalValue), 200)); 65 | updateDialControl( dragObj.currentElement, v/2 ); 66 | // Don't use y yet - but would be: (y - dragObj.cursorStartY); 67 | 68 | event.stopPropagation(); 69 | event.preventDefault(); 70 | } 71 | 72 | function stopDraggingDialControl(event) { 73 | // Stop capturing mousemove and mouseup events. 74 | document.removeEventListener("mousemove", whileDraggingDialControl, true); 75 | document.removeEventListener("mouseup", stopDraggingDialControl, true); 76 | event.stopPropagation(); 77 | } 78 | 79 | // This creates and returns - but DOES NOT INSERT - a new DialControl. 80 | function createNewDialControl( size, initialValue ) { 81 | var e=document.createElement("canvas"); 82 | e.width=size; 83 | e.height=size; 84 | e.className = "dialcontrol"; 85 | e.onmousedown=startDraggingDialControl; 86 | updateDialControl( e, initialValue ); 87 | return e; 88 | } 89 | 90 | 91 | -------------------------------------------------------------------------------- /js/dragging.js: -------------------------------------------------------------------------------- 1 | var dragObj = new Object(); 2 | dragObj.zIndex = 0; 3 | dragObj.lastLit = null; 4 | 5 | // Node Dragging functions - these are used for dragging the audio modules, 6 | // like Destination or AudioSourceBuffer. 7 | 8 | function skipDefault(event) { 9 | if ((event.target.tagName != "SELECT")&&(event.target.tagName != "INPUT")) 10 | event.preventDefault(); 11 | } 12 | 13 | function startDraggingNode(event) { 14 | var el; 15 | var x, y; 16 | 17 | if (event.target.tagName == "SELECT") 18 | return; 19 | el = event.target; 20 | 21 | if (el.nodeType == 3) // if it's a text node 22 | el = el.parentNode; 23 | if (el.classList.contains("module-title")) 24 | el = el.parentNode; 25 | if (el.classList.contains("content")) 26 | el = el.parentNode; 27 | if (!el.classList.contains("module")) 28 | return; 29 | 30 | dragObj.elNode = el; 31 | /* 32 | // If this is a text node, use its parent element. 33 | if ((dragObj.elNode.nodeType == 3)||(dragObj.elNode.className == "analyserCanvas")) 34 | dragObj.elNode = dragObj.elNode.parentNode; 35 | */ 36 | 37 | // Get cursor position with respect to the page. 38 | x = event.clientX + window.scrollX; 39 | y = event.clientY + window.scrollY; 40 | 41 | // Save starting positions of cursor and element. 42 | dragObj.cursorStartX = x; 43 | dragObj.cursorStartY = y; 44 | dragObj.elStartLeft = parseInt(dragObj.elNode.style.left, 10); 45 | dragObj.elStartTop = parseInt(dragObj.elNode.style.top, 10); 46 | 47 | if (isNaN(dragObj.elStartLeft)) dragObj.elStartLeft = 0; 48 | if (isNaN(dragObj.elStartTop)) dragObj.elStartTop = 0; 49 | 50 | // Update element's z-index. 51 | dragObj.elNode.style.zIndex = ++dragObj.zIndex; 52 | 53 | // Capture mousemove and mouseup events on the page. 54 | document.addEventListener("mousemove", whileDraggingNode, true); 55 | document.addEventListener("mouseup", stopDraggingNode, true); 56 | // document.addEventListener( 'pointermove', whileDraggingNode, true ); 57 | // document.addEventListener( 'pointerup', stopDraggingNode, true ); 58 | // document.addEventListener( 'pointerleave', stopDraggingNode, true ); 59 | event.preventDefault(); 60 | } 61 | 62 | function whileDraggingNode(event) { 63 | var x, y; 64 | var e = dragObj.elNode; 65 | 66 | // Get cursor position with respect to the page. 67 | x = event.clientX + window.scrollX; 68 | y = event.clientY + window.scrollY; 69 | 70 | // Move drag element by the same amount the cursor has moved. 71 | e.style.left = (dragObj.elStartLeft + x - dragObj.cursorStartX) + "px"; 72 | e.style.top = (dragObj.elStartTop + y - dragObj.cursorStartY) + "px"; 73 | 74 | if (e.inputConnections) { // update any lines that point in here. 75 | var c; 76 | 77 | var off = e.inputs; 78 | x = window.scrollX + 12; 79 | y = window.scrollY + 12; 80 | 81 | while (off) { 82 | x+=off.offsetLeft; 83 | y+=off.offsetTop; 84 | off=off.offsetParent; 85 | } 86 | 87 | for (c=0; cdest (x1->x2) 263 | if (!dragObj.originIsInput) { // need to flip 264 | var shape = connectorShape; 265 | var x = shape.getAttributeNS(null, "x2"); 266 | var y = shape.getAttributeNS(null, "y2"); 267 | shape.setAttributeNS(null, "x2", shape.getAttributeNS(null, "x1")); 268 | shape.setAttributeNS(null, "y2", shape.getAttributeNS(null, "y1")); 269 | shape.setAttributeNS(null, "x1", x); 270 | shape.setAttributeNS(null, "y1", y); 271 | } 272 | // Put an entry into the destinations's inputs 273 | if (!dst.inputConnections) 274 | dst.inputConnections = new Array(); 275 | connector = new Object(); 276 | connector.line = connectorShape; 277 | connector.source = src; 278 | connector.destination = dst; 279 | dst.inputConnections.push(connector); 280 | 281 | // if the source has an audio node, connect them up. 282 | // AudioBufferSourceNodes may not have an audio node yet. 283 | if (src.audioNode ) 284 | src.audioNode.connect(dst.audioNode); 285 | 286 | if (dst.onConnectInput) 287 | dst.onConnectInput(); 288 | 289 | connectorShape.inputConnection = connector; 290 | connectorShape.destination = dst; 291 | connectorShape.onclick = deleteConnection; 292 | 293 | connectorShape = null; 294 | } 295 | 296 | function deleteConnection() { 297 | var connections = this.destination.inputConnections; 298 | breakSingleInputConnection( connections, connections.indexOf( this.inputConnection ) ); 299 | } 300 | 301 | function breakSingleInputConnection( connections, index ) { 302 | var connector = connections[index]; 303 | var src = connector.source; 304 | 305 | // delete us from their .outputConnections, 306 | src.outputConnections.splice( src.outputConnections.indexOf( connector.destination ), 1); 307 | if (src.audioNode) { // they may not have an audioNode, if they're a BSN or an Oscillator 308 | // call disconnect() on the src, 309 | src.audioNode.disconnect(); 310 | // if there's anything left in their outputConnections, re.connect() those nodes. 311 | // TODO: again, this will break due to src resetting. 312 | for (var j=0; jx2 425 | // That makes updating them when we drag nodes around easier. 426 | connectNodes(dragObj.elNode, toElem); 427 | return; 428 | } 429 | } 430 | } 431 | 432 | // Otherwise, delete the line 433 | dragObj.connectorShape.parentNode.removeChild(dragObj.connectorShape); 434 | dragObj.connectorShape = null; 435 | } 436 | 437 | function stringifyAudio() { 438 | var code = "var context=null;\ntry \{\n\tcontext = window.webkitAudioContext ? " + 439 | "new webkitAudioContext\(\) : new audioContext\(\);\n}\ncatch(e) \{\n" + 440 | "\talert\('Web Audio API is not supported in this browser'\);\n\}\n"; 441 | 442 | var nodes = document.getElementById("soundField").children; 443 | 444 | for (var i=0; i=0)&&c(b,!e)}}),a(function(){var b=document.body,c=b.appendChild(c=document.createElement("div"));c.offsetHeight,a.extend(c.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0}),a.support.minHeight=c.offsetHeight===100,a.support.selectstart="onselectstart"in c,b.removeChild(c).style.display="none"}),a.extend(a.ui,{plugin:{add:function(b,c,d){var e=a.ui[b].prototype;for(var f in d)e.plugins[f]=e.plugins[f]||[],e.plugins[f].push([c,d[f]])},call:function(a,b,c){var d=a.plugins[b];if(!d||!a.element[0].parentNode)return;for(var e=0;e0?!0:(b[d]=1,e=b[d]>0,b[d]=0,e)},isOverAxis:function(a,b,c){return a>b&&a=9||!!b.button?this._mouseStarted?(this._mouseDrag(b),b.preventDefault()):(this._mouseDistanceMet(b)&&this._mouseDelayMet(b)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,b)!==!1,this._mouseStarted?this._mouseDrag(b):this._mouseUp(b)),!this._mouseStarted):this._mouseUp(b)},_mouseUp:function(b){return a(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,b.target==this._mouseDownEvent.target&&a.data(b.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(b)),!1},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(a){return this.mouseDelayMet},_mouseStart:function(a){},_mouseDrag:function(a){},_mouseStop:function(a){},_mouseCapture:function(a){return!0}})})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05 14 | * https://github.com/jquery/jquery-ui 15 | * Includes: jquery.ui.slider.js 16 | * Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */ 17 | (function(a,b){var c=5;a.widget("ui.slider",a.ui.mouse,{widgetEventPrefix:"slide",options:{animate:!1,distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null},_create:function(){var b=this,d=this.options,e=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),f="",g=d.values&&d.values.length||1,h=[];this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget"+" ui-widget-content"+" ui-corner-all"+(d.disabled?" ui-slider-disabled ui-disabled":"")),this.range=a([]),d.range&&(d.range===!0&&(d.values||(d.values=[this._valueMin(),this._valueMin()]),d.values.length&&d.values.length!==2&&(d.values=[d.values[0],d.values[0]])),this.range=a("
").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(d.range==="min"||d.range==="max"?" ui-slider-range-"+d.range:"")));for(var i=e.length;ic&&(f=c,g=a(this),i=b)}),c.range===!0&&this.values(1)===c.min&&(i+=1,g=a(this.handles[i])),j=this._start(b,i),j===!1?!1:(this._mouseSliding=!0,h._handleIndex=i,g.addClass("ui-state-active").focus(),k=g.offset(),l=!a(b.target).parents().andSelf().is(".ui-slider-handle"),this._clickOffset=l?{left:0,top:0}:{left:b.pageX-k.left-g.width()/2,top:b.pageY-k.top-g.height()/2-(parseInt(g.css("borderTopWidth"),10)||0)-(parseInt(g.css("borderBottomWidth"),10)||0)+(parseInt(g.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(b,i,e),this._animateOff=!0,!0))},_mouseStart:function(a){return!0},_mouseDrag:function(a){var b={x:a.pageX,y:a.pageY},c=this._normValueFromMouse(b);return this._slide(a,this._handleIndex,c),!1},_mouseStop:function(a){return this.handles.removeClass("ui-state-active"),this._mouseSliding=!1,this._stop(a,this._handleIndex),this._change(a,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1,!1},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(a){var b,c,d,e,f;return this.orientation==="horizontal"?(b=this.elementSize.width,c=a.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(b=this.elementSize.height,c=a.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),d=c/b,d>1&&(d=1),d<0&&(d=0),this.orientation==="vertical"&&(d=1-d),e=this._valueMax()-this._valueMin(),f=this._valueMin()+d*e,this._trimAlignValue(f)},_start:function(a,b){var c={handle:this.handles[b],value:this.value()};return this.options.values&&this.options.values.length&&(c.value=this.values(b),c.values=this.values()),this._trigger("start",a,c)},_slide:function(a,b,c){var d,e,f;this.options.values&&this.options.values.length?(d=this.values(b?0:1),this.options.values.length===2&&this.options.range===!0&&(b===0&&c>d||b===1&&c1){this.options.values[b]=this._trimAlignValue(c),this._refreshValue(),this._change(null,b);return}if(!arguments.length)return this._values();if(!a.isArray(arguments[0]))return this.options.values&&this.options.values.length?this._values(b):this.value();d=this.options.values,e=arguments[0];for(f=0;f=this._valueMax())return this._valueMax();var b=this.options.step>0?this.options.step:1,c=(a-this._valueMin())%b,d=a-c;return Math.abs(c)*2>=b&&(d+=c>0?b:-b),parseFloat(d.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var b=this.options.range,c=this.options,d=this,e=this._animateOff?!1:c.animate,f,g={},h,i,j,k;this.options.values&&this.options.values.length?this.handles.each(function(b,i){f=(d.values(b)-d._valueMin())/(d._valueMax()-d._valueMin())*100,g[d.orientation==="horizontal"?"left":"bottom"]=f+"%",a(this).stop(1,1)[e?"animate":"css"](g,c.animate),d.options.range===!0&&(d.orientation==="horizontal"?(b===0&&d.range.stop(1,1)[e?"animate":"css"]({left:f+"%"},c.animate),b===1&&d.range[e?"animate":"css"]({width:f-h+"%"},{queue:!1,duration:c.animate})):(b===0&&d.range.stop(1,1)[e?"animate":"css"]({bottom:f+"%"},c.animate),b===1&&d.range[e?"animate":"css"]({height:f-h+"%"},{queue:!1,duration:c.animate}))),h=f}):(i=this.value(),j=this._valueMin(),k=this._valueMax(),f=k!==j?(i-j)/(k-j)*100:0,g[d.orientation==="horizontal"?"left":"bottom"]=f+"%",this.handle.stop(1,1)[e?"animate":"css"](g,c.animate),b==="min"&&this.orientation==="horizontal"&&this.range.stop(1,1)[e?"animate":"css"]({width:f+"%"},c.animate),b==="max"&&this.orientation==="horizontal"&&this.range[e?"animate":"css"]({width:100-f+"%"},{queue:!1,duration:c.animate}),b==="min"&&this.orientation==="vertical"&&this.range.stop(1,1)[e?"animate":"css"]({height:f+"%"},c.animate),b==="max"&&this.orientation==="vertical"&&this.range[e?"animate":"css"]({height:100-f+"%"},{queue:!1,duration:c.animate}))}}),a.extend(a.ui.slider,{version:"1.8.21"})})(jQuery);; -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | //TODO: 2 | /* 3 | 4 | "add new" in ABS 5 | drop sound file on ABS module 6 | 7 | "add new" in convolver 8 | drop convolver file on module 9 | 10 | wavetable 11 | 12 | */ 13 | 14 | 15 | var audioContext = null; 16 | var tempx=50, tempy=150; 17 | var idX = 0; 18 | var lastBufferLoaded = null; 19 | var buffers = new Array(); 20 | 21 | function createNewModule( nodeType, input, output ) { 22 | var e=document.createElement("div"); 23 | e.className="module " + nodeType; 24 | e.id = "module" + idX++; 25 | e.style.left="" + tempx + "px"; 26 | e.style.top="" + tempy + "px"; 27 | if (tempx > 700) { 28 | tempy += 250; 29 | tempx = 50; 30 | } else 31 | tempx += 250; 32 | if (tempy > 600) 33 | tempy = 100; 34 | 35 | e.setAttribute("audioNodeType", nodeType ); 36 | e.addEventListener("mousedown", startDraggingNode, false); 37 | // e.addEventListener( 'pointerdown', startDraggingNode, false ); 38 | var content = document.createElement("div"); 39 | content.className="content"; 40 | e.appendChild(content); 41 | var title = document.createElement("h6"); 42 | title.className = "module-title"; 43 | title.appendChild(document.createTextNode(nodeType)); 44 | content.appendChild(title); 45 | 46 | if (input) { 47 | var i=document.createElement("div"); 48 | i.className="node node-input "; 49 | i.addEventListener( "mousedown", startDraggingConnector, true ); 50 | // i.addEventListener( 'pointerdown', startDraggingConnector, false ); 51 | i.innerHTML = " "; 52 | e.appendChild(i); 53 | e.inputs = i; 54 | } 55 | 56 | if (output) { 57 | var i=document.createElement("div"); 58 | i.className="node node-output"; 59 | i.addEventListener( "mousedown", startDraggingConnector, true ); 60 | // i.addEventListener( 'pointerdown', startDraggingConnector, false ); 61 | i.innerHTML = " "; 62 | e.appendChild(i); 63 | e.outputs = i; 64 | } 65 | 66 | var close = document.createElement("a"); 67 | close.href = "#"; 68 | close.className = "close"; 69 | close.onclick = deleteModule; 70 | e.appendChild( close ); 71 | 72 | // add the node into the soundfield 73 | document.getElementById("modules").appendChild(e); 74 | return(content); 75 | } 76 | 77 | function addModuleSlider( element, label, ivalue, imin, imax, stepUnits, units, onUpdate ) { 78 | var group = document.createElement("div"); 79 | //group.className="control-group"; 80 | 81 | var info = document.createElement("div"); 82 | info.className="slider-info"; 83 | info.setAttribute("min", imin ); 84 | info.setAttribute("max", imax ); 85 | var lab = document.createElement("span"); 86 | lab.className="label"; 87 | lab.appendChild(document.createTextNode(label)); 88 | info.appendChild(lab); 89 | var val = document.createElement("span"); 90 | val.className="value"; 91 | val.appendChild(document.createTextNode("" + ivalue + " " + units)); 92 | 93 | // cache the units type on the element for updates 94 | val.setAttribute("units",units); 95 | info.appendChild(val); 96 | 97 | group.appendChild(info); 98 | 99 | /* 100 | var slider = document.createElement("div"); 101 | slider.className="slider"; 102 | group.appendChild(slider); 103 | 104 | element.appendChild(group); 105 | return $( slider ).slider( { slide: onUpdate, value: ivalue, min: imin, max: imax, step: stepUnits } ); 106 | */ 107 | var slider = document.createElement("input"); 108 | slider.type="range"; 109 | slider.min = imin; 110 | slider.max = imax; 111 | slider.value = ivalue; 112 | slider.step = stepUnits; 113 | slider.oninput = onUpdate; 114 | group.appendChild(slider); 115 | 116 | element.appendChild(group); 117 | return slider; 118 | 119 | } 120 | 121 | 122 | function onUpdateDetune(event) { 123 | var e = updateSlider(event); 124 | e.oscillatorDetune = event.target.value; 125 | if (e.audioNode) 126 | e.audioNode.detune.value = e.oscillatorDetune; 127 | } 128 | 129 | function onUpdateOscillatorFrequency(event) { 130 | var e = updateSlider(event); 131 | e.oscillatorFrequency = event.target.value; 132 | if (e.audioNode) 133 | e.audioNode.frequency.value = e.oscillatorFrequency; 134 | } 135 | 136 | function onUpdateFrequency(event) { 137 | updateSlider(event).audioNode.frequency.value = event.target.value; 138 | } 139 | 140 | function onUpdateGain(event) { 141 | updateSlider(event).audioNode.gain.value = event.target.value; 142 | } 143 | 144 | function onUpdateDelay(event, ui) { 145 | updateSlider(event).audioNode.delayTime.value = event.target.value; 146 | } 147 | 148 | function onUpdateThreshold(event, ui) { 149 | updateSlider(event).audioNode.threshold.value = event.target.value; 150 | } 151 | 152 | function onUpdateKnee(event, ui) { 153 | updateSlider(event).audioNode.knee.value = event.target.value; 154 | } 155 | 156 | function onUpdateRatio(event, ui) { 157 | updateSlider(event).audioNode.ratio.value = event.target.value; 158 | } 159 | 160 | function onUpdateAttack(event, ui) { 161 | updateSlider(event).audioNode.attack.value = event.target.value; 162 | } 163 | 164 | function onUpdateRelease(event, ui) { 165 | updateSlider(event).audioNode.release.value = event.target.value; 166 | } 167 | 168 | function onUpdateQ(event, ui) { 169 | updateSlider(event).audioNode.Q.value = event.target.value; 170 | } 171 | 172 | function updateSlider(event) { 173 | var e = event.target; 174 | 175 | //TODO: yes, this is lazy coding, and fragile. 176 | var output = e.parentNode.children[0].children[1]; 177 | 178 | // update the value text 179 | output.innerHTML = "" + event.target.value + " " + output.getAttribute("units"); 180 | 181 | var module = e; 182 | while (module && !module.classList.contains("module")) 183 | module = module.parentNode; 184 | return module; 185 | } 186 | 187 | function onPlayOscillator(event) { 188 | var playButton = event.target; 189 | if (playButton.isPlaying) { 190 | //stop 191 | playButton.isPlaying = false; 192 | playButton.src = "img/ico-play.gif"; 193 | var e = playButton.parentNode; 194 | while (e && !e.classList.contains("module")) 195 | e = e.parentNode; 196 | if (e && e.audioNode) { 197 | e.audioNode.stop(0); 198 | e.audioNode = null; 199 | } 200 | } else { 201 | playButton.isPlaying = true; 202 | playButton.src = "img/ico-stop.gif"; 203 | var e = playButton.parentNode; 204 | while (e && !e.classList.contains("module")) 205 | e = e.parentNode; 206 | if (e) { 207 | var oscNode = audioContext.createOscillator(); 208 | oscNode.frequency.value = e.oscillatorFrequency; 209 | oscNode.detune.value = e.oscillatorDetune; 210 | oscNode.type = e.oscillatorType; 211 | e.audioNode = oscNode; 212 | if (e.outputConnections) { 213 | e.outputConnections.forEach(function(connection){ 214 | oscNode.connect( connection.destination.audioNode ); }); 215 | } 216 | oscNode.start(0); 217 | } 218 | } 219 | } 220 | 221 | function onToggleLoop(event) { 222 | var checkbox = event.target; 223 | 224 | var e = checkbox.parentNode; 225 | while (e && !e.classList.contains("module")) 226 | e = e.parentNode; 227 | if (e) 228 | e.loop = checkbox.checked; 229 | } 230 | 231 | function onToggleNormalize(event) { 232 | var checkbox = event.target; 233 | 234 | var e = checkbox.parentNode; 235 | while (e && !e.audioNode) 236 | e = e.parentNode; 237 | if (e) 238 | e.audioNode.normalize = checkbox.checked; 239 | } 240 | 241 | var oscTypes = ["sine","square","sawtooth","triangle"]; 242 | 243 | function switchOscillatorTypes(event) { 244 | var select = event.target; 245 | 246 | var e = select.parentNode; 247 | while (e && !e.classList.contains("module")) 248 | e = e.parentNode; 249 | if (e) { 250 | // TODO: wavetable! 251 | 252 | //cache the type 253 | e.oscillatorType = oscTypes[select.selectedIndex]; 254 | 255 | // if we have a playing oscillator, go ahead and switch it live 256 | if (e.audioNode) 257 | e.audioNode.type = oscTypes[select.selectedIndex]; 258 | } 259 | } 260 | 261 | function switchAudioBuffer(event) { 262 | var select = event.target; 263 | 264 | var e = select.parentNode; 265 | while (e && !e.classList.contains("module")) 266 | e = e.parentNode; 267 | 268 | if (e) { 269 | if (select.selectedIndex == select.options.length-1) { 270 | // TODO: open dialog, add new 271 | alert( "Not yet implemented."); 272 | return; 273 | } 274 | e.buffer = buffers[ select.selectedIndex ]; 275 | } 276 | } 277 | 278 | var filterTypes = [ "lowpass", 279 | "highpass", 280 | "bandpass", 281 | "lowshelf", 282 | "highshelf", 283 | "peaking", 284 | "notch", 285 | "allpass" ]; 286 | 287 | function switchFilterTypes(event) { 288 | var select = event.target; 289 | var fType = select.selectedIndex; 290 | 291 | var e = select.parentNode; 292 | while (e && !e.audioNode) 293 | e = e.parentNode; 294 | if (e) { 295 | e.audioNode.type = filterTypes[fType]; 296 | if (fType>2 && fType<6) { 297 | e.children[0].children[3].classList.remove("disabled"); 298 | } else { 299 | e.children[0].children[3].classList.add("disabled"); 300 | } 301 | } 302 | } 303 | 304 | function stopABSource( playButton ) { 305 | //stop 306 | playButton.isPlaying = false; 307 | playButton.src = "img/ico-play.gif"; 308 | var e = playButton.parentNode; 309 | while (e && !e.classList.contains("module")) 310 | e = e.parentNode; 311 | 312 | if ( !e ) 313 | return; 314 | if ( e.stopTimer ) { 315 | window.clearTimeout(e.stopTimer); 316 | e.stopTimer = 0; 317 | } 318 | if ( e.audioNode ) 319 | e.audioNode.stop(0); 320 | 321 | } 322 | 323 | function onPlayABSource(event) { 324 | var playButton = event.target; 325 | if (playButton.isPlaying) 326 | stopABSource( playButton ); 327 | else { 328 | playButton.isPlaying = true; 329 | playButton.src = "img/ico-stop.gif"; 330 | 331 | var e = playButton.parentNode; 332 | while (e && !e.classList.contains("module")) 333 | e = e.parentNode; 334 | 335 | if (!e) 336 | return; 337 | 338 | // if there's already a note playing, cut it off. 339 | if (e.audioNode) { 340 | e.audioNode.stop(0); 341 | e.audioNode.disconnect(); 342 | e.audioNode = null; 343 | } 344 | 345 | // create a new BufferSource, set it to the buffer and connect it. 346 | var n = e.audioNode = audioContext.createBufferSource(); 347 | n.loop = e.loop; 348 | n.buffer = e.buffer; 349 | 350 | if (e.outputConnections) { 351 | e.outputConnections.forEach(function(connection){ 352 | n.connect( connection.destination.audioNode ); }); 353 | } 354 | e.audioNode.start(audioContext.currentTime); 355 | var delay = Math.floor( e.buffer.duration * 1000) + 1; 356 | if (!e.loop) 357 | e.stopTimer = window.setTimeout( stopABSource, delay, playButton ); 358 | } 359 | } 360 | 361 | /* historic code, need to poach connection stuff 362 | function hitplay(e) { 363 | e = e.target.parentNode; // the node element, not the play button. 364 | 365 | // if there's already a note playing, cut it off. 366 | if (e.audioNode) 367 | e.audioNode.stop(0); 368 | 369 | //TODO: disconnect the audioNode before releasing it 370 | 371 | // create a new BufferSource, set it to the buffer and connect it. 372 | var n = e.audioNode = audioContext.createBufferSource(); 373 | n.loop = e.getElementsByTagName("input")[0].checked; 374 | n.gain.value = e.gain; 375 | 376 | if (e.outputConnections) { 377 | e.outputConnections.forEach(function(connection){ 378 | n.connect( connection.destination.audioNode ); }); 379 | } 380 | 381 | e.audioNode.buffer = e.buffer; 382 | e.audioNode.start(0); 383 | } 384 | 385 | function hitstop(e) { 386 | e.target.parentNode.audioNode.stop(0); 387 | e.target.parentNode.audioNode = null; 388 | } 389 | */ 390 | 391 | 392 | 393 | function createOscillator() { 394 | var osc = createNewModule( "oscillator", false, true ); 395 | addModuleSlider( osc, "frequency", 440, 0, 8000, 1, "Hz", onUpdateOscillatorFrequency ); 396 | addModuleSlider( osc, "detune", 0, -1200, 1200, 1, "cents", onUpdateDetune ); 397 | 398 | var play = document.createElement("img"); 399 | play.src = "img/ico-play.gif"; 400 | play.style.marginTop = "10px"; 401 | play.alt = "play"; 402 | play.onclick = onPlayOscillator; 403 | osc.appendChild( play ); 404 | 405 | osc = osc.parentNode; 406 | osc.className += " has-footer"; 407 | 408 | // Add footer element 409 | var footer = document.createElement("footer"); 410 | var sel = document.createElement("select"); 411 | sel.className = "osc-type"; 412 | var opt = document.createElement("option"); 413 | opt.appendChild( document.createTextNode("sine")); 414 | sel.appendChild( opt ); 415 | opt = document.createElement("option"); 416 | opt.appendChild( document.createTextNode("square")); 417 | sel.appendChild( opt ); 418 | opt = document.createElement("option"); 419 | opt.appendChild( document.createTextNode("sawtooth")); 420 | sel.appendChild( opt ); 421 | opt = document.createElement("option"); 422 | opt.appendChild( document.createTextNode("triangle")); 423 | sel.appendChild( opt ); 424 | opt = document.createElement("option"); 425 | opt.appendChild( document.createTextNode("wavetable")); 426 | sel.onchange = switchOscillatorTypes; 427 | sel.appendChild( opt ); 428 | footer.appendChild( sel ); 429 | osc.appendChild( footer ); 430 | 431 | // Cache default values on node element 432 | osc.oscillatorFrequency = 440; 433 | osc.oscillatorDetune = 0; 434 | osc.oscillatorType = "sine"; // SINE 435 | 436 | if (this.event) 437 | this.event.preventDefault(); 438 | } 439 | 440 | function createGain() { 441 | var module = createNewModule( "gain", true, true ); 442 | addModuleSlider( module, "gain", 1.0, 0.0, 10.0, 0.01, "", onUpdateGain ); 443 | 444 | // after adding sliders, walk up to the module to store the audioNode. 445 | module = module.parentNode; 446 | 447 | var gainNode = audioContext.createGain(); 448 | gainNode.gain.value = 1.0; 449 | module.audioNode = gainNode; 450 | 451 | if (this.event) 452 | this.event.preventDefault(); 453 | } 454 | 455 | function gotStream(stream) { 456 | // Create an AudioNode from the stream. 457 | // realAudioInput = audioContext.createMediaStreamSource(stream); 458 | this.audioNode = audioContext.createMediaStreamSource(stream); 459 | } 460 | 461 | function createLiveInput() { 462 | var module = createNewModule( "live input", false, true ); 463 | 464 | // after adding sliders, walk up to the module to store the audioNode. 465 | module = module.parentNode; 466 | var err = function(e) { 467 | alert('Error getting audio'); 468 | console.log(e); 469 | }; 470 | 471 | navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; 472 | if (navigator.getUserMedia ) 473 | navigator.getUserMedia( 474 | { 475 | "audio": { 476 | "mandatory": { 477 | "googEchoCancellation": "false", 478 | "googAutoGainControl": "false", 479 | "googNoiseSuppression": "false", 480 | "googHighpassFilter": "false" 481 | }, 482 | "optional": [] 483 | }, 484 | }, gotStream.bind(module), err ); 485 | else 486 | return(alert("Error: getUserMedia not supported!")); 487 | 488 | if (this.event) 489 | this.event.preventDefault(); 490 | } 491 | 492 | function createDelay() { 493 | var module = createNewModule( "delay", true, true ); 494 | addModuleSlider( module, "delay time", 0.2, 0.0, 10.0, 0.01, "sec", onUpdateDelay ); 495 | 496 | // after adding sliders, walk up to the module to store the audioNode. 497 | module = module.parentNode; 498 | 499 | var delayNode = audioContext.createDelay(); 500 | delayNode.delayTime.value = 0.2; 501 | module.audioNode = delayNode; 502 | 503 | if (this.event) 504 | this.event.preventDefault(); 505 | } 506 | 507 | function createAudioBufferSourceFromMenu(event) { 508 | createAudioBufferSource(null); 509 | } 510 | 511 | function createAudioBufferSource( buffer ) { 512 | var module = createNewModule( "audiobuffersource", false, true ); 513 | 514 | var play = document.createElement("img"); 515 | play.src = "img/ico-play.gif"; 516 | play.style.marginTop = "10px"; 517 | play.alt = "play"; 518 | play.onclick = onPlayABSource; 519 | module.appendChild( play ); 520 | 521 | module = module.parentNode; 522 | module.className += " has-footer has-loop"; 523 | 524 | // Add footer element 525 | var footer = document.createElement("footer"); 526 | 527 | var looper = document.createElement("div"); 528 | looper.className = "loop"; 529 | var label = document.createElement("label"); 530 | var check = document.createElement("input"); 531 | check.type = "checkbox"; 532 | check.onchange = onToggleLoop; 533 | label.appendChild(check); 534 | label.appendChild(document.createTextNode(" Loop")); 535 | looper.appendChild(label); 536 | footer.appendChild(looper); 537 | 538 | var sel = document.createElement("select"); 539 | sel.className = "ab-source"; 540 | var opt = document.createElement("option"); 541 | 542 | if (buffer) { // TODO: NASTY HACK! USING A GLOBAL! Should also add dropped files for all buffers. 543 | opt.appendChild( document.createTextNode( lastBufferLoaded )); 544 | sel.appendChild( opt ); 545 | opt = document.createElement("option"); 546 | buffers.splice(0,0,buffer); 547 | module.buffer = buffer; 548 | } 549 | 550 | opt.appendChild( document.createTextNode("glass-hit.ogg")); 551 | sel.appendChild( opt ); 552 | opt = document.createElement("option"); 553 | opt.appendChild( document.createTextNode("drums.ogg")); 554 | sel.appendChild( opt ); 555 | opt = document.createElement("option"); 556 | opt.appendChild( document.createTextNode("noise.ogg")); 557 | sel.appendChild( opt ); 558 | opt = document.createElement("option"); 559 | opt.appendChild( document.createTextNode("voice.ogg")); 560 | sel.appendChild( opt ); 561 | opt = document.createElement("option"); 562 | opt.appendChild( document.createTextNode("bass.ogg")); 563 | sel.appendChild( opt ); 564 | opt = document.createElement("option"); 565 | opt.appendChild( document.createTextNode("guitar.ogg")); 566 | sel.appendChild( opt ); 567 | opt = document.createElement("option"); 568 | opt.appendChild( document.createTextNode(" add new...")); 569 | sel.appendChild( opt ); 570 | 571 | sel.onchange = switchAudioBuffer; 572 | footer.appendChild( sel ); 573 | module.appendChild( footer ); 574 | 575 | // Add select element and type options 576 | module.buffer = buffer ? buffer : glassBuffer; 577 | 578 | if (this.event) 579 | this.event.preventDefault(); 580 | return module; 581 | } 582 | 583 | 584 | function createDynamicsCompressor() { 585 | var module = createNewModule( "dynamicscompressor", true, true ); 586 | addModuleSlider( module, "threshold", -24.0, -36.0, 0.0, 0.01, "Db", onUpdateThreshold ); 587 | addModuleSlider( module, "knee", 30.0, 0.0, 40.0, 0.01, "Db", onUpdateKnee ); 588 | addModuleSlider( module, "ratio", 12.0, 1.0, 50.0, 0.1, "", onUpdateRatio ); 589 | addModuleSlider( module, "attack", 0.003, 0.0, 1.0, 0.001, "sec", onUpdateAttack ); 590 | addModuleSlider( module, "release", 0.25, 0.0, 2.0, 0.01, "sec", onUpdateRelease ); 591 | 592 | // after adding sliders, walk up to the module to store the audioNode. 593 | module = module.parentNode; 594 | 595 | var audioNode = audioContext.createDynamicsCompressor(); 596 | audioNode.threshold.value = -24.0; 597 | audioNode.knee.value = 20.0; 598 | audioNode.ratio.value = 12.0; 599 | audioNode.attack.value = 0.003; 600 | audioNode.release.value = 0.25; 601 | module.audioNode = audioNode; 602 | if (this.event) 603 | this.event.preventDefault(); 604 | } 605 | 606 | function createConvolver() { 607 | var module = createNewModule( "convolver", true, true ); 608 | 609 | module = module.parentNode; 610 | module.className += " has-footer has-loop"; 611 | 612 | // Add footer element 613 | var footer = document.createElement("footer"); 614 | 615 | var looper = document.createElement("div"); 616 | looper.className = "loop"; 617 | var label = document.createElement("label"); 618 | var check = document.createElement("input"); 619 | check.type = "checkbox"; 620 | check.onchange = onToggleNormalize; 621 | label.appendChild(check); 622 | label.appendChild(document.createTextNode(" norm")); 623 | looper.appendChild(label); 624 | footer.appendChild(looper); 625 | 626 | var sel = document.createElement("select"); 627 | sel.className = "ab-source"; 628 | var opt = document.createElement("option"); 629 | opt.appendChild( document.createTextNode("irHall.ogg")); 630 | sel.appendChild( opt ); 631 | opt = document.createElement("option"); 632 | opt.appendChild( document.createTextNode("irRoom.ogg")); 633 | sel.appendChild( opt ); 634 | opt = document.createElement("option"); 635 | opt.appendChild( document.createTextNode("irParkingGarage.ogg")); 636 | sel.appendChild( opt ); 637 | footer.appendChild( sel ); 638 | module.appendChild( footer ); 639 | 640 | // Add select element and type options 641 | var audioNode = audioContext.createConvolver(); 642 | audioNode.buffer = irHallBuffer; 643 | module.audioNode = audioNode; 644 | if (this.event) 645 | this.event.preventDefault(); 646 | } 647 | 648 | function createAnalyser() { 649 | var module = createNewModule( "analyser", true, true ); 650 | 651 | var canvas = document.createElement( "canvas" ); 652 | canvas.height = "140"; 653 | canvas.width = "240"; 654 | canvas.className = "analyserCanvas"; 655 | canvas.style.webkitBoxReflect = "below 0px -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(0.9, transparent), to(white))" 656 | canvas.style.backgroundImage = "url('img/analyser-bg.png')"; 657 | module.appendChild( canvas ); 658 | 659 | // after adding sliders, walk up to the module to store the audioNode. 660 | module = module.parentNode; 661 | 662 | var audioNode = audioContext.createAnalyser(); 663 | module.audioNode = audioNode; 664 | 665 | audioNode.smoothingTimeConstant = "0.25"; // not much smoothing 666 | audioNode.fftSize = 512; 667 | audioNode.maxDecibels = 0; 668 | module.onConnectInput = onAnalyserConnectInput; 669 | analysers.push(module); // Add to the list of analysers in the animation loop 670 | module.drawingContext = canvas.getContext('2d'); 671 | 672 | if (this.event) 673 | this.event.preventDefault(); 674 | } 675 | 676 | function onAnalyserConnectInput() { 677 | // set up 678 | if (!animationRunning) { 679 | animationRunning = true; 680 | updateAnalysers( 0 ); 681 | } 682 | } 683 | 684 | function createBiquadFilter() { 685 | var module = createNewModule( "biquadfilter", true, true ); 686 | addModuleSlider( module, "frequency", 440, 0, 20000, 1, "Hz", onUpdateFrequency ); 687 | addModuleSlider( module, "Q", 1, 1, 100, 0.1, "", onUpdateQ ); 688 | var gainSlider = addModuleSlider( module, "gain", 1.0, 0.0, 10.0, 0.01, "", onUpdateGain ); 689 | module.children[3].classList.add("disabled"); 690 | // gainSlider.classList.add("disabled"); 691 | 692 | module = module.parentNode; 693 | module.className += " has-footer"; 694 | 695 | // cache the gain slider for later use 696 | module.gainSlider = gainSlider; 697 | 698 | // Add footer element 699 | var footer = document.createElement("footer"); 700 | var sel = document.createElement("select"); 701 | sel.className = "filter-type"; 702 | var opt = document.createElement("option"); 703 | opt.appendChild( document.createTextNode("lowpass")); 704 | sel.appendChild( opt ); 705 | opt = document.createElement("option"); 706 | opt.appendChild( document.createTextNode("highpass")); 707 | sel.appendChild( opt ); 708 | opt = document.createElement("option"); 709 | opt.appendChild( document.createTextNode("bandpass")); 710 | sel.appendChild( opt ); 711 | opt = document.createElement("option"); 712 | opt.appendChild( document.createTextNode("lowshelf")); 713 | sel.appendChild( opt ); 714 | opt = document.createElement("option"); 715 | opt.appendChild( document.createTextNode("highshelf")); 716 | sel.appendChild( opt ); 717 | opt = document.createElement("option"); 718 | opt.appendChild( document.createTextNode("peaking")); 719 | sel.appendChild( opt ); 720 | opt = document.createElement("option"); 721 | opt.appendChild( document.createTextNode("notch")); 722 | sel.appendChild( opt ); 723 | opt = document.createElement("option"); 724 | opt.appendChild( document.createTextNode("allpass")); 725 | sel.appendChild( opt ); 726 | sel.onchange = switchFilterTypes; 727 | footer.appendChild( sel ); 728 | module.appendChild( footer ); 729 | 730 | // Add select element and type options 731 | var audioNode = audioContext.createBiquadFilter(); 732 | audioNode.frequency.value = 440.0; 733 | audioNode.Q.value = 1.0; 734 | audioNode.gain.value = 1.0; 735 | module.audioNode = audioNode; 736 | 737 | if (this.event) 738 | this.event.preventDefault(); 739 | } 740 | 741 | function deleteModule() { 742 | var moduleElement = this.parentNode; 743 | 744 | // First disconnect the audio 745 | disconnectNode( moduleElement ); 746 | // Then delete the visual element 747 | moduleElement.parentNode.removeChild( moduleElement ); 748 | } 749 | 750 | function downloadAudioFromURL( url ){ 751 | var request = new XMLHttpRequest(); 752 | request.open('GET', url, true); 753 | request.responseType = 'arraybuffer'; 754 | 755 | lastBufferLoaded = url; // TODO: get last bit after the last / 756 | 757 | // Decode asynchronously 758 | request.onload = function() { 759 | audioContext.decodeAudioData( request.response, function(buffer) { 760 | createAudioNodeFromBuffer(buffer); 761 | }, function(){alert("error loading!");}); 762 | } 763 | request.send(); 764 | } 765 | 766 | function downloadImpulseFromURL( url ){ 767 | var request = new XMLHttpRequest(); 768 | request.open('GET', url, true); 769 | request.responseType = 'arraybuffer'; 770 | 771 | // Decode asynchronously 772 | request.onload = function() { 773 | audioContext.decodeAudioData( request.response, function(buffer) { 774 | createConvolverNodeFromBuffer(buffer); 775 | }, function(){alert("error loading!");}); 776 | } 777 | request.send(); 778 | } 779 | 780 | // Set up the page as a drop site for audio files. When an audio file is 781 | // dropped on the page, it will be auto-loaded as an AudioBufferSourceNode. 782 | function initDragDropOfAudioFiles() { 783 | // TODO: might want this indicator back 784 | // window.ondragover = function () { this.className = 'hover'; return false; }; 785 | // window.ondragend = function () { this.className = ''; return false; }; 786 | window.ondrop = function (e) { 787 | this.className = ''; 788 | e.preventDefault(); 789 | 790 | var reader = new FileReader(); 791 | reader.onload = function (event) { 792 | audioContext.decodeAudioData( event.target.result, function(buffer) { 793 | createAudioBufferSource(buffer); 794 | }, function(){alert("error loading!");} ); 795 | 796 | }; 797 | reader.onerror = function (event) { 798 | alert("Error: " + reader.error ); 799 | }; 800 | lastBufferLoaded = e.dataTransfer.files[0].name; 801 | reader.readAsArrayBuffer(e.dataTransfer.files[0]); 802 | return false; 803 | }; 804 | } 805 | 806 | var drumsBuffer, 807 | bassBuffer, 808 | voiceBuffer, 809 | noiseBuffer, 810 | guitarBuffer, 811 | irHallBuffer, 812 | irDrumRoomBuffer, 813 | irParkingGarageBuffer; 814 | 815 | function startLoadingSounds() { 816 | var glassRequest = new XMLHttpRequest(); 817 | glassRequest.open("GET", "sounds/glass-hit.ogg", true); 818 | glassRequest.responseType = "arraybuffer"; 819 | glassRequest.onload = function() { 820 | audioContext.decodeAudioData( glassRequest.response, function(buffer) { 821 | glassBuffer = buffer; 822 | buffers[0]= glassBuffer; 823 | } ); 824 | } 825 | glassRequest.send(); 826 | 827 | drumRequest = new XMLHttpRequest(); 828 | drumRequest.open("GET", "sounds/drums.ogg", true); 829 | drumRequest.responseType = "arraybuffer"; 830 | drumRequest.onload = function() { 831 | audioContext.decodeAudioData( drumRequest.response, function(buffer) { 832 | drumsBuffer = buffer; 833 | buffers[1]= drumsBuffer; 834 | } ); 835 | } 836 | drumRequest.send(); 837 | 838 | 839 | noiseRequest = new XMLHttpRequest(); 840 | noiseRequest.open("GET", "sounds/noise.ogg", true); 841 | noiseRequest.responseType = "arraybuffer"; 842 | noiseRequest.onload = function() { 843 | audioContext.decodeAudioData( noiseRequest.response, function(buffer) { 844 | noiseBuffer = buffer; 845 | buffers[2]= noiseBuffer; 846 | } ); 847 | } 848 | noiseRequest.send(); 849 | 850 | voiceRequest = new XMLHttpRequest(); 851 | voiceRequest.open("GET", "sounds/voice.ogg", true); 852 | voiceRequest.responseType = "arraybuffer"; 853 | voiceRequest.onload = function() { 854 | audioContext.decodeAudioData( voiceRequest.response, function(buffer) { 855 | voiceBuffer = buffer; 856 | buffers[3]= voiceBuffer; 857 | } ); 858 | } 859 | voiceRequest.send(); 860 | 861 | bassRequest = new XMLHttpRequest(); 862 | bassRequest.open("GET", "sounds/bass.ogg", true); 863 | bassRequest.responseType = "arraybuffer"; 864 | bassRequest.onload = function() { 865 | audioContext.decodeAudioData( bassRequest.response, function(buffer) { 866 | bassBuffer = buffer; 867 | buffers[4]= bassBuffer; 868 | } ); 869 | } 870 | bassRequest.send(); 871 | 872 | guitarRequest = new XMLHttpRequest(); 873 | guitarRequest.open("GET", "sounds/guitar.ogg", true); 874 | guitarRequest.responseType = "arraybuffer"; 875 | guitarRequest.onload = function() { 876 | audioContext.decodeAudioData( guitarRequest.response, function(buffer) { 877 | guitarBuffer = buffer; 878 | buffers[5]= guitarBuffer; 879 | } ); 880 | } 881 | guitarRequest.send(); 882 | 883 | 884 | var irHallRequest = new XMLHttpRequest(); 885 | irHallRequest.open("GET", "sounds/irHall.ogg", true); 886 | irHallRequest.responseType = "arraybuffer"; 887 | irHallRequest.onload = function() { 888 | audioContext.decodeAudioData( irHallRequest.response, function(buffer) { 889 | irHallBuffer = buffer; } ); 890 | } 891 | irHallRequest.send(); 892 | } 893 | 894 | function setClickHandler( id, handler ) { 895 | var el = document.getElementById( id ); 896 | if (el) { 897 | el.addEventListener( "mousedown", handler, true ); 898 | // el.addEventListener( "pointerdown", handler, false ); 899 | } 900 | } 901 | 902 | // Initialization function for the page. 903 | function init() { 904 | try { 905 | audioContext = new AudioContext(); 906 | } catch(e) { 907 | alert('The Web Audio API is apparently not supported in this browser.'); 908 | } 909 | 910 | initDragDropOfAudioFiles(); // set up page as a drop site for audio files 911 | 912 | startLoadingSounds(); 913 | 914 | // create the one-and-only destination node for the context 915 | var dest = document.getElementById("output"); 916 | dest.audioNode = audioContext.destination; 917 | // stringifyAudio(); 918 | 919 | setClickHandler( "cabs", createAudioBufferSourceFromMenu ); 920 | setClickHandler( "cosc", createOscillator ); 921 | setClickHandler( "cliv", createLiveInput ); 922 | setClickHandler( "cbqf", createBiquadFilter ); 923 | setClickHandler( "cdel", createDelay ); 924 | setClickHandler( "cdyc", createDynamicsCompressor ); 925 | setClickHandler( "cgai", createGain ); 926 | setClickHandler( "ccon", createConvolver ); 927 | setClickHandler( "cana", createAnalyser ); 928 | 929 | // if (navigator.userAgent.indexOf("Android") != -1) 930 | // document.body.style.zoom = "2"; 931 | } 932 | 933 | window.addEventListener('load', init, false); 934 | -------------------------------------------------------------------------------- /js/pointerevents.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright 2012 The Toolkitchen Authors. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | function PointerEvent(e,t){var n=document.createEvent("MouseEvent");return n.__proto__=PointerEvent.prototype,n.initPointerEvent(e,t),n}PointerEvent.prototype.__proto__=MouseEvent.prototype,PointerEvent.prototype.initPointerEvent=function(e,t){var n={bubbles:!1,cancelable:!1,view:null,detail:null,screenX:0,screenY:0,clientX:0,clientY:0,ctrlKey:!1,altKey:!1,shiftKey:!1,metaKey:!1,button:-1,buttons:null,which:0,relatedTarget:null,pointerId:-1,width:0,height:0,pressure:0,tiltX:0,tiltY:0,pointerType:"unavailable",hwTimestamp:0,isPrimary:!1};for(var r in t)r in n&&(n[r]=t[r]);var i;n.buttons!==null?i=n.buttons?n.button:-1:i=n.which?n.button:-1,Object.defineProperties(this,{pointerId:{value:n.pointerId,enumerable:!0},width:{value:n.width,enumerable:!0},height:{value:n.height,enumerable:!0},pressure:{value:n.pressure,enumerable:!0},tiltX:{value:n.tiltX,enumerable:!0},tiltY:{value:n.tiltY,enumerable:!0},pointerType:{value:n.pointerType,enumerable:!0},hwTimestamp:{value:n.hwTimestamp,enumerable:!0},isPrimary:{value:n.isPrimary,enumerable:!0}}),this.initMouseEvent(e,n.bubbles,n.cancelable,n.view,n.detail,n.screenX,n.screenY,n.clientX,n.clientY,n.ctrlKey,n.altKey,n.shiftKey,n.metaKey,i,n.relatedTarget)};var SideTable;typeof WeakMap!="undefined"?SideTable=WeakMap:(SideTable=function(e){this.name="__$"+e+"$__"},SideTable.prototype={set:function(e,t){Object.defineProperty(e,this.name,{value:t,writable:!0})},get:function(e){return e[this.name]}}),function(e){e=e||{},Function.prototype.bind||(Function.prototype.bind=function(t){var n=e.toArray(arguments,1),r=this;return function(){var i=e.toArray(arguments,0);return r.apply(t,n.concat(i))}}),e.toArray=function(e,t){return Array.prototype.slice.call(e,t||0)},window.__PointerEventShim__=e}(window.__PointerEventShim__),function(e){var t={pointers:[],addPointer:function(e){var t={id:e};return this.pointers.push(t),t},removePointer:function(e){var t=this.getPointerIndex(e);if(t>-1)return this.pointers.splice(t,1)[0]},getPointerById:function(e){return this.pointers[this.getPointerIndex(e)]},getPointerIndex:function(e){for(var t=0,n=this.pointers.length,r;t