├── 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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
60 |
61 |
62 |
63 |
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