Sunteți pe pagina 1din 6

1 var Synth, AudioSynth, AudioSynthInstrument;

2 !function(){
3 var _encapsulated = false;
4 var AudioSynthInstance = null;
5 var pack = function(c,arg){return [String.fromCharCode(arg&255,(arg>>8)&255),
String.fromCharCode(arg&255,(arg>>8)&255,(arg>>16)&255,(arg>>24)&255)][c];};
6 var setPrivateVar = function(n,v,w,e){Object.defineProperty(this,n,{value:v,
writable:!!w,enumerable:!!e});};
7 var setPublicVar = function(n,v,w){setPrivateVar.call(this,n,v,w,true);};
8 AudioSynthInstrument = function AudioSynthInstrument(){this.__init__.apply(this,
arguments);};
9 var setPriv = setPrivateVar.bind(AudioSynthInstrument.prototype);
10 var setPub = setPublicVar.bind(AudioSynthInstrument.prototype);
11 setPriv('__init__', function(a,b,c) {
12 if(!_encapsulated) { throw new Error('AudioSynthInstrument can only be
instantiated from the createInstrument method of the AudioSynth object.'); }
13 setPrivateVar.call(this, '_parent', a);
14 setPublicVar.call(this, 'name', b);
15 setPrivateVar.call(this, '_soundID', c);
16 });
17 setPub('play', function(note, octave, duration) {
18 return this._parent.play(this._soundID, note, octave, duration);
19 });
20 setPub('generate', function(note, octave, duration) {
21 return this._parent.generate(this._soundID, note, octave, duration);
22 });
23 AudioSynth = function AudioSynth(){if(AudioSynthInstance instanceof AudioSynth){
return AudioSynthInstance;}else{ this.__init__(); return this; }};
24 setPriv = setPrivateVar.bind(AudioSynth.prototype);
25 setPub = setPublicVar.bind(AudioSynth.prototype);
26 setPriv('_debug',false,true);
27 setPriv('_bitsPerSample',16);
28 setPriv('_channels',1);
29 setPriv('_sampleRate',44100,true);
30 setPub('setSampleRate', function(v) {
31 this._sampleRate = Math.max(Math.min(v|0,44100), 4000);
32 this._clearCache();
33 return this._sampleRate;
34 });
35 setPub('getSampleRate', function() { return this._sampleRate; });
36 setPriv('_volume',32768,true);
37 setPub('setVolume', function(v) {
38 v = parseFloat(v); if(isNaN(v)) { v = 0; }
39 v = Math.round(v*32768);
40 this._volume = Math.max(Math.min(v|0,32768), 0);
41 this._clearCache();
42 return this._volume;
43 });
44 setPub('getVolume', function() { return Math.round(this._volume/32768*10000)/
10000; });
45 setPriv('_notes',{'C':261.63,'C#':277.18,'D':293.66,'D#':311.13,'E':329.63,'F':
346.23,'F#':369.99,'G':392.00,'G#':415.30,'A':440.00,'A#':466.16,'B':493.88});
46 setPriv('_fileCache',[],true);
47 setPriv('_temp',{},true);
48 setPriv('_sounds',[],true);
49 setPriv('_mod',[function(i,s,f,x){return Math.sin((2 * Math.PI)*(i/s)*f+x);}]);
50 setPriv('_resizeCache', function() {
51 var f = this._fileCache;
52 var l = this._sounds.length;
53 while(f.length<l) {
54 var octaveList = [];
55 for(var i = 0; i < 8; i++) {
56 var noteList = {};
57 for(var k in this._notes) {
58 noteList[k] = {};
59 }
60 octaveList.push(noteList);
61 }
62 f.push(octaveList);
63 }
64 });
65 setPriv('_clearCache', function() {
66 this._fileCache = [];
67 this._resizeCache();
68 });
69 setPub('generate', function(sound, note, octave, duration) {
70 var thisSound = this._sounds[sound];
71 if(!thisSound) {
72 for(var i=0;i<this._sounds.length;i++) {
73 if(this._sounds[i].name==sound) {
74 thisSound = this._sounds[i];
75 sound = i;
76 break;
77 }
78 }
79 }
80 if(!thisSound) { throw new Error('Invalid sound or sound ID: ' + sound); }
81 var t = (new Date).valueOf();
82 this._temp = {};
83 octave |= 0;
84 octave = Math.min(8, Math.max(1, octave));
85 var time = !duration?2:parseFloat(duration);
86 if(typeof(this._notes[note])=='undefined') { throw new Error(note + ' is not
a valid note.'); }
87 if(typeof(this._fileCache[sound][octave-1][note][time])!='undefined') {
88 if(this._debug) { console.log((new Date).valueOf() - t, 'ms to retrieve
(cached)'); }
89 return this._fileCache[sound][octave-1][note][time];
90 } else {
91 var frequency = this._notes[note] * Math.pow(2,octave-4);
92 var data = [];
93 var sampleRate = this._sampleRate;
94 var volume = this._volume;
95 var channels = this._channels;
96 var bitsPerSample = this._bitsPerSample;
97 var attack = thisSound.attack(sampleRate, frequency, volume);
98 var dampen = thisSound.dampen(sampleRate, frequency, volume);
99 var wave = thisSound.wave.bind({modulate: this._mod, vars: this._temp});
100 var val = 0;
101 var curVol = 0;
102
103 for (var i = 0; i < (sampleRate * time); i++) {
104 if(i<=sampleRate*attack) {
105 curVol = volume * (i/(sampleRate*attack));
106 } else {
107 curVol = volume * Math.pow((1-((i-(sampleRate*attack))/(
sampleRate*(time-attack)))),dampen);
108 }
109
110 val = curVol * Math.min(Math.max(wave(i, sampleRate, frequency,
volume), -1), 1);
111 val = String.fromCharCode(val&255, (val>>>8)&255);
112 data.push(val);
113 }
114
115 data = data.join('');
116
117 // Format sub-chunk
118 var chunk1 = [
119 'fmt ', // Sub-chunk identifier
120 pack(1, 16), // Chunk length
121 pack(0, 1), // Audio format (1 is linear quantization)
122 pack(0, channels),
123 pack(1, sampleRate),
124 pack(1, sampleRate * channels * bitsPerSample / 8), // Byte rate
125 pack(0, channels * bitsPerSample / 8),
126 pack(0, bitsPerSample)
127 ].join('');
128 // Data sub-chunk (contains the sound)
129 var chunk2 = [
130 'data', // Sub-chunk identifier
131 pack(1, data.length * channels * bitsPerSample / 8), // Chunk length
132 data
133 ].join('');
134 // Header
135 var header = [
136 'RIFF',
137 pack(1, 4 + (8 + chunk1.length) + (8 + chunk2.length)), // Length
138 'WAVE'
139 ].join('');
140 var out = [header, chunk1, chunk2].join('');
141 var dataURI = 'data:audio/wav;base64,' + escape(window.btoa(out));
142 this._fileCache[sound][octave-1][note][time] = dataURI;
143 if(this._debug) { console.log((new Date).valueOf() - t, 'ms to generate'
); }
144 return dataURI;
145 }
146 });
147 setPub('play', function(note, octave, duration) {
148 var src = this.generate(note, octave, duration);
149 var audio = new Audio(src);
150 audio.addEventListener('ended', function() { audio = null; });
151 audio.autoplay = true;
152 audio.setAttribute('type', 'audio/wav');
153 return true;
154 });
155 setPub('debug', function() { this._debug = true; });
156 setPub('createInstrument', function(sound) {
157 var n = 0;
158 var found = false;
159 if(typeof(sound)=='string') {
160 for(var i=0;i<this._sounds.length;i++) {
161 if(this._sounds[i].name==sound) {
162 found = true;
163 n = i;
164 break;
165 }
166 }
167 } else {
168 if(this._sounds[sound]) {
169 n = sound;
170 sound = this._sounds[n].name;
171 found = true;
172 }
173 }
174 if(!found) { throw new Error('Invalid sound or sound ID: ' + sound); }
175 _encapsulated = true;
176 var ins = new AudioSynthInstrument(this, sound, n);
177 _encapsulated = false;
178 return ins;
179 });
180 setPub('listSounds', function() {
181 var r = [];
182 for(var i=0;i<this._sounds.length;i++) {
183 r.push(this._sounds[i].name);
184 }
185 return r;
186 });
187 setPriv('__init__', function(){
188 this._resizeCache();
189 });
190 setPub('loadSoundProfile', function() {
191 for(var i=0,len=arguments.length;i<len;i++) {
192 o = arguments[i];
193 if(!(o instanceof Object)) { throw new Error('Invalid sound profile.'); }
194 this._sounds.push(o);
195 }
196 this._resizeCache();
197 return true;
198 });
199 setPub('loadModulationFunction', function() {
200 for(var i=0,len=arguments.length;i<len;i++) {
201 f = arguments[i];
202 if(typeof(f)!='function') { throw new Error('Invalid modulation
function.'); }
203 this._mod.push(f);
204 }
205 return true;
206 });
207 AudioSynthInstance = new AudioSynth();
208 Synth = AudioSynthInstance;
209 }();
210
211 Synth.loadModulationFunction(
212 function(i, sampleRate, frequency, x) { return 1 * Math.sin(2 * Math.PI * ((i /
sampleRate) * frequency) + x); },
213 function(i, sampleRate, frequency, x) { return 1 * Math.sin(4 * Math.PI * ((i /
sampleRate) * frequency) + x); },
214 function(i, sampleRate, frequency, x) { return 1 * Math.sin(8 * Math.PI * ((i /
sampleRate) * frequency) + x); },
215 function(i, sampleRate, frequency, x) { return 1 * Math.sin(0.5 * Math.PI * ((i /
sampleRate) * frequency) + x); },
216 function(i, sampleRate, frequency, x) { return 1 * Math.sin(0.25 * Math.PI * ((i
/ sampleRate) * frequency) + x); },
217 function(i, sampleRate, frequency, x) { return 0.5 * Math.sin(2 * Math.PI * ((i /
sampleRate) * frequency) + x); },
218 function(i, sampleRate, frequency, x) { return 0.5 * Math.sin(4 * Math.PI * ((i /
sampleRate) * frequency) + x); },
219 function(i, sampleRate, frequency, x) { return 0.5 * Math.sin(8 * Math.PI * ((i /
sampleRate) * frequency) + x); },
220 function(i, sampleRate, frequency, x) { return 0.5 * Math.sin(0.5 * Math.PI * ((i
/ sampleRate) * frequency) + x); },
221 function(i, sampleRate, frequency, x) { return 0.5 * Math.sin(0.25 * Math.PI * ((
i / sampleRate) * frequency) + x); }
222 );
223
224 Synth.loadSoundProfile({
225 name: 'piano',
226 attack: function() { return 0.002; },
227 dampen: function(sampleRate, frequency, volume) {
228 return Math.pow(0.5*Math.log((frequency*volume)/sampleRate),2);
229 },
230 wave: function(i, sampleRate, frequency, volume) {
231 var base = this.modulate[0];
232 return this.modulate[1](
233 i,
234 sampleRate,
235 frequency,
236 Math.pow(base(i, sampleRate, frequency, 0), 2) +
237 (0.75 * base(i, sampleRate, frequency, 0.25)) +
238 (0.1 * base(i, sampleRate, frequency, 0.5))
239 );
240 }
241 },
242 {
243 name: 'organ',
244 attack: function() { return 0.3 },
245 dampen: function(sampleRate, frequency) { return 1+(frequency * 0.01); },
246 wave: function(i, sampleRate, frequency) {
247 var base = this.modulate[0];
248 return this.modulate[1](
249 i,
250 sampleRate,
251 frequency,
252 base(i, sampleRate, frequency, 0) +
253 0.5*base(i, sampleRate, frequency, 0.25) +
254 0.25*base(i, sampleRate, frequency, 0.5)
255 );
256 }
257 },
258 {
259 name: 'acoustic',
260 attack: function() { return 0.002; },
261 dampen: function() { return 1; },
262 wave: function(i, sampleRate, frequency) {
263
264 var vars = this.vars;
265 vars.valueTable = !vars.valueTable?[]:vars.valueTable;
266 if(typeof(vars.playVal)=='undefined') { vars.playVal = 0; }
267 if(typeof(vars.periodCount)=='undefined') { vars.periodCount = 0; }
268
269 var valueTable = vars.valueTable;
270 var playVal = vars.playVal;
271 var periodCount = vars.periodCount;
272
273 var period = sampleRate/frequency;
274 var p_hundredth = Math.floor((period-Math.floor(period))*100);
275
276 var resetPlay = false;
277
278 if(valueTable.length<=Math.ceil(period)) {
279
280 valueTable.push(Math.round(Math.random())*2-1);
281
282 return valueTable[valueTable.length-1];
283
284 } else {
285
286 valueTable[playVal] = (valueTable[playVal>=(valueTable.length-1)?0:
playVal+1] + valueTable[playVal]) * 0.5;
287
288 if(playVal>=Math.floor(period)) {
289 if(playVal<Math.ceil(period)) {
290 if((periodCount%100)>=p_hundredth) {
291 // Reset
292 resetPlay = true;
293 valueTable[playVal+1] = (valueTable[0] + valueTable[playVal+1
]) * 0.5;
294 vars.periodCount++;
295 }
296 } else {
297 resetPlay = true;
298 }
299 }
300
301 var _return = valueTable[playVal];
302 if(resetPlay) { vars.playVal = 0; } else { vars.playVal++; }
303
304 return _return;
305
306 }
307 }
308 },
309 {
310 name: 'edm',
311 attack: function() { return 0.002; },
312 dampen: function() { return 1; },
313 wave: function(i, sampleRate, frequency) {
314 var base = this.modulate[0];
315 var mod = this.modulate.slice(1);
316 return mod[0](
317 i,
318 sampleRate,
319 frequency,
320 mod[9](
321 i,
322 sampleRate,
323 frequency,
324 mod[2](
325 i,
326 sampleRate,
327 frequency,
328 Math.pow(base(i, sampleRate, frequency, 0), 3) +
329 Math.pow(base(i, sampleRate, frequency, 0.5), 5) +
330 Math.pow(base(i, sampleRate, frequency, 1), 7)
331 )
332 ) +
333 mod[8](
334 i,
335 sampleRate,
336 frequency,
337 base(i, sampleRate, frequency, 1.75)
338 )
339 );
340 }
341 });

S-ar putea să vă placă și