├── .DS_Store
├── LICENSE.txt
├── README.md
├── app.yaml
├── audio
├── .DS_Store
├── gettysburg.ogg
└── junky.ogg
├── css
├── main.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
├── .DS_Store
├── analyser-bg.png
├── body-bkg.gif
├── forkme.png
├── ico-play.png
├── ico-preview.gif
├── ico-stop.png
├── logo copy.png
├── logo.png
├── mic128.png
├── mic16.png
├── play.png
├── results-bkg.gif
├── results-bottom.png
├── results-middle.png
├── results-top.png
├── slider-bkg.gif
└── slider-handle.png
├── index.html
├── js
├── .DS_Store
├── WebMIDIAPI.js
├── app.js
├── jquery-1.7.2.min.js
├── jquery-ui-1.8.21.custom.min.js
├── main.js
├── midi.js
├── recorderjs
│ ├── recorder.js
│ └── recorderWorker.js
├── sliders.js
├── ui.js
├── visualizer
│ ├── .DS_Store
│ ├── base.js
│ ├── cameracontroller.js
│ ├── events.js
│ ├── matrix4x4.js
│ ├── shader.js
│ └── visualizer.js
├── vocoder.js
└── waveshaper.js
├── manifest.json
└── shaders
├── .DS_Store
├── common-vertex.shader
├── frequency-fragment.shader
├── sonogram-fragment.shader
├── sonogram-vertex.shader
├── texture-only.shader
└── waveform-fragment.shader
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/.DS_Store
--------------------------------------------------------------------------------
/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 Vocoder
2 |
3 | This application (also shown at I/O 2012) implements a 28-band (actually variable number of bands) vocoder - a "robotic voice" processor. It's a pretty complex audio processing demo. It also supports live input, and has several controls exposed; it supports MIDI control over the pitch and other parameters.
4 |
5 | Check it out, feel free to submit issues or requests, fork, submit pull requests, etc.
6 |
7 | The live app is at https://cwilso.github.io/Vocoder/.
8 |
9 | -Chris
10 |
--------------------------------------------------------------------------------
/app.yaml:
--------------------------------------------------------------------------------
1 | application: webaudiovocoder
2 | version: 1
3 | runtime: python
4 | api_version: 1
5 | threadsafe: yes
6 |
7 | handlers:
8 | - url: /
9 | static_files: index.html
10 | upload: index.html
11 |
12 | - url: /vocoder.appcache
13 | static_files: vocoder.appcache
14 | upload: vocoder.appcache
15 |
16 | - url: /audio
17 | static_dir: audio
18 |
19 | - url: /css
20 | static_dir: css
21 |
22 | - url: /fonts
23 | static_dir: fonts
24 |
25 | - url: /img
26 | static_dir: img
27 |
28 | - url: /favicon.ico
29 | static_files: favicon.ico
30 | upload: favicon.ico
31 |
32 | - url: /js
33 | static_dir: js
34 |
35 | - url: /lib
36 | static_dir: lib
37 |
38 | - url: /shaders
39 | static_dir: shaders
40 |
41 |
42 |
--------------------------------------------------------------------------------
/audio/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/audio/.DS_Store
--------------------------------------------------------------------------------
/audio/gettysburg.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/audio/gettysburg.ogg
--------------------------------------------------------------------------------
/audio/junky.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/audio/junky.ogg
--------------------------------------------------------------------------------
/css/main.css:
--------------------------------------------------------------------------------
1 | /* main.css */
2 |
3 | /* ---------------------------------------------------------------------------
4 | * :: Type
5 | *
6 | * - Fonts
7 | * - Base
8 | * - Links
9 | * -------------------------------------------------------------------------*/
10 |
11 | .notready h6 {
12 | background: red;
13 | }
14 |
15 | div.link {
16 | font-style: italic;
17 | color: lightgrey;
18 | margin-left: 780px;
19 | margin-top: 5px;
20 | }
21 |
22 | #mobile { display: none; margin-left: 1em; color: lightgrey;}
23 |
24 | div.link a {
25 | color: white;
26 | font: inherit;
27 | text-decoration: underline;
28 | }
29 |
30 | /* Fonts */
31 | @font-face {
32 | font-family: 'ProximaNovaRgRegular';
33 | src: url('../fonts/proxima_nova_reg-webfont.eot');
34 | src: url('../fonts/proxima_nova_reg-webfont.eot?#iefix') format('embedded-opentype'),
35 | url('../fonts/proxima_nova_reg-webfont.woff') format('woff'),
36 | url('../fonts/proxima_nova_reg-webfont.ttf') format('truetype'),
37 | url('../fonts/proxima_nova_reg-webfont.svg#ProximaNovaRgRegular') format('svg');
38 | font-weight: normal;
39 | font-style: normal;
40 | }
41 |
42 | /* Base */
43 | html,
44 | button,
45 | input,
46 | select,
47 | textarea {
48 | font-family: 'ProximaNovaRgRegular', 'Helvetica Neue', Helvetica, Arial, sans-serif;
49 | color: #4c4c4c;
50 | }
51 | body {
52 | font-size: 1em;
53 | line-height: 1.4;
54 | }
55 |
56 | /* Links */
57 | a,
58 | a:visited {
59 | color: #4c4c4c;
60 | font-size: .6875em; /* 11 / 16 */
61 | text-decoration: none;
62 | }
63 | a:hover {
64 | color: #000;
65 | text-decoration: none;
66 | }
67 |
68 |
69 | /* ---------------------------------------------------------------------------
70 | * :: Raw elements
71 | * -------------------------------------------------------------------------*/
72 | body {
73 | background: url('../img/body-bkg.gif');
74 | }
75 |
76 |
77 | /* ---------------------------------------------------------------------------
78 | * :: Main
79 | *
80 | * - Header
81 | * -------------------------------------------------------------------------*/
82 | #main {
83 | position: relative;
84 | z-index: 1;
85 | }
86 |
87 | /* Header */
88 | #header {
89 | margin: 0;
90 | padding: 1em 0 0 1em;
91 | }
92 |
93 | #footer { margin: 0; padding: 0;}
94 |
95 | /* ---------------------------------------------------------------------------
96 | * :: Modules
97 | *
98 | * - Controls
99 | * - Dropdown
100 | * - Dropdown arrow
101 | * - Modules
102 | * -------------------------------------------------------------------------*/
103 | #modules {
104 | display: inline;
105 | float: left;
106 | margin: 0 0 0 18px;
107 | padding: 20px 0 0;
108 | width: 200px;
109 | }
110 |
111 | /* Module */
112 | .module {
113 | border-radius: 5px;
114 | box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
115 | display: inline-block;
116 | position: relative;
117 | }
118 | .module .content {
119 | background: #fff;
120 | border-radius: 5px;
121 | min-width: 10em;
122 | overflow: hidden;
123 | padding: 1.25em;
124 | position: relative;
125 | }
126 | .module.has-footer .content {
127 | border-bottom: 1px solid #dbdbdb;
128 | border-radius: 5px 5px 0 0;
129 | }
130 | .module h6 {
131 | font-size: 1.375em;
132 | font-weight: normal;
133 | margin: 0 0 .2em;
134 | text-align: center;
135 | text-transform: uppercase;
136 | }
137 | .module.audio-buffer-source .ico-play,
138 | .module.audio-buffer-source .ico-stop {
139 | display: none;
140 | }
141 | .module.audio-buffer-source.play .ico-play {
142 | display: block;
143 | }
144 | .module.audio-buffer-source.stop .ico-stop {
145 | display: block;
146 | }
147 |
148 | /* Module footer */
149 | .module footer {
150 | background-color: #fff;
151 | background-image: -webkit-linear-gradient(top, rgb(255, 255, 255), rgb(219, 219, 219));
152 | background-image: linear-gradient(top, rgb(255, 255, 255), rgb(219, 219, 219));
153 | border-radius: 0 0 5px 5px;
154 | overflow: hidden;
155 | padding: 0;
156 | }
157 | .module footer a {
158 | display: inline;
159 | float: left;
160 | padding: .75em 0;
161 | text-align: center;
162 | text-transform: uppercase;
163 | width: 100px;
164 | }
165 | .module footer a:hover,
166 | .module footer a.active {
167 | background: #d4d4d4;
168 | box-shadow: inset 1px 1px 1px rgba(0,0,0,.2);
169 | }
170 | .module footer a.left {
171 | border-right: 1px solid #dbdbdb;
172 | width: 99px;
173 | }
174 | .module footer a.top {
175 | border-bottom: 1px solid #dbdbdb;
176 | }
177 |
178 | /* Control group */
179 | .control-group {
180 | margin: 0 0 1em;
181 | }
182 | .control-group.last {
183 | margin: 0;
184 | }
185 |
186 | /* Slider */
187 | .slider-info {
188 | font-size: .625em; /* 10 / 16 */
189 | margin-bottom: 0 0 1em;
190 | overflow: hidden;
191 | padding-bottom: .4em;
192 | position: relative;
193 | }
194 | .slider-info .label {
195 | display: inline;
196 | float: left;
197 | text-transform: uppercase;
198 | width: 49%;
199 | }
200 | .slider-info .value {
201 | display: inline;
202 | float: right;
203 | font-weight: bold;
204 | text-align: right;
205 | text-transform: uppercase;
206 | width: 49%;
207 | }
208 | .module .slider-info:last-child {
209 | margin-bottom: 0;
210 | }
211 |
212 | .ui-slider {
213 | background: url('../img/slider-bkg.gif') 0 0 repeat-x;
214 | border: 0 none;
215 | color: #fff;
216 | height: 11px;
217 | position: relative;
218 | text-align: left;
219 | }
220 | .ui-state-default,
221 | .ui-widget-content .ui-state-default,
222 | .ui-widget-header .ui-state-default {
223 | border: 1px solid #cccccc;
224 | background: #f6f6f6;
225 | font-weight: bold;
226 | color: #1c94c4;
227 | }
228 | .ui-slider .ui-slider-handle {
229 | background: url('../img/slider-handle.png') 0 1px no-repeat;
230 | border: 0 none;
231 | cursor: default;
232 | height: 18px;
233 | margin-left: -.6em;
234 | position: absolute;
235 | top: -.3em;
236 | width: 16px;
237 | z-index: 2;
238 | }
239 | .ui-slider .ui-slider-range {
240 | background-position: 0 0;
241 | border: 0;
242 | display: block;
243 | font-size: .7em;
244 | height: 100%;
245 | position: absolute;
246 | top: 0;
247 | z-index: 1;
248 | }
249 | .ui-slider .ui-slider-range-min {
250 | left: 0;
251 | }
252 | .ui-slider .ui-slider-range-max {
253 | right: 0;
254 | }
255 |
256 | /* Preview link */
257 | .preview-link {
258 | color: #c2c2c2;
259 | display: block;
260 | padding: 2em 0 0;
261 | text-align: center;
262 | text-transform: uppercase;
263 | }
264 | .preview-link img {
265 | display: inline-block;
266 | width: 9px;
267 | height: 11px;
268 | line-height: 11px;
269 | margin-right: 3px;
270 | vertical-align: text-top;
271 | }
272 |
273 | /* Module close */
274 | .module .preview {
275 | height: 11px;
276 | position: absolute;
277 | right: 8px;
278 | top: 8px;
279 | width: 11px;
280 | }
281 | .module .preview:hover {
282 | opacity: .8
283 | }
284 |
285 |
286 | /* Specific modules */
287 | .module.modulator {
288 | margin-bottom: 1.875em;
289 | }
290 |
291 | /* ---------------------------------------------------------------------------
292 | * :: Connection
293 | * -------------------------------------------------------------------------*/
294 | #connection {
295 | display: inline;
296 | float: left;
297 | padding-top: 87px;
298 | width: 152px;
299 | }
300 | #play {
301 | background: url('../img/play.png') 0 0 no-repeat;
302 | display: block;
303 | height: 299px;
304 | text-indent: -999em;
305 | width: 152px;
306 | }
307 | #play:hover {
308 | background-position: -152px 0;
309 | }
310 |
311 | #record {
312 | width: 64px;
313 | height: 64px;
314 | margin: 20px auto 10px;
315 | display: block;
316 | }
317 |
318 | #record.recording {
319 | abackground: red;
320 | background: -webkit-radial-gradient(center, ellipse cover, #ff0000 0%,transparent 75%,transparent 100%,transparent 100%);
321 | background: -moz-radial-gradient(center, ellipse cover, #ff0000 0%,transparent 75%,transparent 100%,transparent 100%);
322 | background: radial-gradient(center, ellipse cover, #ff0000 0%,transparent 75%,transparent 100%,transparent 100%);
323 | }
324 |
325 | #recfile {
326 | color: lightblue;
327 | display: block;
328 | text-align: center;
329 | }
330 |
331 | /* ---------------------------------------------------------------------------
332 | * :: Results
333 | * -------------------------------------------------------------------------*/
334 | #results {
335 | display: inline;
336 | float: left;
337 | width: 590px;
338 | position: relative;
339 | margin-top:15px;
340 | }
341 | #results h2 {
342 | color: #fff;
343 | font-size: 1.375em;
344 | font-weight: normal;
345 | margin: 0;
346 | text-align: center;
347 | text-transform: uppercase;
348 | position: absolute;
349 | right: 10px;
350 | }
351 | #result-top,
352 | #result-middle {
353 | margin: 0 0 1.875em;
354 | }
355 | #result-top,
356 | #result-middle,
357 | #result-bottom {
358 | box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
359 | background-image: url('../img/analyser-bg.png');
360 | border-radius: 8px;
361 | overflow: hidden;
362 | }
363 |
364 | #result-top canvas,
365 | #result-middle canvas,
366 | #result-bottom canvas {
367 | width:582px; margin-left: 4px; margin-top: 4px;
368 | margin-bottom: -2px;
369 | }
370 |
371 | #modulator.droptarget, #carrier.droptarget { outline: 1px solid red; opacity: 0.5;}
372 |
373 | /* ---------------------------------------------------------------------------
374 | * :: Global classes and styles
375 | *
376 | * - Clearfix
377 | * -------------------------------------------------------------------------*/
378 |
379 | /* Clearfix */
380 | .container:before, .container:after { content: ""; display: table; }
381 | .container:after { clear: both; }
382 | .container { *zoom: 1; }
383 |
--------------------------------------------------------------------------------
/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/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/favicon.ico
--------------------------------------------------------------------------------
/fonts/proxima_nova_reg-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/fonts/proxima_nova_reg-webfont.eot
--------------------------------------------------------------------------------
/fonts/proxima_nova_reg-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/fonts/proxima_nova_reg-webfont.ttf
--------------------------------------------------------------------------------
/fonts/proxima_nova_reg-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/fonts/proxima_nova_reg-webfont.woff
--------------------------------------------------------------------------------
/img/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/img/.DS_Store
--------------------------------------------------------------------------------
/img/analyser-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/img/analyser-bg.png
--------------------------------------------------------------------------------
/img/body-bkg.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/img/body-bkg.gif
--------------------------------------------------------------------------------
/img/forkme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/img/forkme.png
--------------------------------------------------------------------------------
/img/ico-play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/img/ico-play.png
--------------------------------------------------------------------------------
/img/ico-preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/img/ico-preview.gif
--------------------------------------------------------------------------------
/img/ico-stop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/img/ico-stop.png
--------------------------------------------------------------------------------
/img/logo copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/img/logo copy.png
--------------------------------------------------------------------------------
/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/img/logo.png
--------------------------------------------------------------------------------
/img/mic128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/img/mic128.png
--------------------------------------------------------------------------------
/img/mic16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/img/mic16.png
--------------------------------------------------------------------------------
/img/play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/img/play.png
--------------------------------------------------------------------------------
/img/results-bkg.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/img/results-bkg.gif
--------------------------------------------------------------------------------
/img/results-bottom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/img/results-bottom.png
--------------------------------------------------------------------------------
/img/results-middle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/img/results-middle.png
--------------------------------------------------------------------------------
/img/results-top.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/img/results-top.png
--------------------------------------------------------------------------------
/img/slider-bkg.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/img/slider-bkg.gif
--------------------------------------------------------------------------------
/img/slider-handle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/img/slider-handle.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Vocoder
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 | // For recording the audio
35 |
36 |
37 |
38 |
39 |
40 |
44 |
45 |
46 |
62 |
101 |
102 |
103 |
Play
104 |

