├── .gitignore
├── LICENSE
├── README.md
├── demo.html
├── demo.js
├── demo_require.html
├── demo_require.js
├── require.js
├── requireLICENSE
└── tones.js
/.gitignore:
--------------------------------------------------------------------------------
1 | *.sublime-project
2 | *.sublime-workspace
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Keith Peters
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 | tones
2 | =====
3 |
4 | The Web Audio API is pretty amazing. You can actually synthesize audio with JavaScript right in the browser. The only problem is that it's pretty low level and not exactly intuitive to work with. This library is meant to make it as simple as humanly possible to create pleasant sounding musical tones.
5 |
6 | See a few demos of tones in action here:
7 |
8 | http://bit101.github.io/tones/
9 |
10 | The tones api is drop dead simple. One main method: `play`.
11 |
12 | The play method can be used in a few different ways:
13 |
14 | 1. Play a tone at a specific frequency.
15 | 2. Play a named note in the default 4th octave.
16 | 3. Play a named note in a specific octave.
17 |
18 | Examples:
19 |
20 | tones.play(440); // plays a 440 hz tone
21 | tones.play("c"); // plays a C note in the 4th octave.
22 | tones.play("c#"); // plays a C sharp note in the 4th octave.
23 | tones.play("eb"); // plays an E flat note in the 4th octave.
24 | tones.play("c", 2); // plays a C note in the 2nd octave.
25 |
26 | Named notes are case insensitive. "c" is the same as "C".
27 |
28 | There are also a few properties that affect the sound of the tone played. Set these before playing a tone and they will affect all future tones played.
29 |
30 | type // this is the wave form, a string of one of the following: "sine", "square", "sawtooth" or "triangle"
31 | attack // sets the attack of the envelope, or the time that the sound takes to reach full volume.
32 | release // sets the release of the envelope, or the time it takes for the sound to fade out.
33 |
34 | Attack and release are numbers which should generally be in the range of around 1 to 300. A low release, and mid-range release will give a bell sound. A long attack and release will sound more like a flute. Combined with different wave form types, this allows you to create all kinds of unique sounds.
35 |
36 | Note that attack and release values do not represent discrete times. But roughly speaking your sound will last about 10 times the total of these two in milliseconds. For example, an attack of 100 and release of 200 totals 300, so the tone will last around 3000 milliseconds or 3 seconds before completely fading out.
37 |
38 | There is also a `getTimeMS` function. This returns the current time reported by the audio context, which keeps a very accurate internal timer. Some of the examples use this to set up a sort of sequencer.
39 |
40 | There's a whole lot more that could be done to this, and I'll probably add features to this, while trying to make sure that the core functionality remains as simple as possible.
41 |
42 | It goes without saying that this library will only work in browsers that support the Web Audio API, which includes Chrome, Firefox and Safari (I think), but not Internet Explorer.
43 |
--------------------------------------------------------------------------------
/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | AudioLib
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/demo.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | // ambient();
4 | // scoredAmbient();
5 | // song();
6 | // interactive();
7 | // physics();
8 | piano();
9 |
10 |
11 | ////////////
12 | // sounds
13 | ////////////
14 |
15 | /*
16 | tone types:
17 | "sine",
18 | "square",
19 | "sawtooth",
20 | "triangle",
21 | "custom"
22 | */
23 |
24 |
25 |
26 | function interactive() {
27 | var types = ["sine", "square", "sawtooth", "triangle"];
28 | var typeLabel = document.createElement("h3");
29 | typeLabel.textContent = "type: " + tones.type;
30 | document.body.appendChild(typeLabel);
31 |
32 | var typeSlider = document.createElement("input");
33 | typeSlider.type = "range";
34 | typeSlider.min = 0;
35 | typeSlider.max = 3;
36 | typeSlider.value = types.indexOf(tones.type);
37 | typeSlider.style.width = "500px";
38 | typeSlider.addEventListener("input", function() {
39 | tones.type = types[typeSlider.value];
40 | typeLabel.textContent = "type: " + tones.type;
41 | })
42 | document.body.appendChild(typeSlider);
43 |
44 | var attackLabel = document.createElement("h3");
45 | attackLabel.textContent = "attack: " + tones.attack;
46 | document.body.appendChild(attackLabel);
47 |
48 | var attackSlider = document.createElement("input");
49 | attackSlider.type = "range";
50 | attackSlider.min = 1;
51 | attackSlider.max = 300;
52 | attackSlider.value = tones.attack;
53 | attackSlider.style.width = "500px";
54 | attackSlider.addEventListener("input", function() {
55 | tones.attack = attackSlider.value;
56 | attackLabel.textContent = "attack: " + tones.attack;
57 | })
58 | document.body.appendChild(attackSlider);
59 |
60 | var releaseLabel = document.createElement("h3");
61 | releaseLabel.textContent = "release: " + tones.release;
62 | document.body.appendChild(releaseLabel);
63 |
64 | var releaseSlider = document.createElement("input");
65 | releaseSlider.type = "range";
66 | releaseSlider.min = 1;
67 | releaseSlider.max = 300;
68 | releaseSlider.value = tones.release;
69 | releaseSlider.style.width = "500px";
70 | releaseSlider.addEventListener("input", function() {
71 | tones.release = releaseSlider.value;
72 | releaseLabel.textContent = "release: " + tones.release;
73 | })
74 | document.body.appendChild(releaseSlider);
75 |
76 | ambient();
77 | }
78 |
79 |
80 | function ambient() {
81 |
82 | var timing = 250;
83 | var notes = [ "C#", "D#", "F#", "D#"];
84 | var prevTime = tones.getTimeMS();
85 | var elapsed = 0;
86 |
87 | play();
88 |
89 |
90 | function play() {
91 | var now = tones.getTimeMS();
92 | elapsed += now - prevTime;
93 | if(elapsed > timing) {
94 | while(elapsed > timing) elapsed -= timing;
95 | var note = notes[Math.floor(Math.random() * notes.length)];
96 | var octave = Math.floor(Math.random() * 10);
97 | tones.play(note, octave);
98 | }
99 | prevTime = now;
100 | requestAnimationFrame(play);
101 | }
102 |
103 | }
104 |
105 |
106 | function scoredAmbient() {
107 | tones.type = "triangle";
108 | tones.release = 300;
109 |
110 | var timing = 250;
111 | var notes = [ "C#", "D#", "F#", "D#"];
112 |
113 | score = [];
114 | for(var i = 0; i < 16; i++) {
115 | var note = notes[Math.floor(Math.random() * notes.length)];
116 | var octave = Math.floor(Math.random() * 10);
117 | console.log(i, ":", note, octave);
118 | score.push({
119 | note: note,
120 | octave: octave
121 | });
122 | }
123 | var index = 0;
124 |
125 |
126 |
127 | var prevTime = tones.getTimeMS();
128 | var elapsed = 0
129 | play();
130 |
131 |
132 |
133 | function play() {
134 | var now = tones.getTimeMS();
135 | elapsed += now - prevTime;
136 | if(elapsed > timing) {
137 | elapsed -= timing;
138 | var t = score[index];
139 | tones.play(t.note, t.octave);
140 | index++;
141 | index %= score.length;
142 | }
143 | prevTime = now;
144 | requestAnimationFrame(play);
145 |
146 | }
147 |
148 | }
149 |
150 | function song() {
151 | tones.type = "square";
152 | tones.attack = 20;
153 | tones.release = 200;
154 |
155 | var notes = "ccggaag-ffeeddc-ggffeed-ggffeed-ccggaag-ffeeddc-----",
156 | timing = 300,
157 | index = 0;
158 |
159 | var prevTime = tones.getTimeMS();
160 | var elapsed = 0
161 | play();
162 |
163 |
164 |
165 | function play() {
166 | var now = tones.getTimeMS();
167 | elapsed += now - prevTime;
168 | if(elapsed > timing) {
169 | elapsed -= timing;
170 | var note = notes.charAt(index);
171 | if(note !== "-") {
172 | tones.play(note);
173 | }
174 | index++;
175 | index %= notes.length;
176 | }
177 | prevTime = now;
178 | requestAnimationFrame(play);
179 |
180 | }
181 | }
182 |
183 | function physics() {
184 | var canvas = document.createElement("canvas"),
185 | context = canvas.getContext("2d"),
186 | width = canvas.width = window.innerWidth,
187 | height = canvas.height = window.innerHeight;
188 |
189 | canvas.style.display = "block";
190 | document.body.style.margin = 0;
191 | document.body.appendChild(canvas);
192 |
193 |
194 | var balls = [],
195 | num = 8,
196 | gravity = 0.5;
197 |
198 | for(var i = 0; i < num; i++) {
199 | var size = Math.random();
200 | balls.push({
201 | x: Math.random() * width,
202 | y: Math.random() * height,
203 | vx: Math.random() * 10 - 5,
204 | vy: Math.random() * 10 - 5,
205 | radius: 10 + size * 50,
206 | freq: 350 - size * 300
207 | })
208 | }
209 |
210 | play();
211 |
212 | function play() {
213 | context.clearRect(0, 0, width, height);
214 | for(var i = 0; i < num; i++) {
215 | var ball = balls[i];
216 | ball.x += ball.vx;
217 | ball.y += ball.vy;
218 | if(ball.x + ball.radius > width) {
219 | ball.x = width - ball.radius;
220 | ball.vx *= -1;
221 | tones.play(ball.freq);
222 | }
223 | else if(ball.x - ball.radius < 0) {
224 | ball.x = ball.radius;
225 | ball.vx *= -1;
226 | tones.play(ball.freq);
227 | }
228 | if(ball.y + ball.radius > height) {
229 | ball.y = height - ball.radius;
230 | ball.vy *= -1;
231 | if(Math.abs(ball.vy) > 2)
232 | tones.play(ball.freq);
233 | }
234 | else if(ball.y - ball.radius < 0) {
235 | ball.y = ball.radius;
236 | ball.vy *= -1;
237 | tones.play(ball.freq);
238 | }
239 | ball.vy += gravity;
240 | context.beginPath();
241 | context.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2, false);
242 | context.fill();
243 | }
244 |
245 |
246 |
247 | requestAnimationFrame(play);
248 | }
249 | }
250 |
251 | function piano() {
252 | tones.attack = 0;
253 | tones.release = 300;
254 | tones.type = "sawtooth";
255 | // white
256 | var notes = ["c", "d", "e", "f", "g", "a", "b"];
257 | for(var i = 0; i < 7; i++) {
258 | makeKey(100 + i * 100, 100, 100, 500, "white", notes[i]);
259 | }
260 | // black
261 | makeKey(170, 100, 60, 275, "black", "c#");
262 | makeKey(270, 100, 60, 275, "black", "d#");
263 | makeKey(470, 100, 60, 275, "black", "f#");
264 | makeKey(570, 100, 60, 275, "black", "g#");
265 | makeKey(670, 100, 60, 275, "black", "a#");
266 |
267 |
268 | function makeKey(x, y, width, height, color, note) {
269 | var key = document.createElement("div");
270 | key.style.width = width + "px";
271 | key.style.height = height + "px";
272 | key.style.position = "absolute";
273 | key.style.left = x + "px";
274 | key.style.top = y + "px";
275 | key.style.backgroundColor = color;
276 | key.style.border = "solid 1px black";
277 | key.note = note;
278 | key.addEventListener("mousedown", function(event) {
279 | tones.play(event.target.note);
280 | });
281 | document.body.appendChild(key);
282 |
283 | }
284 | }
285 |
--------------------------------------------------------------------------------
/demo_require.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | AudioLib
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/demo_require.js:
--------------------------------------------------------------------------------
1 | require(["tones"], function(tones) {
2 |
3 | // ambient();
4 | // scoredAmbient();
5 | // song();
6 | // interactive();
7 | // physics();
8 | piano();
9 |
10 |
11 | ////////////
12 | // sounds
13 | ////////////
14 |
15 | /*
16 | tone types:
17 | "sine",
18 | "square",
19 | "sawtooth",
20 | "triangle",
21 | "custom"
22 | */
23 |
24 |
25 |
26 | function interactive() {
27 | var types = ["sine", "square", "sawtooth", "triangle"];
28 | var typeLabel = document.createElement("h3");
29 | typeLabel.textContent = "type: " + tones.type;
30 | document.body.appendChild(typeLabel);
31 |
32 | var typeSlider = document.createElement("input");
33 | typeSlider.type = "range";
34 | typeSlider.min = 0;
35 | typeSlider.max = 3;
36 | typeSlider.value = types.indexOf(tones.type);
37 | typeSlider.style.width = "500px";
38 | typeSlider.addEventListener("input", function() {
39 | tones.type = types[typeSlider.value];
40 | typeLabel.textContent = "type: " + tones.type;
41 | })
42 | document.body.appendChild(typeSlider);
43 |
44 | var attackLabel = document.createElement("h3");
45 | attackLabel.textContent = "attack: " + tones.attack;
46 | document.body.appendChild(attackLabel);
47 |
48 | var attackSlider = document.createElement("input");
49 | attackSlider.type = "range";
50 | attackSlider.min = 1;
51 | attackSlider.max = 300;
52 | attackSlider.value = tones.attack;
53 | attackSlider.style.width = "500px";
54 | attackSlider.addEventListener("input", function() {
55 | tones.attack = attackSlider.value;
56 | attackLabel.textContent = "attack: " + tones.attack;
57 | })
58 | document.body.appendChild(attackSlider);
59 |
60 | var releaseLabel = document.createElement("h3");
61 | releaseLabel.textContent = "release: " + tones.release;
62 | document.body.appendChild(releaseLabel);
63 |
64 | var releaseSlider = document.createElement("input");
65 | releaseSlider.type = "range";
66 | releaseSlider.min = 1;
67 | releaseSlider.max = 300;
68 | releaseSlider.value = tones.release;
69 | releaseSlider.style.width = "500px";
70 | releaseSlider.addEventListener("input", function() {
71 | tones.release = releaseSlider.value;
72 | releaseLabel.textContent = "release: " + tones.release;
73 | })
74 | document.body.appendChild(releaseSlider);
75 |
76 | ambient();
77 | }
78 |
79 |
80 | function ambient() {
81 |
82 | var timing = 250;
83 | var notes = [ "C#", "D#", "F#", "D#"];
84 | var prevTime = tones.getTimeMS();
85 | var elapsed = 0;
86 |
87 | play();
88 |
89 |
90 | function play() {
91 | var now = tones.getTimeMS();
92 | elapsed += now - prevTime;
93 | if(elapsed > timing) {
94 | while(elapsed > timing) elapsed -= timing;
95 | var note = notes[Math.floor(Math.random() * notes.length)];
96 | var octave = Math.floor(Math.random() * 10);
97 | tones.play(note, octave);
98 | }
99 | prevTime = now;
100 | requestAnimationFrame(play);
101 | }
102 |
103 | }
104 |
105 |
106 | function scoredAmbient() {
107 | tones.type = "triangle";
108 | tones.release = 300;
109 |
110 | var timing = 250;
111 | var notes = [ "C#", "D#", "F#", "D#"];
112 |
113 | score = [];
114 | for(var i = 0; i < 16; i++) {
115 | var note = notes[Math.floor(Math.random() * notes.length)];
116 | var octave = Math.floor(Math.random() * 10);
117 | console.log(i, ":", note, octave);
118 | score.push({
119 | note: note,
120 | octave: octave
121 | });
122 | }
123 | var index = 0;
124 |
125 |
126 |
127 | var prevTime = tones.getTimeMS();
128 | var elapsed = 0
129 | play();
130 |
131 |
132 |
133 | function play() {
134 | var now = tones.getTimeMS();
135 | elapsed += now - prevTime;
136 | if(elapsed > timing) {
137 | elapsed -= timing;
138 | var t = score[index];
139 | tones.play(t.note, t.octave);
140 | index++;
141 | index %= score.length;
142 | }
143 | prevTime = now;
144 | requestAnimationFrame(play);
145 |
146 | }
147 |
148 | }
149 |
150 | function song() {
151 | tones.type = "square";
152 | tones.attack = 20;
153 | tones.release = 200;
154 |
155 | var notes = "ccggaag-ffeeddc-ggffeed-ggffeed-ccggaag-ffeeddc-----",
156 | timing = 300,
157 | index = 0;
158 |
159 | var prevTime = tones.getTimeMS();
160 | var elapsed = 0
161 | play();
162 |
163 |
164 |
165 | function play() {
166 | var now = tones.getTimeMS();
167 | elapsed += now - prevTime;
168 | if(elapsed > timing) {
169 | elapsed -= timing;
170 | var note = notes.charAt(index);
171 | if(note !== "-") {
172 | tones.play(note);
173 | }
174 | index++;
175 | index %= notes.length;
176 | }
177 | prevTime = now;
178 | requestAnimationFrame(play);
179 |
180 | }
181 | }
182 |
183 | function physics() {
184 | var canvas = document.createElement("canvas"),
185 | context = canvas.getContext("2d"),
186 | width = canvas.width = window.innerWidth,
187 | height = canvas.height = window.innerHeight;
188 |
189 | canvas.style.display = "block";
190 | document.body.style.margin = 0;
191 | document.body.appendChild(canvas);
192 |
193 |
194 | var balls = [],
195 | num = 8,
196 | gravity = 0.5;
197 |
198 | for(var i = 0; i < num; i++) {
199 | var size = Math.random();
200 | balls.push({
201 | x: Math.random() * width,
202 | y: Math.random() * height,
203 | vx: Math.random() * 10 - 5,
204 | vy: Math.random() * 10 - 5,
205 | radius: 10 + size * 50,
206 | freq: 350 - size * 300
207 | })
208 | }
209 |
210 | play();
211 |
212 | function play() {
213 | context.clearRect(0, 0, width, height);
214 | for(var i = 0; i < num; i++) {
215 | var ball = balls[i];
216 | ball.x += ball.vx;
217 | ball.y += ball.vy;
218 | if(ball.x + ball.radius > width) {
219 | ball.x = width - ball.radius;
220 | ball.vx *= -1;
221 | tones.play(ball.freq);
222 | }
223 | else if(ball.x - ball.radius < 0) {
224 | ball.x = ball.radius;
225 | ball.vx *= -1;
226 | tones.play(ball.freq);
227 | }
228 | if(ball.y + ball.radius > height) {
229 | ball.y = height - ball.radius;
230 | ball.vy *= -1;
231 | if(Math.abs(ball.vy) > 2)
232 | tones.play(ball.freq);
233 | }
234 | else if(ball.y - ball.radius < 0) {
235 | ball.y = ball.radius;
236 | ball.vy *= -1;
237 | tones.play(ball.freq);
238 | }
239 | ball.vy += gravity;
240 | context.beginPath();
241 | context.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2, false);
242 | context.fill();
243 | }
244 |
245 |
246 |
247 | requestAnimationFrame(play);
248 | }
249 | }
250 |
251 | function piano() {
252 | tones.attack = 0;
253 | tones.release = 300;
254 | tones.type = "sawtooth";
255 | // white
256 | var notes = ["c", "d", "e", "f", "g", "a", "b"];
257 | for(var i = 0; i < 7; i++) {
258 | makeKey(100 + i * 100, 100, 100, 500, "white", notes[i]);
259 | }
260 | // black
261 | makeKey(170, 100, 60, 275, "black", "c#");
262 | makeKey(270, 100, 60, 275, "black", "d#");
263 | makeKey(470, 100, 60, 275, "black", "f#");
264 | makeKey(570, 100, 60, 275, "black", "g#");
265 | makeKey(670, 100, 60, 275, "black", "a#");
266 |
267 |
268 | function makeKey(x, y, width, height, color, note) {
269 | var key = document.createElement("div");
270 | key.style.width = width + "px";
271 | key.style.height = height + "px";
272 | key.style.position = "absolute";
273 | key.style.left = x + "px";
274 | key.style.top = y + "px";
275 | key.style.backgroundColor = color;
276 | key.style.border = "solid 1px black";
277 | key.note = note;
278 | key.addEventListener("mousedown", function(event) {
279 | tones.play(event.target.note);
280 | });
281 | document.body.appendChild(key);
282 |
283 | }
284 | }
285 | });
--------------------------------------------------------------------------------
/require.js:
--------------------------------------------------------------------------------
1 | /*
2 | RequireJS 1.0.8 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
3 | Available via the MIT or new BSD license.
4 | see: http://github.com/jrburke/requirejs for details
5 | */
6 | var requirejs,require,define;
7 | (function(r){function K(a){return O.call(a)==="[object Function]"}function G(a){return O.call(a)==="[object Array]"}function $(a,c,l){for(var j in c)if(!(j in L)&&(!(j in a)||l))a[j]=c[j];return d}function P(a,c,d){a=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+a);if(d)a.originalError=d;return a}function aa(a,c,d){var j,k,t;for(j=0;t=c[j];j++){t=typeof t==="string"?{name:t}:t;k=t.location;if(d&&(!k||k.indexOf("/")!==0&&k.indexOf(":")===-1))k=d+"/"+(k||t.name);a[t.name]={name:t.name,location:k||
8 | t.name,main:(t.main||"main").replace(fa,"").replace(ba,"")}}}function V(a,c){a.holdReady?a.holdReady(c):c?a.readyWait+=1:a.ready(!0)}function ga(a){function c(b,f){var g,m;if(b&&b.charAt(0)===".")if(f){q.pkgs[f]?f=[f]:(f=f.split("/"),f=f.slice(0,f.length-1));g=b=f.concat(b.split("/"));var a;for(m=0;a=g[m];m++)if(a===".")g.splice(m,1),m-=1;else if(a==="..")if(m===1&&(g[2]===".."||g[0]===".."))break;else m>0&&(g.splice(m-1,2),m-=2);m=q.pkgs[g=b[0]];b=b.join("/");m&&b===g+"/"+m.main&&(b=g)}else b.indexOf("./")===
9 | 0&&(b=b.substring(2));return b}function l(b,f){var g=b?b.indexOf("!"):-1,m=null,a=f?f.name:null,h=b,e,d;g!==-1&&(m=b.substring(0,g),b=b.substring(g+1,b.length));m&&(m=c(m,a));b&&(m?e=(g=n[m])&&g.normalize?g.normalize(b,function(b){return c(b,a)}):c(b,a):(e=c(b,a),d=G[e],d||(d=i.nameToUrl(b,null,f),G[e]=d)));return{prefix:m,name:e,parentMap:f,url:d,originalName:h,fullName:m?m+"!"+(e||""):e}}function j(){var b=!0,f=q.priorityWait,g,a;if(f){for(a=0;g=f[a];a++)if(!s[g]){b=!1;break}b&&delete q.priorityWait}return b}
10 | function k(b,f,g){return function(){var a=ha.call(arguments,0),c;if(g&&K(c=a[a.length-1]))c.__requireJsBuild=!0;a.push(f);return b.apply(null,a)}}function t(b,f,g){f=k(g||i.require,b,f);$(f,{nameToUrl:k(i.nameToUrl,b),toUrl:k(i.toUrl,b),defined:k(i.requireDefined,b),specified:k(i.requireSpecified,b),isBrowser:d.isBrowser});return f}function p(b){var f,g,a,c=b.callback,h=b.map,e=h.fullName,ca=b.deps;a=b.listeners;var j=q.requireExecCb||d.execCb;if(c&&K(c)){if(q.catchError.define)try{g=j(e,b.callback,
11 | ca,n[e])}catch(k){f=k}else g=j(e,b.callback,ca,n[e]);if(e)(c=b.cjsModule)&&c.exports!==r&&c.exports!==n[e]?g=n[e]=b.cjsModule.exports:g===r&&b.usingExports?g=n[e]:(n[e]=g,H[e]&&(T[e]=!0))}else e&&(g=n[e]=c,H[e]&&(T[e]=!0));if(x[b.id])delete x[b.id],b.isDone=!0,i.waitCount-=1,i.waitCount===0&&(J=[]);delete M[e];if(d.onResourceLoad&&!b.placeholder)d.onResourceLoad(i,h,b.depArray);if(f)return g=(e?l(e).url:"")||f.fileName||f.sourceURL,a=f.moduleTree,f=P("defineerror",'Error evaluating module "'+e+'" at location "'+
12 | g+'":\n'+f+"\nfileName:"+g+"\nlineNumber: "+(f.lineNumber||f.line),f),f.moduleName=e,f.moduleTree=a,d.onError(f);for(f=0;c=a[f];f++)c(g);return r}function u(b,f){return function(g){b.depDone[f]||(b.depDone[f]=!0,b.deps[f]=g,b.depCount-=1,b.depCount||p(b))}}function o(b,f){var g=f.map,a=g.fullName,c=g.name,h=N[b]||(N[b]=n[b]),e;if(!f.loading)f.loading=!0,e=function(b){f.callback=function(){return b};p(f);s[f.id]=!0;A()},e.fromText=function(b,f){var g=Q;s[b]=!1;i.scriptCount+=1;i.fake[b]=!0;g&&(Q=!1);
13 | d.exec(f);g&&(Q=!0);i.completeLoad(b)},a in n?e(n[a]):h.load(c,t(g.parentMap,!0,function(b,a){var c=[],e,m;for(e=0;m=b[e];e++)m=l(m,g.parentMap),b[e]=m.fullName,m.prefix||c.push(b[e]);f.moduleDeps=(f.moduleDeps||[]).concat(c);return i.require(b,a)}),e,q)}function y(b){x[b.id]||(x[b.id]=b,J.push(b),i.waitCount+=1)}function D(b){this.listeners.push(b)}function v(b,f){var g=b.fullName,a=b.prefix,c=a?N[a]||(N[a]=n[a]):null,h,e;g&&(h=M[g]);if(!h&&(e=!0,h={id:(a&&!c?O++ +"__p@:":"")+(g||"__r@"+O++),map:b,
14 | depCount:0,depDone:[],depCallbacks:[],deps:[],listeners:[],add:D},B[h.id]=!0,g&&(!a||N[a])))M[g]=h;a&&!c?(g=l(a),a in n&&!n[a]&&(delete n[a],delete R[g.url]),a=v(g,!0),a.add(function(){var f=l(b.originalName,b.parentMap),f=v(f,!0);h.placeholder=!0;f.add(function(b){h.callback=function(){return b};p(h)})})):e&&f&&(s[h.id]=!1,i.paused.push(h),y(h));return h}function C(b,f,a,c){var b=l(b,c),d=b.name,h=b.fullName,e=v(b),j=e.id,k=e.deps,o;if(h){if(h in n||s[j]===!0||h==="jquery"&&q.jQuery&&q.jQuery!==
15 | a().fn.jquery)return;B[j]=!0;s[j]=!0;h==="jquery"&&a&&W(a())}e.depArray=f;e.callback=a;for(a=0;a0)return r;if(q.priorityWait)if(j())A();else return r;for(h in s)if(!(h in L)&&(c=!0,!s[h]))if(b)a+=h+" ";else if(l=!0,h.indexOf("!")===-1){k=[];break}else(e=M[h]&&M[h].moduleDeps)&&k.push.apply(k,e);if(!c&&!i.waitCount)return r;if(b&&a)return b=P("timeout","Load timeout for modules: "+a),b.requireType="timeout",b.requireModules=a,b.contextName=i.contextName,d.onError(b);
18 | if(l&&k.length)for(a=0;h=x[k[a]];a++)if(h=F(h,{})){z(h,{});break}if(!b&&(l||i.scriptCount)){if((I||da)&&!X)X=setTimeout(function(){X=0;E()},50);return r}if(i.waitCount){for(a=0;h=J[a];a++)z(h,{});i.paused.length&&A();Y<5&&(Y+=1,E())}Y=0;d.checkReadyState();return r}var i,A,q={waitSeconds:7,baseUrl:"./",paths:{},pkgs:{},catchError:{}},S=[],B={require:!0,exports:!0,module:!0},G={},n={},s={},x={},J=[],R={},O=0,M={},N={},H={},T={},Z=0;W=function(b){if(!i.jQuery&&(b=b||(typeof jQuery!=="undefined"?jQuery:
19 | null))&&!(q.jQuery&&b.fn.jquery!==q.jQuery)&&("holdReady"in b||"readyWait"in b))if(i.jQuery=b,w(["jquery",[],function(){return jQuery}]),i.scriptCount)V(b,!0),i.jQueryIncremented=!0};A=function(){var b,a,c,l,k,h;i.takeGlobalQueue();Z+=1;if(i.scriptCount<=0)i.scriptCount=0;for(;S.length;)if(b=S.shift(),b[0]===null)return d.onError(P("mismatch","Mismatched anonymous define() module: "+b[b.length-1]));else w(b);if(!q.priorityWait||j())for(;i.paused.length;){k=i.paused;i.pausedCount+=k.length;i.paused=
20 | [];for(l=0;b=k[l];l++)a=b.map,c=a.url,h=a.fullName,a.prefix?o(a.prefix,b):!R[c]&&!s[h]&&((q.requireLoad||d.load)(i,h,c),c.indexOf("empty:")!==0&&(R[c]=!0));i.startTime=(new Date).getTime();i.pausedCount-=k.length}Z===1&&E();Z-=1;return r};i={contextName:a,config:q,defQueue:S,waiting:x,waitCount:0,specified:B,loaded:s,urlMap:G,urlFetched:R,scriptCount:0,defined:n,paused:[],pausedCount:0,plugins:N,needFullExec:H,fake:{},fullExec:T,managerCallbacks:M,makeModuleMap:l,normalize:c,configure:function(b){var a,
21 | c,d;b.baseUrl&&b.baseUrl.charAt(b.baseUrl.length-1)!=="/"&&(b.baseUrl+="/");a=q.paths;d=q.pkgs;$(q,b,!0);if(b.paths){for(c in b.paths)c in L||(a[c]=b.paths[c]);q.paths=a}if((a=b.packagePaths)||b.packages){if(a)for(c in a)c in L||aa(d,a[c],c);b.packages&&aa(d,b.packages);q.pkgs=d}if(b.priority)c=i.requireWait,i.requireWait=!1,A(),i.require(b.priority),A(),i.requireWait=c,q.priorityWait=b.priority;if(b.deps||b.callback)i.require(b.deps||[],b.callback)},requireDefined:function(b,a){return l(b,a).fullName in
22 | n},requireSpecified:function(b,a){return l(b,a).fullName in B},require:function(b,c,g){if(typeof b==="string"){if(K(c))return d.onError(P("requireargs","Invalid require call"));if(d.get)return d.get(i,b,c);c=l(b,c);b=c.fullName;return!(b in n)?d.onError(P("notloaded","Module name '"+c.fullName+"' has not been loaded yet for context: "+a)):n[b]}(b&&b.length||c)&&C(null,b,c,g);if(!i.requireWait)for(;!i.scriptCount&&i.paused.length;)A();return i.require},takeGlobalQueue:function(){U.length&&(ja.apply(i.defQueue,
23 | [i.defQueue.length-1,0].concat(U)),U=[])},completeLoad:function(b){var a;for(i.takeGlobalQueue();S.length;)if(a=S.shift(),a[0]===null){a[0]=b;break}else if(a[0]===b)break;else w(a),a=null;a?w(a):w([b,[],b==="jquery"&&typeof jQuery!=="undefined"?function(){return jQuery}:null]);d.isAsync&&(i.scriptCount-=1);A();d.isAsync||(i.scriptCount-=1)},toUrl:function(b,a){var c=b.lastIndexOf("."),d=null;c!==-1&&(d=b.substring(c,b.length),b=b.substring(0,c));return i.nameToUrl(b,d,a)},nameToUrl:function(b,a,g){var l,
24 | k,h,e,j=i.config,b=c(b,g&&g.fullName);if(d.jsExtRegExp.test(b))a=b+(a?a:"");else{l=j.paths;k=j.pkgs;g=b.split("/");for(e=g.length;e>0;e--)if(h=g.slice(0,e).join("/"),l[h]){g.splice(0,e,l[h]);break}else if(h=k[h]){b=b===h.name?h.location+"/"+h.main:h.location;g.splice(0,e,b);break}a=g.join("/")+(a||".js");a=(a.charAt(0)==="/"||a.match(/^[\w\+\.\-]+:/)?"":j.baseUrl)+a}return j.urlArgs?a+((a.indexOf("?")===-1?"?":"&")+j.urlArgs):a}};i.jQueryCheck=W;i.resume=A;return i}function ka(){var a,c,d;if(C&&C.readyState===
25 | "interactive")return C;a=document.getElementsByTagName("script");for(c=a.length-1;c>-1&&(d=a[c]);c--)if(d.readyState==="interactive")return C=d;return null}var la=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,ma=/require\(\s*["']([^'"\s]+)["']\s*\)/g,fa=/^\.\//,ba=/\.js$/,O=Object.prototype.toString,u=Array.prototype,ha=u.slice,ja=u.splice,I=!!(typeof window!=="undefined"&&navigator&&document),da=!I&&typeof importScripts!=="undefined",na=I&&navigator.platform==="PLAYSTATION 3"?/^complete$/:/^(complete|loaded)$/,
26 | ea=typeof opera!=="undefined"&&opera.toString()==="[object Opera]",L={},D={},U=[],C=null,Y=0,Q=!1,ia={require:!0,module:!0,exports:!0},d,u={},J,y,v,E,o,w,F,B,z,W,X;if(typeof define==="undefined"){if(typeof requirejs!=="undefined")if(K(requirejs))return;else u=requirejs,requirejs=r;typeof require!=="undefined"&&!K(require)&&(u=require,require=r);d=requirejs=function(a,c,d){var j="_",k;!G(a)&&typeof a!=="string"&&(k=a,G(c)?(a=c,c=d):a=[]);if(k&&k.context)j=k.context;d=D[j]||(D[j]=ga(j));k&&d.configure(k);
27 | return d.require(a,c)};d.config=function(a){return d(a)};require||(require=d);d.toUrl=function(a){return D._.toUrl(a)};d.version="1.0.8";d.jsExtRegExp=/^\/|:|\?|\.js$/;y=d.s={contexts:D,skipAsync:{}};if(d.isAsync=d.isBrowser=I)if(v=y.head=document.getElementsByTagName("head")[0],E=document.getElementsByTagName("base")[0])v=y.head=E.parentNode;d.onError=function(a){throw a;};d.load=function(a,c,l){d.resourcesReady(!1);a.scriptCount+=1;d.attach(l,a,c);if(a.jQuery&&!a.jQueryIncremented)V(a.jQuery,!0),
28 | a.jQueryIncremented=!0};define=function(a,c,d){var j,k;typeof a!=="string"&&(d=c,c=a,a=null);G(c)||(d=c,c=[]);!c.length&&K(d)&&d.length&&(d.toString().replace(la,"").replace(ma,function(a,d){c.push(d)}),c=(d.length===1?["require"]:["require","exports","module"]).concat(c));if(Q&&(j=J||ka()))a||(a=j.getAttribute("data-requiremodule")),k=D[j.getAttribute("data-requirecontext")];(k?k.defQueue:U).push([a,c,d]);return r};define.amd={multiversion:!0,plugins:!0,jQuery:!0};d.exec=function(a){return eval(a)};
29 | d.execCb=function(a,c,d,j){return c.apply(j,d)};d.addScriptToDom=function(a){J=a;E?v.insertBefore(a,E):v.appendChild(a);J=null};d.onScriptLoad=function(a){var c=a.currentTarget||a.srcElement,l;if(a.type==="load"||c&&na.test(c.readyState))C=null,a=c.getAttribute("data-requirecontext"),l=c.getAttribute("data-requiremodule"),D[a].completeLoad(l),c.detachEvent&&!ea?c.detachEvent("onreadystatechange",d.onScriptLoad):c.removeEventListener("load",d.onScriptLoad,!1)};d.attach=function(a,c,l,j,k,o){var p;
30 | if(I)return j=j||d.onScriptLoad,p=c&&c.config&&c.config.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script"),p.type=k||c&&c.config.scriptType||"text/javascript",p.charset="utf-8",p.async=!y.skipAsync[a],c&&p.setAttribute("data-requirecontext",c.contextName),p.setAttribute("data-requiremodule",l),p.attachEvent&&!(p.attachEvent.toString&&p.attachEvent.toString().indexOf("[native code]")<0)&&!ea?(Q=!0,o?p.onreadystatechange=function(){if(p.readyState===
31 | "loaded")p.onreadystatechange=null,p.attachEvent("onreadystatechange",j),o(p)}:p.attachEvent("onreadystatechange",j)):p.addEventListener("load",j,!1),p.src=a,o||d.addScriptToDom(p),p;else da&&(importScripts(a),c.completeLoad(l));return null};if(I){o=document.getElementsByTagName("script");for(B=o.length-1;B>-1&&(w=o[B]);B--){if(!v)v=w.parentNode;if(F=w.getAttribute("data-main")){if(!u.baseUrl)o=F.split("/"),w=o.pop(),o=o.length?o.join("/")+"/":"./",u.baseUrl=o,F=w.replace(ba,"");u.deps=u.deps?u.deps.concat(F):
32 | [F];break}}}d.checkReadyState=function(){var a=y.contexts,c;for(c in a)if(!(c in L)&&a[c].waitCount)return;d.resourcesReady(!0)};d.resourcesReady=function(a){var c,l;d.resourcesDone=a;if(d.resourcesDone)for(l in a=y.contexts,a)if(!(l in L)&&(c=a[l],c.jQueryIncremented))V(c.jQuery,!1),c.jQueryIncremented=!1};d.pageLoaded=function(){if(document.readyState!=="complete")document.readyState="complete"};if(I&&document.addEventListener&&!document.readyState)document.readyState="loading",window.addEventListener("load",
33 | d.pageLoaded,!1);d(u);if(d.isAsync&&typeof setTimeout!=="undefined")z=y.contexts[u.context||"_"],z.requireWait=!0,setTimeout(function(){z.requireWait=!1;z.scriptCount||z.resume();d.checkReadyState()},0)}})();
34 |
--------------------------------------------------------------------------------
/requireLICENSE:
--------------------------------------------------------------------------------
1 | RequireJS is released under two licenses: new BSD, and MIT. You may pick the
2 | license that best suits your development needs. The text of both licenses are
3 | provided below.
4 |
5 |
6 | The "New" BSD License:
7 | ----------------------
8 |
9 | Copyright (c) 2010-2014, The Dojo Foundation
10 | All rights reserved.
11 |
12 | Redistribution and use in source and binary forms, with or without
13 | modification, are permitted provided that the following conditions are met:
14 |
15 | * Redistributions of source code must retain the above copyright notice, this
16 | list of conditions and the following disclaimer.
17 | * Redistributions in binary form must reproduce the above copyright notice,
18 | this list of conditions and the following disclaimer in the documentation
19 | and/or other materials provided with the distribution.
20 | * Neither the name of the Dojo Foundation nor the names of its contributors
21 | may be used to endorse or promote products derived from this software
22 | without specific prior written permission.
23 |
24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
25 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
28 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 |
35 |
36 |
37 | MIT License
38 | -----------
39 |
40 | Copyright (c) 2010-2014, The Dojo Foundation
41 |
42 | Permission is hereby granted, free of charge, to any person obtaining a copy
43 | of this software and associated documentation files (the "Software"), to deal
44 | in the Software without restriction, including without limitation the rights
45 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
46 | copies of the Software, and to permit persons to whom the Software is
47 | furnished to do so, subject to the following conditions:
48 |
49 | The above copyright notice and this permission notice shall be included in
50 | all copies or substantial portions of the Software.
51 |
52 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
53 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
54 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
55 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
56 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
57 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
58 | THE SOFTWARE.
59 |
--------------------------------------------------------------------------------
/tones.js:
--------------------------------------------------------------------------------
1 | (function(window) {
2 | var tones = {
3 | context: new (window.AudioContext || window.webkitAudioContext)(),
4 | attack: 1,
5 | release: 100,
6 | volume: 1,
7 | type: "sine",
8 |
9 |
10 | playFrequency: function(freq) {
11 | this.attack = this.attack || 1;
12 | this.release = this.release || 1;
13 | var envelope = this.context.createGain();
14 | envelope.gain.setValueAtTime(this.volume, this.context.currentTime);
15 | envelope.connect(this.context.destination);
16 |
17 | envelope.gain.setValueAtTime(0, this.context.currentTime);
18 | envelope.gain.setTargetAtTime(this.volume, this.context.currentTime, this.attack / 1000);
19 | if(this.release) {
20 | envelope.gain.setTargetAtTime(0, this.context.currentTime + this.attack / 1000, this.release / 1000);
21 | setTimeout(function() {
22 | osc.stop();
23 | osc.disconnect(envelope);
24 | envelope.gain.cancelScheduledValues(tones.context.currentTime);
25 | envelope.disconnect(tones.context.destination);
26 |
27 | }, this.attack * 10 + this.release * 10);
28 | }
29 |
30 | var osc = this.context.createOscillator();
31 | osc.frequency.setValueAtTime(freq, this.context.currentTime);
32 | osc.type = this.type;
33 | osc.connect(envelope);
34 | osc.start();
35 | },
36 |
37 | /**
38 | * Usage:
39 | * notes.play(440); // plays 440 hz tone
40 | * notes.play("c"); // plays note c in default 4th octave
41 | * notes.play("c#"); // plays note c sharp in default 4th octave
42 | * notes.play("eb"); // plays note e flat in default 4th octave
43 | * notes.play("c", 2); // plays note c in 2nd octave
44 | */
45 | play: function(freqOrNote, octave) {
46 | if(typeof freqOrNote === "number") {
47 | this.playFrequency(freqOrNote);
48 | }
49 | else if(typeof freqOrNote === "string") {
50 | if(octave == null) {
51 | octave = 4;
52 | }
53 | this.playFrequency(this.map[octave][freqOrNote.toLowerCase()]);
54 | }
55 | },
56 |
57 | getTimeMS: function() {
58 | return this.context.currentTime * 1000;
59 | },
60 |
61 | map: [{
62 | // octave 0
63 | "c": 16.351,
64 | "c#": 17.324,
65 | "db": 17.324,
66 | "d": 18.354,
67 | "d#": 19.445,
68 | "eb": 19.445,
69 | "e": 20.601,
70 | "f": 21.827,
71 | "f#": 23.124,
72 | "gb": 23.124,
73 | "g": 24.499,
74 | "g#": 25.956,
75 | "ab": 25.956,
76 | "a": 27.5,
77 | "a#": 29.135,
78 | "bb": 29.135,
79 | "b": 30.868
80 | },
81 | {
82 | // octave 1
83 | "c": 32.703,
84 | "c#": 34.648,
85 | "db": 34.648,
86 | "d": 36.708,
87 | "d#": 38.891,
88 | "eb": 38.891,
89 | "e": 41.203,
90 | "f": 43.654,
91 | "f#": 46.249,
92 | "gb": 46.249,
93 | "g": 48.999,
94 | "g#": 51.913,
95 | "ab": 51.913,
96 | "a": 55,
97 | "a#": 58.27,
98 | "bb": 58.27,
99 | "b": 61.735
100 | },
101 | {
102 | // octave 2
103 | "c": 65.406,
104 | "c#": 69.296,
105 | "db": 69.296,
106 | "d": 73.416,
107 | "d#": 77.782,
108 | "eb": 77.782,
109 | "e": 82.407,
110 | "f": 87.307,
111 | "f#": 92.499,
112 | "gb": 92.499,
113 | "g": 97.999,
114 | "g#": 103.826,
115 | "ab": 103.826,
116 | "a": 110,
117 | "a#": 116.541,
118 | "bb": 116.541,
119 | "b": 123.471
120 | },
121 | {
122 | // octave 3
123 | "c": 130.813,
124 | "c#": 138.591,
125 | "db": 138.591,
126 | "d": 146.832,
127 | "d#": 155.563,
128 | "eb": 155.563,
129 | "e": 164.814,
130 | "f": 174.614,
131 | "f#": 184.997,
132 | "gb": 184.997,
133 | "g": 195.998,
134 | "g#": 207.652,
135 | "ab": 207.652,
136 | "a": 220,
137 | "a#": 233.082,
138 | "bb": 233.082,
139 | "b": 246.942
140 | },
141 | {
142 | // octave 4
143 | "c": 261.626,
144 | "c#": 277.183,
145 | "db": 277.183,
146 | "d": 293.665,
147 | "d#": 311.127,
148 | "eb": 311.127,
149 | "e": 329.628,
150 | "f": 349.228,
151 | "f#": 369.994,
152 | "gb": 369.994,
153 | "g": 391.995,
154 | "g#": 415.305,
155 | "ab": 415.305,
156 | "a": 440,
157 | "a#": 466.164,
158 | "bb": 466.164,
159 | "b": 493.883
160 | },
161 | {
162 | // octave 5
163 | "c": 523.251,
164 | "c#": 554.365,
165 | "db": 554.365,
166 | "d": 587.33,
167 | "d#": 622.254,
168 | "eb": 622.254,
169 | "e": 659.255,
170 | "f": 698.456,
171 | "f#": 739.989,
172 | "gb": 739.989,
173 | "g": 783.991,
174 | "g#": 830.609,
175 | "ab": 830.609,
176 | "a": 880,
177 | "a#": 932.328,
178 | "bb": 932.328,
179 | "b": 987.767
180 | },
181 | {
182 | // octave 6
183 | "c": 1046.502,
184 | "c#": 1108.731,
185 | "db": 1108.731,
186 | "d": 1174.659,
187 | "d#": 1244.508,
188 | "eb": 1244.508,
189 | "e": 1318.51,
190 | "f": 1396.913,
191 | "f#": 1479.978,
192 | "gb": 1479.978,
193 | "g": 1567.982,
194 | "g#": 1661.219,
195 | "ab": 1661.219,
196 | "a": 1760,
197 | "a#": 1864.655,
198 | "bb": 1864.655,
199 | "b": 1975.533
200 | },
201 | {
202 | // octave 7
203 | "c": 2093.005,
204 | "c#": 2217.461,
205 | "db": 2217.461,
206 | "d": 2349.318,
207 | "d#": 2489.016,
208 | "eb": 2489.016,
209 | "e": 2637.021,
210 | "f": 2793.826,
211 | "f#": 2959.955,
212 | "gb": 2959.955,
213 | "g": 3135.964,
214 | "g#": 3322.438,
215 | "ab": 3322.438,
216 | "a": 3520,
217 | "a#": 3729.31,
218 | "bb": 3729.31,
219 | "b": 3951.066
220 | },
221 | {
222 | // octave 8
223 | "c": 4186.009,
224 | "c#": 4434.922,
225 | "db": 4434.922,
226 | "d": 4698.636,
227 | "d#": 4978.032,
228 | "eb": 4978.032,
229 | "e": 5274.042,
230 | "f": 5587.652,
231 | "f#": 5919.91,
232 | "gb": 5919.91,
233 | "g": 6271.928,
234 | "g#": 6644.876,
235 | "ab": 6644.876,
236 | "a": 7040,
237 | "a#": 7458.62,
238 | "bb": 7458.62,
239 | "b": 7902.132
240 | },
241 | {
242 | // octave 9
243 | "c": 8372.018,
244 | "c#": 8869.844,
245 | "db": 8869.844,
246 | "d": 9397.272,
247 | "d#": 9956.064,
248 | "eb": 9956.064,
249 | "e": 10548.084,
250 | "f": 11175.304,
251 | "f#": 11839.82,
252 | "gb": 11839.82,
253 | "g": 12543.856,
254 | "g#": 13289.752,
255 | "ab": 13289.752,
256 | "a": 14080,
257 | "a#": 14917.24,
258 | "bb": 14917.24,
259 | "b": 15804.264
260 | }]
261 | };
262 |
263 | // need to create a node in order to kick off the timer in Chrome.
264 | tones.context.createGain();
265 |
266 | if (typeof define === "function" && define.amd) {
267 | define(tones);
268 | } else {
269 | window.tones = tones;
270 | }
271 |
272 | }(window));
--------------------------------------------------------------------------------