├── .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 | Fork me on GitHub 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('