105 |
106 |
107 |
108 |
input
109 |
110 |
111 |
112 |
113 |
114 |
115 |
output
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
143 |
144 |
145 |
146 |
147 |
--------------------------------------------------------------------------------
/js/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/js/.DS_Store
--------------------------------------------------------------------------------
/js/WebMIDIAPI.js:
--------------------------------------------------------------------------------
1 | // Initialize the MIDI library.
2 | (function (global, exports, perf) {
3 | 'use strict';
4 | var midiIO,
5 | debug = false;
6 | if (debug) {
7 | window.console.warn('Debuggin enabled');
8 | }
9 |
10 | //init: create plugin
11 | if (!window.navigator.requestMIDIAccess) {
12 | window.navigator.requestMIDIAccess = _requestMIDIAccess;
13 | if (!window.navigator.getMIDIAccess)
14 | window.navigator.getMIDIAccess = _requestMIDIAccess;
15 | }
16 |
17 | function _JazzInstance() {
18 | this.inputInUse = false;
19 | this.outputInUse = false;
20 |
21 | // load the Jazz plugin
22 | var o1 = document.createElement("object");
23 | o1.id = "_Jazz" + Math.random() + "ie";
24 | o1.classid = "CLSID:1ACE1618-1C7D-4561-AEE1-34842AA85E90";
25 |
26 | this.activeX = o1;
27 |
28 | var o2 = document.createElement("object");
29 | o2.id = "_Jazz" + Math.random;
30 | o2.type="audio/x-jazz";
31 | o1.appendChild(o2);
32 |
33 | this.objRef = o2;
34 |
35 | var e = document.createElement("p");
36 | e.appendChild(document.createTextNode("This page requires the "));
37 |
38 | var a = document.createElement("a");
39 | a.appendChild(document.createTextNode("Jazz plugin"));
40 | a.href = "http://jazz-soft.net/";
41 |
42 | e.appendChild(a);
43 | e.appendChild(document.createTextNode("."));
44 | o2.appendChild(e);
45 |
46 | var insertionPoint = document.getElementById("MIDIPlugin");
47 | if (!insertionPoint)
48 | insertionPoint = document.body;
49 | insertionPoint.appendChild(o1);
50 |
51 | if (this.objRef.isJazz)
52 | this._Jazz = this.objRef;
53 | else if (this.activeX.isJazz)
54 | this._Jazz = this.activeX;
55 | else
56 | this._Jazz = null;
57 | if (this._Jazz) {
58 | this._Jazz._jazzTimeZero = this._Jazz.Time();
59 | this._Jazz._perfTimeZero = window.performance.now();
60 | }
61 | }
62 |
63 | function _requestMIDIAccess( successCallback, errorCallback ) {
64 | new MIDIAccess( successCallback, errorCallback );
65 | return;
66 | }
67 |
68 |
69 | // API Methods
70 |
71 | function MIDIAccess( successCallback, errorCallback ) {
72 | this._jazzInstances = new Array();
73 | this._jazzInstances.push( new _JazzInstance() );
74 |
75 | if (this._jazzInstances[0]._Jazz) {
76 | this._Jazz = this._jazzInstances[0]._Jazz;
77 | this._successCallback = successCallback;
78 | window.setTimeout( _onReady.bind(this), 3 );
79 | } else {
80 | if (errorCallback)
81 | errorCallback( { code: 1 } );
82 | }
83 | }
84 |
85 | function _onReady() {
86 | if (this._successCallback)
87 | this._successCallback( this );
88 | }
89 |
90 | MIDIAccess.prototype.enumerateInputs = function( ) {
91 | if (!this._Jazz)
92 | return null;
93 | var list=this._Jazz.MidiInList();
94 | var inputs = new Array( list.length );
95 |
96 | for ( var i=0; i1)) {
271 | var sendObj = new Object;
272 | sendObj.jazz = this._jazzInstance;
273 | sendObj.data = data;
274 |
275 | window.setTimeout( _sendLater.bind(sendObj), delayBeforeSend );
276 | } else {
277 | this._jazzInstance.MidiOutLong( data );
278 | }
279 | return true;
280 | }
281 |
282 | }(window));
283 |
284 | // Polyfill window.performance.now() if necessary.
285 | (function (exports) {
286 | var perf = {},
287 | props;
288 |
289 | function findAlt() {
290 | var prefix = "moz,webkit,opera,ms".split(","),
291 | i = prefix.length,
292 | //worst case, we use Date.now()
293 | props = {
294 | value: function (start) {
295 | return function () {
296 | return Date.now() - start;
297 | }
298 | }(Date.now())
299 | };
300 |
301 | //seach for vendor prefixed version
302 | for (; i >= 0; i--) {
303 | if ((prefix[i] + "Now") in exports.performance) {
304 | props.value = function (method) {
305 | return function () {
306 | exports.performance[method]();
307 | }
308 | }(prefix[i] + "Now");
309 | return props;
310 | }
311 | }
312 |
313 | //otherwise, try to use connectionStart
314 | if ("timing" in exports.performance &&
315 | "connectStart" in exports.performance.timing) {
316 | //this pretty much approximates performance.now() to the millisecond
317 | props.value = function (start) {
318 | return function(){Date.now() - start;}
319 | }(exports.performance.timing.connectStart);
320 | }
321 | return props;
322 | }
323 |
324 | //if already defined, bail
325 | if (("performance" in exports) && ("now" in exports.performance)) {
326 | return;
327 | }
328 | if (!("performance" in exports)) {
329 | Object.defineProperty(exports, "performance", {
330 | get: function () {
331 | return perf;
332 | }
333 | });
334 | //otherwise, perforance is there, but not "now()"
335 | }
336 | props = findAlt();
337 | Object.defineProperty(exports.performance, "now", props);
338 | }(window));
339 |
340 |
341 |
342 |
--------------------------------------------------------------------------------
/js/app.js:
--------------------------------------------------------------------------------
1 | chrome.app.runtime.onLaunched.addListener(function() {
2 | chrome.app.window.create('index.html', {
3 | width: 980,
4 | height: 550
5 | })
6 | })
--------------------------------------------------------------------------------
/js/jquery-ui-1.8.21.custom.min.js:
--------------------------------------------------------------------------------
1 | /*! jQuery UI - v1.8.21 - 2012-06-05
2 | * https://github.com/jquery/jquery-ui
3 | * Includes: jquery.ui.core.js
4 | * Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */
5 | (function(a,b){function c(b,c){var e=b.nodeName.toLowerCase();if("area"===e){var f=b.parentNode,g=f.name,h;return!b.href||!g||f.nodeName.toLowerCase()!=="map"?!1:(h=a("img[usemap=#"+g+"]")[0],!!h&&d(h))}return(/input|select|textarea|button|object/.test(e)?!b.disabled:"a"==e?b.href||c:c)&&d(b)}function d(b){return!a(b).parents().andSelf().filter(function(){return a.curCSS(this,"visibility")==="hidden"||a.expr.filters.hidden(this)}).length}a.ui=a.ui||{};if(a.ui.version)return;a.extend(a.ui,{version:"1.8.21",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}}),a.fn.extend({propAttr:a.fn.prop||a.fn.attr,_focus:a.fn.focus,focus:function(b,c){return typeof b=="number"?this.each(function(){var d=this;setTimeout(function(){a(d).focus(),c&&c.call(d)},b)}):this._focus.apply(this,arguments)},scrollParent:function(){var b;return a.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?b=this.parents().filter(function(){return/(relative|absolute|fixed)/.test(a.curCSS(this,"position",1))&&/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0):b=this.parents().filter(function(){return/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0),/fixed/.test(this.css("position"))||!b.length?a(document):b},zIndex:function(c){if(c!==b)return this.css("zIndex",c);if(this.length){var d=a(this[0]),e,f;while(d.length&&d[0]!==document){e=d.css("position");if(e==="absolute"||e==="relative"||e==="fixed"){f=parseInt(d.css("zIndex"),10);if(!isNaN(f)&&f!==0)return f}d=d.parent()}}return 0},disableSelection:function(){return this.bind((a.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),a.each(["Width","Height"],function(c,d){function h(b,c,d,f){return a.each(e,function(){c-=parseFloat(a.curCSS(b,"padding"+this,!0))||0,d&&(c-=parseFloat(a.curCSS(b,"border"+this+"Width",!0))||0),f&&(c-=parseFloat(a.curCSS(b,"margin"+this,!0))||0)}),c}var e=d==="Width"?["Left","Right"]:["Top","Bottom"],f=d.toLowerCase(),g={innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};a.fn["inner"+d]=function(c){return c===b?g["inner"+d].call(this):this.each(function(){a(this).css(f,h(this,c)+"px")})},a.fn["outer"+d]=function(b,c){return typeof b!="number"?g["outer"+d].call(this,b):this.each(function(){a(this).css(f,h(this,b,!0,c)+"px")})}}),a.extend(a.expr[":"],{data:function(b,c,d){return!!a.data(b,d[3])},focusable:function(b){return c(b,!isNaN(a.attr(b,"tabindex")))},tabbable:function(b){var d=a.attr(b,"tabindex"),e=isNaN(d);return(e||d>=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 | /*
2 | * Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 | *
4 | * Redistribution and use in source and binary forms, with or without
5 | * modification, are permitted provided that the following conditions are
6 | * met:
7 | *
8 | * * Redistributions of source code must retain the above copyright
9 | * notice, this list of conditions and the following disclaimer.
10 | * * Redistributions in binary form must reproduce the above
11 | * copyright notice, this list of conditions and the following disclaimer
12 | * in the documentation and/or other materials provided with the
13 | * distribution.
14 | * * Neither the name of Google Inc. nor the names of its
15 | * contributors may be used to endorse or promote products derived from
16 | * this software without specific prior written permission.
17 | *
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | */
30 |
31 | var audioContext = null;
32 | var modulatorBuffer = null;
33 | var carrierBuffer = null;
34 | var modulatorNode = null;
35 | var carrierNode = null;
36 | var vocoding = false;
37 | var cheapAnalysis = true;
38 |
39 | // Debug visualizer stuff here
40 | var analyser1;
41 | var analyser2;
42 | var analyserView1;
43 | var analyserView2;
44 |
45 | //constants for carrier buttons
46 | var FILE = 0, SAWTOOTH=1, WAVETABLE=2, FILENAME=-1;
47 |
48 | cheapAnalysis = (navigator.userAgent.indexOf("Android")!=-1)||(navigator.userAgent.indexOf("iPad")!=-1)||(navigator.userAgent.indexOf("iPhone")!=-1);;
49 |
50 | if (!cheapAnalysis)
51 | o3djs.require('o3djs.shader');
52 | else
53 | document.write("")
54 |
55 |
56 | var carrierAnalyserNode = null;
57 |
58 | function previewCarrier() {
59 | if (this.event)
60 | this.event.preventDefault();
61 |
62 | var carrierPreviewImg = document.getElementById("carrierpreview");
63 | if (carrierPreviewImg.classList.contains("playing") ) {
64 | finishPreviewingCarrier();
65 | return;
66 | }
67 |
68 | if (vocoding)
69 | vocode(); // this will shut off the vocoder
70 |
71 | if (document.getElementById("modulatorpreview").classList.contains("playing") )
72 | finishPreviewingModulator();
73 |
74 | carrierPreviewImg.classList.add("playing");
75 | carrierPreviewImg.src = "img/ico-stop.png";
76 |
77 | carrierAnalyserNode = audioContext.createGain();
78 | carrierAnalyserNode.gain.value = 0.25; // carrier is LOUD.
79 | carrierAnalyserNode.connect( audioContext.destination );
80 | carrierAnalyserNode.connect( analyser2 );
81 |
82 | createCarriersAndPlay( carrierAnalyserNode );
83 |
84 | window.requestAnimationFrame( updateAnalysers );
85 |
86 | }
87 |
88 | function shutOffCarrier() {
89 | oscillatorNode.stop(0);
90 | oscillatorNode = null;
91 | noiseNode.stop(0);
92 | noiseNode = null;
93 | carrierSampleNode.stop(0);
94 | carrierSampleNode = null;
95 | }
96 |
97 | function finishPreviewingCarrier() {
98 | var carPreviewImg = document.getElementById("carrierpreview");
99 | carPreviewImg.classList.remove("playing");
100 | carPreviewImg.src = "img/ico-play.png";
101 |
102 | cancelVocoderUpdates();
103 | shutOffCarrier();
104 | carrierAnalyserNode.disconnect();
105 | carrierAnalyserNode = null;
106 | }
107 |
108 | var endOfModulatorTimer = 0;
109 |
110 | function previewModulator() {
111 | if (this.event)
112 | this.event.preventDefault();
113 |
114 | var modPreviewImg = document.getElementById("modulatorpreview");
115 | if (modPreviewImg.classList.contains("playing") ) {
116 | finishPreviewingModulator();
117 | return;
118 | }
119 |
120 | if (vocoding)
121 | vocode(); // this will shut off the vocoder
122 |
123 | if (document.getElementById("carrierpreview").classList.contains("playing") )
124 | finishPreviewingCarrier();
125 |
126 | modPreviewImg.classList.add("playing");
127 | modPreviewImg.src = "img/ico-stop.png";
128 | modulatorNode = audioContext.createBufferSource();
129 | modulatorNode.buffer = modulatorBuffer;
130 |
131 | vocoding = false;
132 | modulatorNode.connect( audioContext.destination );
133 | modulatorNode.connect( analyser1 );
134 |
135 | modulatorNode.start(0);
136 |
137 | window.requestAnimationFrame( updateAnalysers );
138 | endOfModulatorTimer = window.setTimeout( finishPreviewingModulator, modulatorNode.buffer.duration * 1000 + 20 );
139 | }
140 |
141 | function finishPreviewingModulator() {
142 | var modPreviewImg = document.getElementById("modulatorpreview");
143 | if (endOfModulatorTimer)
144 | window.clearTimeout(endOfModulatorTimer);
145 | endOfModulatorTimer = 0;
146 | cancelVocoderUpdates();
147 | modulatorNode.stop(0);
148 | modulatorNode = null;
149 | modPreviewImg.classList.remove("playing");
150 | modPreviewImg.src = "img/ico-play.png";
151 | }
152 |
153 | function loadModulator( buffer ) {
154 | modulatorBuffer = buffer;
155 | var e = document.getElementById("modulator");
156 | e.classList.remove("notready");
157 | e.classList.add("ready");
158 | }
159 |
160 | function loadCarrier( buffer ) {
161 | carrierBuffer = buffer;
162 | if (vocoding) {
163 | newCarrierNode = audioContext.createBufferSource();
164 | newCarrierNode.buffer = carrierBuffer;
165 | newCarrierNode.loop = true;
166 | newCarrierNode.connect( carrierInput );
167 | carrierNode.disconnect();
168 | newCarrierNode.start(0);
169 | carrierNode.stop(0);
170 | carrierNode = newCarrierNode;
171 | }
172 | var e = document.getElementById("carrier");
173 | e.classList.remove("notready");
174 | e.classList.add("ready");
175 | }
176 |
177 | function downloadAudioFromURL( url, cb ){
178 | var request = new XMLHttpRequest();
179 | request.open('GET', url, true);
180 | request.responseType = 'arraybuffer';
181 |
182 | // Decode asynchronously
183 | request.onload = function() {
184 | audioContext.decodeAudioData( request.response, function(buffer) {
185 | cb(buffer);
186 | }, function(){alert("error loading!");});
187 | }
188 | request.send();
189 | }
190 |
191 | function selectSawtooth() {
192 | if ( wavetableSignalGain )
193 | wavetableSignalGain.gain.value = SAWTOOTHBOOST;
194 | if (oscillatorNode)
195 | oscillatorNode.type = "sawtooth";
196 | document.getElementById("sawtooth").classList.add("active");
197 | document.getElementById("wavetable").classList.remove("active");
198 | }
199 |
200 | function selectWavetable() {
201 | if ( wavetableSignalGain )
202 | wavetableSignalGain.gain.value = WAVETABLEBOOST;
203 | if (oscillatorNode)
204 | oscillatorNode.setPeriodicWave ?
205 | oscillatorNode.setPeriodicWave(wavetable) :
206 | oscillatorNode.setWaveTable(wavetable);
207 | wavetableSignalGain.gain.value = WAVETABLEBOOST;
208 |
209 | document.getElementById("sawtooth").classList.remove("active");
210 | document.getElementById("wavetable").classList.add("active");
211 | }
212 |
213 | function setModulatorFileName( url ) {
214 | var lastSlash = url.lastIndexOf( "/" );
215 | if (lastSlash != -1)
216 | url = url.slice(lastSlash+1);
217 |
218 | var mod = document.getElementById("modulatorfilename");
219 | if (mod)
220 | mod.innerText = url;
221 | }
222 |
223 | function setCarrierFileName( url ) {
224 | var lastSlash = url.lastIndexOf( "/" );
225 | if (lastSlash != -1)
226 | url = url.slice(lastSlash+1);
227 |
228 | var carrier = document.getElementById("carrierfilename");
229 | if (carrier)
230 | carrier.innerText = url;
231 | }
232 |
233 | function startLoadingModulator( url ) {
234 | var e = document.getElementById("modulator");
235 | e.classList.remove("ready");
236 | e.classList.add("notready");
237 |
238 | modulatorBuffer = null;
239 | setModulatorFileName( url );
240 | downloadAudioFromURL( url, loadModulator );
241 | }
242 |
243 | function startLoadingCarrier( url ) {
244 | var e = document.getElementById("carrier");
245 | e.classList.remove("ready");
246 | e.classList.add("notready");
247 |
248 | carrierBuffer = null;
249 | setCarrierFileName( url )
250 | downloadAudioFromURL( url, loadCarrier );
251 | }
252 |
253 | // Set up the page as a drop site for audio files. When an audio file is
254 | // dropped on the page, it will be auto-loaded as an AudioBufferSourceNode.
255 | function initDragDropOfAudioFiles() {
256 | var mod = document.getElementById("modulator");
257 |
258 | mod.ondragover = function () {
259 | this.classList.add("droptarget");
260 | return false; };
261 | mod.ondragleave = function () { this.classList.remove("droptarget"); return false; };
262 | mod.ondragend = function () { this.classList.remove("droptarget"); return false; };
263 | mod.ondrop = function (e) {
264 | if (!audioContext)
265 | initAudio();
266 | this.classList.remove("droptarget");
267 | e.preventDefault();
268 | modulatorBuffer = null;
269 | setModulatorFileName( e.dataTransfer.files[0].name );
270 |
271 | var reader = new FileReader();
272 | reader.onload = function (event) {
273 | audioContext.decodeAudioData( event.target.result, function(buffer) {
274 | loadModulator( buffer );
275 | }, function(){alert("error loading!");} );
276 |
277 | };
278 | reader.onerror = function (event) {
279 | alert("Error: " + reader.error );
280 | };
281 | reader.readAsArrayBuffer(e.dataTransfer.files[0]);
282 | return false;
283 | };
284 | var car = document.getElementById("carrier");
285 |
286 | car.ondragover = function () { this.classList.add("droptarget"); return false; };
287 | car.ondragleave = function () { this.classList.remove("droptarget"); return false; };
288 | car.ondragend = function () { this.classList.remove("droptarget"); return false; };
289 | car.ondrop = function (e) {
290 | this.classList.remove("droptarget");
291 | e.preventDefault();
292 | carrierBuffer = null;
293 | setCarrierFileName( e.dataTransfer.files[0].name );
294 |
295 | var reader = new FileReader();
296 | reader.onload = function (event) {
297 | audioContext.decodeAudioData( event.target.result, function(buffer) {
298 | loadCarrier( buffer );
299 | }, function(){alert("error loading!");} );
300 |
301 | };
302 | reader.onerror = function (event) {
303 | alert("Error: " + reader.error );
304 | };
305 | reader.readAsArrayBuffer(e.dataTransfer.files[0]);
306 | return false;
307 | };
308 | }
309 |
310 | function updateSlider( element, value, units) {
311 | while (!element.classList.contains("module")) {
312 | if (element.classList.contains("control-group")) {
313 | //TODO: yes, this is lazy coding, and fragile.
314 | element.children[0].children[1].innerText = "" + value + units;
315 | return;
316 | }
317 | element = element.parentNode;
318 | }
319 | }
320 |
321 | function onUpdateModGain(event, ui) {
322 | updateSlider( event.target, ui.value, this.units );
323 | modulatorGainValue = ui.value;
324 | if (modulatorGain)
325 | modulatorGain.gain.value = ui.value;
326 | }
327 |
328 | // sample-based carrier
329 | function onUpdateSampleLevel(event, ui) {
330 | updateSlider( event.target, ui.value, this.units );
331 | carrierSampleGainValue = ui.value;
332 | if (carrierSampleGain)
333 | carrierSampleGain.gain.value = ui.value;
334 | }
335 |
336 | // noise in carrier
337 | function onUpdateSynthLevel(event, ui) {
338 | updateSlider( event.target, ui.value, this.units );
339 | oscillatorGainValue = ui.value;
340 | if (oscillatorGain)
341 | oscillatorGain.gain.value = ui.value;
342 | }
343 |
344 | // noise in carrier
345 | function onUpdateNoiseLevel(event, ui) {
346 | updateSlider( event.target, ui.value, this.units );
347 | noiseGainValue = ui.value;
348 | if (noiseGain)
349 | noiseGain.gain.value = ui.value;
350 | }
351 |
352 | // detuning for wavetable and sawtooth oscillators
353 | function onUpdateDetuneLevel(event, ui) {
354 | updateSlider( event.target, ui.value, this.units );
355 | oscillatorDetuneValue = ui.value;
356 | if (oscillatorNode)
357 | oscillatorNode.detune.value = ui.value;
358 | }
359 |
360 | function loadModulatorFile() {
361 | if (this.event)
362 | this.event.preventDefault();
363 |
364 | alert("Try dropping a file onto the modulator.");
365 | }
366 |
367 | function loadCarrierFile() {
368 | if (this.event)
369 | this.event.preventDefault();
370 |
371 | alert("Try dropping a file onto the carrier.");
372 | }
373 |
374 | function initAudio() {
375 | generateVocoderBands( 55, 7040, cheapAnalysis ? 14 : 28 );
376 |
377 | // Debug visualizer
378 | analyser1 = audioContext.createAnalyser();
379 | analyser1.fftSize = cheapAnalysis ? 256 : 1024;
380 | analyser1.smoothingTimeConstant = 0.5;
381 | analyser2 = audioContext.createAnalyser();
382 | analyser2.fftSize = cheapAnalysis ? 256 : 1024;
383 | analyser2.smoothingTimeConstant = 0.5;
384 |
385 | if (cheapAnalysis) {
386 | analyserView1 = document.getElementById("view1").getContext('2d');
387 | analyserView2 = document.getElementById("view2").getContext('2d');
388 | } else {
389 | analyserView1 = new AnalyserView("view1");
390 | analyserView1.initByteBuffer( analyser1 );
391 | analyserView2 = new AnalyserView("view2");
392 | analyserView2.initByteBuffer( analyser2 );
393 | }
394 |
395 | // Set up the vocoder chains
396 | setupVocoderGraph();
397 | }
398 |
399 | // Initialization function for the page.
400 | function init() {
401 | console.log("It's okay if the AudioContext doesn't start yet - ignore this warning.");
402 | audioContext = new AudioContext();
403 |
404 | startLoadingModulator( "audio/gettysburg.ogg" );
405 | startLoadingCarrier( "audio/junky.ogg" );
406 |
407 | document.getElementById("modpreview").addEventListener('click', previewModulator );
408 | document.getElementById("liveInput").addEventListener('click', useLiveInput );
409 | document.getElementById("loadcarrier").addEventListener('click', loadCarrierFile );
410 | document.getElementById("sawtooth").addEventListener('click', selectSawtooth );
411 | document.getElementById("wavetable").addEventListener('click', selectWavetable );
412 | document.getElementById("previewcarrier").addEventListener('click', previewCarrier );
413 | document.getElementById("play").addEventListener('click', vocode );
414 | document.getElementById("record").addEventListener('click', toggleRecording );
415 | initDragDropOfAudioFiles(); // set up panels as drop sites for audio files
416 |
417 | vocoderCanvas = document.getElementById("vcanvas").getContext('2d');
418 |
419 | // hook up the UI sliders
420 | var slider = document.createElement("div");
421 | slider.className="slider";
422 | document.getElementById("modgaingroup").appendChild(slider);
423 | $( slider ).slider( { slide: onUpdateModGain, value: (cheapAnalysis) ? 2.5 : 1.0, min: 0.0, max: 4.0, step: 0.1 } );
424 | slider.units = "";
425 |
426 | slider = document.createElement("div");
427 | slider.className="slider";
428 | document.getElementById("samplegroup").appendChild(slider);
429 | $( slider ).slider( { slide: onUpdateSampleLevel, value: 0.0, min: 0.0, max: 2.0, step: 0.01 } );
430 | slider.units = "";
431 |
432 | slider = document.createElement("div");
433 | slider.className="slider";
434 | document.getElementById("synthgroup").appendChild(slider);
435 | $( slider ).slider( { slide: onUpdateSynthLevel, value: 1.0, min: 0.0, max: 2.0, step: 0.01 } );
436 | slider.units = "";
437 |
438 | slider = document.createElement("div");
439 | slider.className="slider";
440 | document.getElementById("noisegroup").appendChild(slider);
441 | $( slider ).slider( { slide: onUpdateNoiseLevel, value: 0.22, min: 0.0, max: 2.0, step: 0.01 } );
442 | slider.units = "";
443 |
444 | slider = document.createElement("div");
445 | slider.className="slider";
446 | document.getElementById("detunegroup").appendChild(slider);
447 | $( slider ).slider( { slide: onUpdateDetuneLevel, value: 0, min: -1200, max: 1200, step: 1 } );
448 | slider.units = " cents";
449 | }
450 |
451 | function keyevent( event ) {
452 | if (event)
453 | return;
454 | }
455 |
456 | var recording=false;
457 | var recIndex=0;
458 | var audioRecorder=null;
459 |
460 | function toggleRecording() {
461 | initAudio(); // Make sure audioContext is started.
462 | if (recording) {
463 | // stop recording
464 | audioRecorder.stop();
465 | document.getElementById("record").classList.remove("recording");
466 | audioRecorder.getBuffers( gotBuffers );
467 | } else {
468 | // start recording
469 | if (!audioRecorder)
470 | audioRecorder = new Recorder( analyser2 );
471 |
472 | document.getElementById("record").classList.add("recording");
473 | var link = $("recfile");
474 | link.href = "#";
475 | link.innerText = "";
476 | audioRecorder.clear();
477 | audioRecorder.record();
478 | }
479 | recording = !recording;
480 | }
481 |
482 | function gotBuffers( buffers ) {
483 | // the ONLY time gotBuffers is called is right after a new recording is completed -
484 | // so here's where we should set up the download.
485 | audioRecorder.exportWAV( doneEncoding );
486 | }
487 |
488 | function doneEncoding( blob ) {
489 | Recorder.setupDownload( blob, "myRecording" + ((recIndex<10)?"0":"") + recIndex + ".wav" );
490 | document.getElementById("recfile").innerText = "download";
491 | recIndex++;
492 | }
493 |
494 | window.onload=init;
495 | window.onkeydown=keyevent();
496 |
--------------------------------------------------------------------------------
/js/midi.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 | *
4 | * Redistribution and use in source and binary forms, with or without
5 | * modification, are permitted provided that the following conditions are
6 | * met:
7 | *
8 | * * Redistributions of source code must retain the above copyright
9 | * notice, this list of conditions and the following disclaimer.
10 | * * Redistributions in binary form must reproduce the above
11 | * copyright notice, this list of conditions and the following disclaimer
12 | * in the documentation and/or other materials provided with the
13 | * distribution.
14 | * * Neither the name of Google Inc. nor the names of its
15 | * contributors may be used to endorse or promote products derived from
16 | * this software without specific prior written permission.
17 | *
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | */
30 |
31 | function midiMessageReceived( e ) {
32 | var cmd = e.data[0] >> 4;
33 | var channel = e.data[0] & 0xf;
34 | var b = e.data[1];
35 | var c = e.data[2];
36 |
37 | if ( cmd==8 || ((cmd==9)&&(c==0)) ) { // with MIDI, note on with velocity zero is the same as note off
38 | // if (b == lastNote) { // this keeps from shutting off if we're overlapping notes
39 | // we don't currently need note off
40 | // lastNote = -1;
41 | // }
42 | } else if (cmd == 9) { // note on message
43 | if (channel == 0 ) { // Change oscillator detune.
44 | var noteNumber = b - 60;
45 | var detuneValue = noteNumber * 100;
46 | var detunegroup = document.getElementById("detunegroup");
47 | $( detunegroup.children[1] ).slider( "value", detuneValue );
48 | updateSlider( detunegroup, detuneValue, " cents" );
49 | if (oscillatorNode)
50 | oscillatorNode.detune.value = detuneValue;
51 | } else if (channel == 1) { //pads - play previews
52 | if (b==48)
53 | previewModulator(); // is a toggle.
54 | else if (b==49)
55 | previewCarrier(); // is a toggle.
56 | else if (b==44)
57 | vocode(); // is a toggle.
58 | }
59 | } else if (cmd == 11) { // continuous controller
60 | switch (b) {
61 | case 1: // CC2: "Gender" - tuning frequencies
62 | scaleCarrierFilterFrequencies((Math.floor( (100 * c) / 63.5) / 100) + 0.5) // ideally would be 0.5 - 2.0, centered on 1.
63 | break;
64 |
65 | case 71:
66 | case 2: // CC1: Modulator gain level
67 | var value = Math.floor( (100 * c) / 63.5) / 50; // 0.0-4.0
68 | var modgaingroup = document.getElementById("modgaingroup");
69 | $( modgaingroup.children[1] ).slider( "value", value );
70 | updateSlider( modgaingroup, value, "" );
71 | modulatorGainValue = value;
72 | if (modulatorGain)
73 | modulatorGain.gain.value = value;
74 | break;
75 |
76 | case 74:
77 | case 5: // CC5: Carrier sample level
78 | var sampleValue = Math.floor( (100 * c) / 63.5) / 100; // 0.0-2.0
79 | var samplegroup = document.getElementById("samplegroup");
80 | $( samplegroup.children[1] ).slider( "value", sampleValue );
81 | updateSlider( samplegroup, sampleValue, "" );
82 | if (carrierSampleGain)
83 | carrierSampleGain.gain.value = sampleValue;
84 | break;
85 |
86 | case 10:
87 | case 6: // CC6: Carrier synth level
88 | var synthValue = Math.floor( (100 * c) / 63.5) / 100; // 0.0-2.0
89 | var synthgroup = document.getElementById("synthgroup");
90 | $( synthgroup.children[1] ).slider( "value", synthValue );
91 | updateSlider( synthgroup, synthValue, "" );
92 | if (oscillatorGain)
93 | oscillatorGain.gain.value = synthValue;
94 | break;
95 |
96 | case 7: // CC7: Carrier noise level
97 | var noiseValue = Math.floor( (100 * c) / 63.5) / 100; // 0.0-2.0
98 | var noisegroup = document.getElementById("noisegroup");
99 | $( noisegroup.children[1] ).slider( "value", noiseValue );
100 | updateSlider( noisegroup, noiseValue, "" );
101 | if (noiseGain)
102 | noiseGain.gain.value = noiseValue;
103 | break;
104 |
105 | case 73:
106 | case 8: // CC8: HP filter gain
107 | hpFilterGain.gain.value = c / 63.5; // 0.0-1.0
108 | break;
109 |
110 | default:
111 | console.log("Controller " + b + " received: " + c );
112 | }
113 | }
114 | }
115 |
116 | //init: create plugin
117 | window.addEventListener('load', function() {
118 | navigator.requestMIDIAccess().then( gotMIDI, didntGetMIDI );
119 | } );
120 |
121 | var midi = null;
122 | var midiIn = null;
123 |
124 | function gotMIDI( midiAccess ) {
125 | midi = midiAccess;
126 | if ((typeof(midiAccess.inputs) == "function")) { //Old Skool MIDI inputs() code
127 | var ins = midiAccess.inputs();
128 | for (var i=0; i
122 | var div = document.createElement("div");
123 | div.appendChild(document.createTextNode(label));
124 | var ctl = document.createElement("input");
125 | ctl.type = "range";
126 | ctl.min = minValue;
127 | ctl.max = maxValue;
128 | ctl.step = (maxValue - minValue) / 1000.0;
129 | ctl.value = defaultValue;
130 | ctl.oninput = onChange;
131 | ctl.audioNodes = nodeArray;
132 | ctl.label = label;
133 | div.appendChild(ctl);
134 | div.appendChild(document.createTextNode(defaultValue));
135 | document.getElementById("sliders").appendChild(div);
136 | }
137 |
138 | function addSingleValueSlider( label, defaultValue, minValue, maxValue, node, onChange ) {
139 | // insert a range control
140 | //
141 | var div = document.createElement("div");
142 | div.appendChild(document.createTextNode(label));
143 | var ctl = document.createElement("input");
144 | ctl.type = "range";
145 | ctl.min = minValue;
146 | ctl.max = maxValue;
147 | ctl.step = (maxValue - minValue) / 1000.0;
148 | ctl.value = defaultValue;
149 | ctl.oninput = onChange;
150 | ctl.audioNode = node;
151 | ctl.label = label;
152 | div.appendChild(ctl);
153 | div.appendChild(document.createTextNode(defaultValue));
154 | document.getElementById("sliders").appendChild(div);
155 | }
156 |
157 |
--------------------------------------------------------------------------------
/js/ui.js:
--------------------------------------------------------------------------------
1 | /* ui.js */
2 |
3 | (function($, w) {
4 | 'use strict';
5 |
6 | // Ready
7 | $(function() {
8 |
9 | $('.slider').slider();
10 |
11 | });
12 |
13 | })(jQuery, window);
14 |
--------------------------------------------------------------------------------
/js/visualizer/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cwilso/Vocoder/8d03e5a5e9ff7041ae3ec98228f1ab140db4b4eb/js/visualizer/.DS_Store
--------------------------------------------------------------------------------
/js/visualizer/base.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2009, Google Inc.
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are
7 | * met:
8 | *
9 | * * Redistributions of source code must retain the above copyright
10 | * notice, this list of conditions and the following disclaimer.
11 | * * Redistributions in binary form must reproduce the above
12 | * copyright notice, this list of conditions and the following disclaimer
13 | * in the documentation and/or other materials provided with the
14 | * distribution.
15 | * * Neither the name of Google Inc. nor the names of its
16 | * contributors may be used to endorse or promote products derived from
17 | * this software without specific prior written permission.
18 | *
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 | */
31 |
32 |
33 | /**
34 | * @fileoverview Base for all o3d sample utilties.
35 | * For more information about o3d see
36 | * http://code.google.com/p/o3d.
37 | *
38 | *
39 | * The main point of this module is to provide a central place to
40 | * have an init function to register an o3d namespace object because many other
41 | * modules need access to it.
42 | */
43 |
44 | /**
45 | * A namespace for all the o3djs utility libraries.
46 | * @namespace
47 | */
48 | var o3djs = o3djs || {};
49 |
50 | /**
51 | * Define this because the Google internal JSCompiler needs goog.typedef below.
52 | */
53 | var goog = goog || {};
54 |
55 | /**
56 | * A macro for defining composite types.
57 | *
58 | * By assigning goog.typedef to a name, this tells Google internal JSCompiler
59 | * that this is not the name of a class, but rather it's the name of a composite
60 | * type.
61 | *
62 | * For example,
63 | * /** @type {Array|NodeList} / goog.ArrayLike = goog.typedef;
64 | * will tell JSCompiler to replace all appearances of goog.ArrayLike in type
65 | * definitions with the union of Array and NodeList.
66 | *
67 | * Does nothing in uncompiled code.
68 | */
69 | goog.typedef = true;
70 |
71 | /**
72 | * Reference to the global context. In most cases this will be 'window'.
73 | */
74 | o3djs.global = this;
75 |
76 | /**
77 | * Flag used to force a function to run in the browser when it is called
78 | * from V8.
79 | * @type {boolean}
80 | */
81 | o3djs.BROWSER_ONLY = true;
82 |
83 | /**
84 | * Array of namespaces that have been provided.
85 | * @private
86 | * @type {!Array.}
87 | */
88 | o3djs.provided_ = [];
89 |
90 | /**
91 | * Creates object stubs for a namespace. When present in a file,
92 | * o3djs.provide also indicates that the file defines the indicated
93 | * object.
94 | * @param {string} name name of the object that this file defines.
95 | */
96 | o3djs.provide = function(name) {
97 | // Ensure that the same namespace isn't provided twice.
98 | if (o3djs.getObjectByName(name) &&
99 | !o3djs.implicitNamespaces_[name]) {
100 | throw 'Namespace "' + name + '" already declared.';
101 | }
102 |
103 | var namespace = name;
104 | while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) {
105 | o3djs.implicitNamespaces_[namespace] = true;
106 | }
107 |
108 | o3djs.exportPath_(name);
109 | o3djs.provided_.push(name);
110 | };
111 |
112 |
113 | /**
114 | * Namespaces implicitly defined by o3djs.provide. For example,
115 | * o3djs.provide('o3djs.events.Event') implicitly declares
116 | * that 'o3djs' and 'o3djs.events' must be namespaces.
117 | *
118 | * @type {Object}
119 | * @private
120 | */
121 | o3djs.implicitNamespaces_ = {};
122 |
123 | /**
124 | * Builds an object structure for the provided namespace path,
125 | * ensuring that names that already exist are not overwritten. For
126 | * example:
127 | * "a.b.c" -> a = {};a.b={};a.b.c={};
128 | * Used by o3djs.provide and o3djs.exportSymbol.
129 | * @param {string} name name of the object that this file defines.
130 | * @param {Object} opt_object the object to expose at the end of the path.
131 | * @param {Object} opt_objectToExportTo The object to add the path to; default
132 | * is |o3djs.global|.
133 | * @private
134 | */
135 | o3djs.exportPath_ = function(name, opt_object, opt_objectToExportTo) {
136 | var parts = name.split('.');
137 | var cur = opt_objectToExportTo || o3djs.global;
138 | var part;
139 |
140 | // Internet Explorer exhibits strange behavior when throwing errors from
141 | // methods externed in this manner. See the testExportSymbolExceptions in
142 | // base_test.html for an example.
143 | if (!(parts[0] in cur) && cur.execScript) {
144 | cur.execScript('var ' + parts[0]);
145 | }
146 |
147 | // Parentheses added to eliminate strict JS warning in Firefox.
148 | while (parts.length && (part = parts.shift())) {
149 | if (!parts.length && o3djs.isDef(opt_object)) {
150 | // last part and we have an object; use it.
151 | cur[part] = opt_object;
152 | } else if (cur[part]) {
153 | cur = cur[part];
154 | } else {
155 | cur = cur[part] = {};
156 | }
157 | }
158 | };
159 |
160 |
161 | /**
162 | * Returns an object based on its fully qualified external name. If you are
163 | * using a compilation pass that renames property names beware that using this
164 | * function will not find renamed properties.
165 | *
166 | * @param {string} name The fully qualified name.
167 | * @param {Object} opt_obj The object within which to look; default is
168 | * |o3djs.global|.
169 | * @return {Object} The object or, if not found, null.
170 | */
171 | o3djs.getObjectByName = function(name, opt_obj) {
172 | var parts = name.split('.');
173 | var cur = opt_obj || o3djs.global;
174 | for (var pp = 0; pp < parts.length; ++pp) {
175 | var part = parts[pp];
176 | if (cur[part]) {
177 | cur = cur[part];
178 | } else {
179 | return null;
180 | }
181 | }
182 | return cur;
183 | };
184 |
185 |
186 | /**
187 | * Implements a system for the dynamic resolution of dependencies.
188 | * @param {string} rule Rule to include, in the form o3djs.package.part.
189 | */
190 | o3djs.require = function(rule) {
191 | // TODO(gman): For some unknown reason, when we call
192 | // o3djs.util.getScriptTagText_ it calls
193 | // document.getElementsByTagName('script') and for some reason the scripts do
194 | // not always show up. Calling it here seems to fix that as long as we
195 | // actually ask for the length, at least in FF 3.5.1 It would be nice to
196 | // figure out why.
197 | var dummy = document.getElementsByTagName('script').length;
198 |
199 | // if the object already exists we do not need do do anything
200 | if (o3djs.getObjectByName(rule)) {
201 | return;
202 | }
203 | var path = o3djs.getPathFromRule_(rule);
204 | if (path) {
205 | o3djs.included_[path] = true;
206 | o3djs.writeScripts_();
207 | } else {
208 | throw new Error('o3djs.require could not find: ' + rule);
209 | }
210 | };
211 |
212 |
213 | /**
214 | * Path for included scripts.
215 | * @type {string}
216 | */
217 | o3djs.basePath = '';
218 |
219 |
220 | /**
221 | * Object used to keep track of urls that have already been added. This
222 | * record allows the prevention of circular dependencies.
223 | * @type {Object}
224 | * @private
225 | */
226 | o3djs.included_ = {};
227 |
228 |
229 | /**
230 | * This object is used to keep track of dependencies and other data that is
231 | * used for loading scripts.
232 | * @private
233 | * @type {Object}
234 | */
235 | o3djs.dependencies_ = {
236 | visited: {}, // used when resolving dependencies to prevent us from
237 | // visiting the file twice.
238 | written: {} // used to keep track of script files we have written.
239 | };
240 |
241 |
242 | /**
243 | * Tries to detect the base path of the o3djs-base.js script that
244 | * bootstraps the o3djs libraries.
245 | * @private
246 | */
247 | o3djs.findBasePath_ = function() {
248 | var doc = o3djs.global.document;
249 | if (typeof doc == 'undefined') {
250 | return;
251 | }
252 | if (o3djs.global.BASE_PATH) {
253 | o3djs.basePath = o3djs.global.BASE_PATH;
254 | return;
255 | } else {
256 | // HACKHACK to hide compiler warnings :(
257 | o3djs.global.BASE_PATH = null;
258 | }
259 | var scripts = doc.getElementsByTagName('script');
260 | for (var script, i = 0; script = scripts[i]; i++) {
261 | var src = script.src;
262 | var l = src.length;
263 | if (src.substr(l - 13) == 'o3djs/base.js') {
264 | o3djs.basePath = src.substr(0, l - 13);
265 | return;
266 | }
267 | }
268 | };
269 |
270 |
271 | /**
272 | * Writes a script tag if, and only if, that script hasn't already been added
273 | * to the document. (Must be called at execution time.)
274 | * @param {string} src Script source.
275 | * @private
276 | */
277 | o3djs.writeScriptTag_ = function(src) {
278 | var doc = o3djs.global.document;
279 | if (typeof doc != 'undefined' &&
280 | !o3djs.dependencies_.written[src]) {
281 | o3djs.dependencies_.written[src] = true;
282 | doc.write('