databender 1.0.4 → 2.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2639 +1,211 @@
1
1
  var Databender = (function () {
2
- 'use strict';
2
+ 'use strict';
3
3
 
4
- var random = function (x, y) {
5
- return x+(y-x+1)*crypto.getRandomValues(new Uint32Array(1))[0]/2**32|0
6
- };
4
+ const isFunction = (candidate) => typeof candidate === 'function';
7
5
 
8
- var random$1 = /*#__PURE__*/Object.freeze({
9
- default: random,
10
- __moduleExports: random
11
- });
6
+ const isConnectable = (candidate) => candidate && typeof candidate.connect === 'function';
12
7
 
13
- var random$2 = ( random$1 && random ) || random$1;
8
+ const isPromise = (candidate) => candidate && typeof candidate.then === 'function';
14
9
 
15
- var biquad = (config, offlineAudioCtx, bufferSource) => {
16
- if (config.biquad.randomize) {
17
- var waveArray = new Float32Array(config.biquad.randomValues);
18
- for (let i=0;i<config.biquad.randomValues;i++) {
19
- waveArray[i] = random$2(0.0001, config.biquad.biquadFrequency);
20
- }
21
- }
22
- var biquadFilter = offlineAudioCtx.createBiquadFilter();
23
- biquadFilter.type = config.biquad.type;
24
- if (config.biquad.randomize) {
25
- biquadFilter.frequency.cancelScheduledValues(0);
26
- biquadFilter.frequency.setValueCurveAtTime(waveArray, 0, bufferSource.buffer.duration);
27
- biquadFilter.detune.setValueCurveAtTime(waveArray, 0, bufferSource.buffer.duration);
28
- } else if (config.biquad.enablePartial) {
29
- biquadFilter.frequency.cancelScheduledValues(0);
30
- biquadFilter.frequency.setTargetAtTime(config.biquad.biquadFrequency, config.biquad.areaOfEffect, config.biquad.areaOfEffect);
31
- } else {
32
- biquadFilter.frequency.cancelScheduledValues(0);
33
- biquadFilter.frequency.value = config.biquad.biquadFrequency;
34
- } biquadFilter.Q.value = config.biquad.quality;
35
- biquadFilter.detune.cancelScheduledValues(0);
36
- biquadFilter.detune.value = config.biquad.detune;
37
- return biquadFilter;
38
- };
39
-
40
- var biquad$1 = /*#__PURE__*/Object.freeze({
41
- default: biquad,
42
- __moduleExports: biquad
43
- });
44
-
45
- var bitcrusher = (config, tuna) => {
46
- return new tuna.Bitcrusher({
47
- bits: config.bitcrusher.bits,
48
- normfreq: config.bitcrusher.normfreq,
49
- bufferSize: config.bitcrusher.bufferSize
50
- });
51
- };
52
-
53
- var bitcrusher$1 = /*#__PURE__*/Object.freeze({
54
- default: bitcrusher,
55
- __moduleExports: bitcrusher
56
- });
57
-
58
- var chorus = (config, tuna) => {
59
- return new tuna.Chorus({
60
- feedback: config.chorus.feedback,
61
- delay: config.chorus.delay,
62
- depth: config.chorus.depth,
63
- rate: config.chorus.rate,
64
- });
65
- };
66
-
67
- var chorus$1 = /*#__PURE__*/Object.freeze({
68
- default: chorus,
69
- __moduleExports: chorus
70
- });
71
-
72
- var convolver = (config, tuna) => {
73
- return new tuna.Convolver({
74
- highCut: config.convolver.highCut,
75
- lowCut: config.convolver.lowCut,
76
- dryLevel: config.convolver.dryLevel,
77
- wetLevel: config.convolver.wetLevel,
78
- level: config.convolver.level,
79
- impulse: config.convolver.impulse
80
- });
81
- };
82
-
83
- var convolver$1 = /*#__PURE__*/Object.freeze({
84
- default: convolver,
85
- __moduleExports: convolver
86
- });
87
-
88
- var detune = (config, tuna, bufferSource) => {
89
- if (config.detune.randomize) {
90
- var waveArray = new Float32Array(config.detune.randomValues);
91
- for (let i=0;i<config.detune.randomValues;i++) {
92
- waveArray[i] = random$2(0.0001, 400);
93
- }
94
- }
95
- if (config.detune.randomize) {
96
- bufferSource.detune.setValueCurveAtTime(waveArray, 0, bufferSource.buffer.duration);
97
- } else if (config.detune.enablePartial) {
98
- bufferSource.detune.setTargetAtTime(config.detune.value, config.detune.areaOfEffect, config.detune.areaOfEffect);
99
- } else {
100
- bufferSource.detune.value = config.detune.value;
101
- } return bufferSource;
102
- };
103
-
104
- var detune$1 = /*#__PURE__*/Object.freeze({
105
- default: detune,
106
- __moduleExports: detune
107
- });
108
-
109
- var gain = (config) => {
110
- const gainNode = offlineAudioCtx.createGain();
111
- gainNode.gain.value = config.gain.value;
112
- return gainNode;
113
- };
114
-
115
- var gain$1 = /*#__PURE__*/Object.freeze({
116
- default: gain,
117
- __moduleExports: gain
118
- });
119
-
120
- var phaser = (config, tuna) => {
121
- return new tuna.Phaser({
122
- rate: config.phaser.rate,
123
- depth: config.phaser.depth,
124
- feedback: config.phaser.feedback,
125
- stereoPhase: config.phaser.stereoPhase,
126
- baseModulationFrequency: config.phaser.baseModulationFrequency
127
- });
128
- };
129
-
130
- var phaser$1 = /*#__PURE__*/Object.freeze({
131
- default: phaser,
132
- __moduleExports: phaser
133
- });
134
-
135
- var pingPong = (config, tuna) => {
136
- return new tuna.PingPongDelay({
137
- wetLevel: config.pingPong.wetLevel,
138
- feedback: config.pingPong.feedback,
139
- delayTimeLeft: config.pingPong.delayTimeLeft,
140
- delayTimeRight: config.pingPong.delayTimeRight
141
- });
142
- };
143
-
144
- var pingPong$1 = /*#__PURE__*/Object.freeze({
145
- default: pingPong,
146
- __moduleExports: pingPong
147
- });
148
-
149
- var playbackRate = (config, tuna, bufferSource) => {
150
- if (config.playbackRate.randomize) {
151
- var waveArray = new Float32Array(config.playbackRate.randomValues);
152
- for (i=0;i<config.playbackRate.randomValues;i++) {
153
- waveArray[i] = window.random.integer(0.0001, 8);
154
- }
155
- bufferSource.playbackRate.setValueCurveAtTime(waveArray, 0, bufferSource.buffer.duration);
156
- } else if (config.playbackRate.enablePartial) {
157
- bufferSource.playbackRate.setTargetAtTime(config.playbackRate.value, config.playbackRate.areaOfEffect, config.playbackRate.areaOfEffect);
158
- } else {
159
- bufferSource.playbackRate.value = config.playbackRate.value;
160
- } return bufferSource;
161
- };
162
-
163
- var playbackRate$1 = /*#__PURE__*/Object.freeze({
164
- default: playbackRate,
165
- __moduleExports: playbackRate
166
- });
167
-
168
- var require$$0 = ( biquad$1 && biquad ) || biquad$1;
169
-
170
- var require$$1 = ( bitcrusher$1 && bitcrusher ) || bitcrusher$1;
171
-
172
- var require$$2 = ( chorus$1 && chorus ) || chorus$1;
173
-
174
- var require$$3 = ( convolver$1 && convolver ) || convolver$1;
175
-
176
- var require$$4 = ( detune$1 && detune ) || detune$1;
177
-
178
- var require$$5 = ( gain$1 && gain ) || gain$1;
179
-
180
- var require$$6 = ( phaser$1 && phaser ) || phaser$1;
181
-
182
- var require$$7 = ( pingPong$1 && pingPong ) || pingPong$1;
183
-
184
- var require$$8 = ( playbackRate$1 && playbackRate ) || playbackRate$1;
185
-
186
- var biquad$2 = require$$0;
187
- var bitcrusher$2 = require$$1;
188
- var chorus$2 = require$$2;
189
- var convolver$2 = require$$3;
190
- var detune$2 = require$$4;
191
- var gain$2 = require$$5;
192
- var phaser$2 = require$$6;
193
- var pingPong$2 = require$$7;
194
- var playbackRate$2 = require$$8;
195
-
196
- var effects = {
197
- biquad: biquad$2,
198
- bitcrusher: bitcrusher$2,
199
- chorus: chorus$2,
200
- convolver: convolver$2,
201
- detune: detune$2,
202
- gain: gain$2,
203
- phaser: phaser$2,
204
- pingPong: pingPong$2,
205
- playbackRate: playbackRate$2
206
- };
207
-
208
- var effects$1 = /*#__PURE__*/Object.freeze({
209
- default: effects,
210
- __moduleExports: effects,
211
- biquad: biquad$2,
212
- bitcrusher: bitcrusher$2,
213
- chorus: chorus$2,
214
- convolver: convolver$2,
215
- detune: detune$2,
216
- gain: gain$2,
217
- phaser: phaser$2,
218
- pingPong: pingPong$2,
219
- playbackRate: playbackRate$2
220
- });
10
+ const normalizeEffectNode = (candidate) => {
11
+ if (!candidate) {
12
+ return null;
13
+ }
221
14
 
222
- function createCommonjsModule(fn, module) {
223
- return module = { exports: {} }, fn(module, module.exports), module.exports;
224
- }
15
+ const input = isConnectable(candidate.input) ? candidate.input : candidate;
16
+ const output = isConnectable(candidate.output) ? candidate.output : input;
225
17
 
226
- var tuna = createCommonjsModule(function (module) {
227
- /*
228
- Copyright (c) 2012 DinahMoe AB & Oskar Eriksson
229
-
230
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
231
- files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
232
- modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
233
- is furnished to do so, subject to the following conditions:
234
-
235
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
236
-
237
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
238
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
239
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
240
- OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
241
- */
242
- /*global module*/
243
- (function() {
244
-
245
- var userContext,
246
- userInstance,
247
- pipe = function(param, val) {
248
- param.value = val;
249
- },
250
- Super = Object.create(null, {
251
- activate: {
252
- writable: true,
253
- value: function(doActivate) {
254
- if (doActivate) {
255
- this.input.disconnect();
256
- this.input.connect(this.activateNode);
257
- if (this.activateCallback) {
258
- this.activateCallback(doActivate);
259
- }
260
- } else {
261
- this.input.disconnect();
262
- this.input.connect(this.output);
263
- }
264
- }
265
- },
266
- bypass: {
267
- get: function() {
268
- return this._bypass;
269
- },
270
- set: function(value) {
271
- if (this._lastBypassValue === value) {
272
- return;
273
- }
274
- this._bypass = value;
275
- this.activate(!value);
276
- this._lastBypassValue = value;
277
- }
278
- },
279
- connect: {
280
- value: function(target) {
281
- this.output.connect(target);
282
- }
283
- },
284
- disconnect: {
285
- value: function(target) {
286
- this.output.disconnect(target);
287
- }
288
- },
289
- connectInOrder: {
290
- value: function(nodeArray) {
291
- var i = nodeArray.length - 1;
292
- while (i--) {
293
- if (!nodeArray[i].connect) {
294
- return console.error("AudioNode.connectInOrder: TypeError: Not an AudioNode.", nodeArray[i]);
295
- }
296
- if (nodeArray[i + 1].input) {
297
- nodeArray[i].connect(nodeArray[i + 1].input);
298
- } else {
299
- nodeArray[i].connect(nodeArray[i + 1]);
300
- }
301
- }
302
- }
303
- },
304
- getDefaults: {
305
- value: function() {
306
- var result = {};
307
- for (var key in this.defaults) {
308
- result[key] = this.defaults[key].value;
309
- }
310
- return result;
311
- }
312
- },
313
- automate: {
314
- value: function(property, value, duration, startTime) {
315
- var start = startTime ? ~~(startTime / 1000) : userContext.currentTime,
316
- dur = duration ? ~~(duration / 1000) : 0,
317
- _is = this.defaults[property],
318
- param = this[property],
319
- method;
320
-
321
- if (param) {
322
- if (_is.automatable) {
323
- if (!duration) {
324
- method = "setValueAtTime";
325
- } else {
326
- method = "linearRampToValueAtTime";
327
- param.cancelScheduledValues(start);
328
- param.setValueAtTime(param.value, start);
329
- }
330
- param[method](value, dur + start);
331
- } else {
332
- param = value;
333
- }
334
- } else {
335
- console.error("Invalid Property for " + this.name);
336
- }
337
- }
338
- }
339
- }),
340
- FLOAT = "float",
341
- BOOLEAN = "boolean",
342
- STRING = "string",
343
- INT = "int";
344
-
345
- if (module.exports) {
346
- module.exports = Tuna;
347
- } else if (typeof undefined === "function") {
348
- window.define("Tuna", definition);
349
- } else {
350
- window.Tuna = Tuna;
351
- }
352
-
353
- function definition() {
354
- return Tuna;
355
- }
356
-
357
- function Tuna(context) {
358
- if (!(this instanceof Tuna)) {
359
- return new Tuna(context);
360
- }
361
-
362
- var _window = typeof window === "undefined" ? {} : window;
363
-
364
- if (!_window.AudioContext) {
365
- _window.AudioContext = _window.webkitAudioContext;
366
- }
367
- if (!context) {
368
- console.log("tuna.js: Missing audio context! Creating a new context for you.");
369
- context = _window.AudioContext && (new _window.AudioContext());
370
- }
371
- if (!context) {
372
- throw new Error("Tuna cannot initialize because this environment does not support web audio.");
373
- }
374
- connectify(context);
375
- userContext = context;
376
- userInstance = this;
377
- }
378
-
379
- function connectify(context) {
380
- if (context.__connectified__ === true) return;
381
-
382
- var gain = context.createGain(),
383
- proto = Object.getPrototypeOf(Object.getPrototypeOf(gain)),
384
- oconnect = proto.connect;
385
-
386
- proto.connect = shimConnect;
387
- context.__connectified__ = true; // Prevent overriding connect more than once
388
-
389
- function shimConnect() {
390
- var node = arguments[0];
391
- arguments[0] = Super.isPrototypeOf ? (Super.isPrototypeOf(node) ? node.input : node) : (node.input || node);
392
- oconnect.apply(this, arguments);
393
- return node;
394
- }
395
- }
396
-
397
- function dbToWAVolume(db) {
398
- return Math.max(0, Math.round(100 * Math.pow(2, db / 6)) / 100);
399
- }
400
-
401
- function fmod(x, y) {
402
- // http://kevin.vanzonneveld.net
403
- // * example 1: fmod(5.7, 1.3);
404
- // * returns 1: 0.5
405
- var tmp, tmp2, p = 0,
406
- pY = 0,
407
- l = 0.0,
408
- l2 = 0.0;
409
-
410
- tmp = x.toExponential().match(/^.\.?(.*)e(.+)$/);
411
- p = parseInt(tmp[2], 10) - (tmp[1] + "").length;
412
- tmp = y.toExponential().match(/^.\.?(.*)e(.+)$/);
413
- pY = parseInt(tmp[2], 10) - (tmp[1] + "").length;
414
-
415
- if (pY > p) {
416
- p = pY;
417
- }
418
-
419
- tmp2 = (x % y);
420
-
421
- if (p < -100 || p > 20) {
422
- // toFixed will give an out of bound error so we fix it like this:
423
- l = Math.round(Math.log(tmp2) / Math.log(10));
424
- l2 = Math.pow(10, l);
425
-
426
- return (tmp2 / l2).toFixed(l - p) * l2;
427
- } else {
428
- return parseFloat(tmp2.toFixed(-p));
429
- }
430
- }
431
-
432
- function sign(x) {
433
- if (x === 0) {
434
- return 1;
435
- } else {
436
- return Math.abs(x) / x;
437
- }
438
- }
439
-
440
- function tanh(n) {
441
- return (Math.exp(n) - Math.exp(-n)) / (Math.exp(n) + Math.exp(-n));
442
- }
443
-
444
- function initValue(userVal, defaultVal) {
445
- return userVal === undefined ? defaultVal : userVal;
446
- }
447
-
448
- Tuna.prototype.Bitcrusher = function(properties) {
449
- if (!properties) {
450
- properties = this.getDefaults();
451
- }
452
- this.bufferSize = properties.bufferSize || this.defaults.bufferSize.value;
453
-
454
- this.input = userContext.createGain();
455
- this.activateNode = userContext.createGain();
456
- this.processor = userContext.createScriptProcessor(this.bufferSize, 1, 1);
457
- this.output = userContext.createGain();
458
-
459
- this.activateNode.connect(this.processor);
460
- this.processor.connect(this.output);
461
-
462
- var phaser = 0,
463
- last = 0,
464
- input, output, step, i, length;
465
- this.processor.onaudioprocess = function(e) {
466
- input = e.inputBuffer.getChannelData(0),
467
- output = e.outputBuffer.getChannelData(0),
468
- step = Math.pow(1 / 2, this.bits);
469
- length = input.length;
470
- for (i = 0; i < length; i++) {
471
- phaser += this.normfreq;
472
- if (phaser >= 1.0) {
473
- phaser -= 1.0;
474
- last = step * Math.floor(input[i] / step + 0.5);
475
- }
476
- output[i] = last;
477
- }
478
- };
479
-
480
- this.bits = properties.bits || this.defaults.bits.value;
481
- this.normfreq = initValue(properties.normfreq, this.defaults.normfreq.value);
482
- this.bypass = properties.bypass || this.defaults.bypass.value;
483
- };
484
- Tuna.prototype.Bitcrusher.prototype = Object.create(Super, {
485
- name: {
486
- value: "Bitcrusher"
487
- },
488
- defaults: {
489
- writable: true,
490
- value: {
491
- bits: {
492
- value: 4,
493
- min: 1,
494
- max: 16,
495
- automatable: false,
496
- type: INT
497
- },
498
- bufferSize: {
499
- value: 4096,
500
- min: 256,
501
- max: 16384,
502
- automatable: false,
503
- type: INT
504
- },
505
- bypass: {
506
- value: false,
507
- automatable: false,
508
- type: BOOLEAN
509
- },
510
- normfreq: {
511
- value: 0.1,
512
- min: 0.0001,
513
- max: 1.0,
514
- automatable: false,
515
- type: FLOAT
516
- }
517
- }
518
- },
519
- bits: {
520
- enumerable: true,
521
- get: function() {
522
- return this.processor.bits;
523
- },
524
- set: function(value) {
525
- this.processor.bits = value;
526
- }
527
- },
528
- normfreq: {
529
- enumerable: true,
530
- get: function() {
531
- return this.processor.normfreq;
532
- },
533
- set: function(value) {
534
- this.processor.normfreq = value;
535
- }
536
- }
537
- });
538
-
539
- Tuna.prototype.Cabinet = function(properties) {
540
- if (!properties) {
541
- properties = this.getDefaults();
542
- }
543
- this.input = userContext.createGain();
544
- this.activateNode = userContext.createGain();
545
- this.convolver = this.newConvolver(properties.impulsePath || "../impulses/impulse_guitar.wav");
546
- this.makeupNode = userContext.createGain();
547
- this.output = userContext.createGain();
548
-
549
- this.activateNode.connect(this.convolver.input);
550
- this.convolver.output.connect(this.makeupNode);
551
- this.makeupNode.connect(this.output);
552
- //don't use makeupGain setter at init to avoid smoothing
553
- this.makeupNode.gain.value = initValue(properties.makeupGain, this.defaults.makeupGain.value);
554
- this.bypass = properties.bypass || this.defaults.bypass.value;
555
- };
556
- Tuna.prototype.Cabinet.prototype = Object.create(Super, {
557
- name: {
558
- value: "Cabinet"
559
- },
560
- defaults: {
561
- writable: true,
562
- value: {
563
- makeupGain: {
564
- value: 1,
565
- min: 0,
566
- max: 20,
567
- automatable: true,
568
- type: FLOAT
569
- },
570
- bypass: {
571
- value: false,
572
- automatable: false,
573
- type: BOOLEAN
574
- }
575
- }
576
- },
577
- makeupGain: {
578
- enumerable: true,
579
- get: function() {
580
- return this.makeupNode.gain;
581
- },
582
- set: function(value) {
583
- this.makeupNode.gain.setTargetAtTime(value, userContext.currentTime, 0.01);
584
- }
585
- },
586
- newConvolver: {
587
- value: function(impulsePath) {
588
- return new userInstance.Convolver({
589
- impulse: impulsePath,
590
- dryLevel: 0,
591
- wetLevel: 1
592
- });
593
- }
594
- }
595
- });
596
-
597
- Tuna.prototype.Chorus = function(properties) {
598
- if (!properties) {
599
- properties = this.getDefaults();
600
- }
601
- this.input = userContext.createGain();
602
- this.attenuator = this.activateNode = userContext.createGain();
603
- this.splitter = userContext.createChannelSplitter(2);
604
- this.delayL = userContext.createDelay();
605
- this.delayR = userContext.createDelay();
606
- this.feedbackGainNodeLR = userContext.createGain();
607
- this.feedbackGainNodeRL = userContext.createGain();
608
- this.merger = userContext.createChannelMerger(2);
609
- this.output = userContext.createGain();
610
-
611
- this.lfoL = new userInstance.LFO({
612
- target: this.delayL.delayTime,
613
- callback: pipe
614
- });
615
- this.lfoR = new userInstance.LFO({
616
- target: this.delayR.delayTime,
617
- callback: pipe
618
- });
619
-
620
- this.input.connect(this.attenuator);
621
- this.attenuator.connect(this.output);
622
- this.attenuator.connect(this.splitter);
623
- this.splitter.connect(this.delayL, 0);
624
- this.splitter.connect(this.delayR, 1);
625
- this.delayL.connect(this.feedbackGainNodeLR);
626
- this.delayR.connect(this.feedbackGainNodeRL);
627
- this.feedbackGainNodeLR.connect(this.delayR);
628
- this.feedbackGainNodeRL.connect(this.delayL);
629
- this.delayL.connect(this.merger, 0, 0);
630
- this.delayR.connect(this.merger, 0, 1);
631
- this.merger.connect(this.output);
632
-
633
- this.feedback = initValue(properties.feedback, this.defaults.feedback.value);
634
- this.rate = initValue(properties.rate, this.defaults.rate.value);
635
- this.delay = initValue(properties.delay, this.defaults.delay.value);
636
- this.depth = initValue(properties.depth, this.defaults.depth.value);
637
- this.lfoR.phase = Math.PI / 2;
638
- this.attenuator.gain.value = 0.6934; // 1 / (10 ^ (((20 * log10(3)) / 3) / 20))
639
- this.lfoL.activate(true);
640
- this.lfoR.activate(true);
641
- this.bypass = properties.bypass || this.defaults.bypass.value;
642
- };
643
- Tuna.prototype.Chorus.prototype = Object.create(Super, {
644
- name: {
645
- value: "Chorus"
646
- },
647
- defaults: {
648
- writable: true,
649
- value: {
650
- feedback: {
651
- value: 0.4,
652
- min: 0,
653
- max: 0.95,
654
- automatable: false,
655
- type: FLOAT
656
- },
657
- delay: {
658
- value: 0.0045,
659
- min: 0,
660
- max: 1,
661
- automatable: false,
662
- type: FLOAT
663
- },
664
- depth: {
665
- value: 0.7,
666
- min: 0,
667
- max: 1,
668
- automatable: false,
669
- type: FLOAT
670
- },
671
- rate: {
672
- value: 1.5,
673
- min: 0,
674
- max: 8,
675
- automatable: false,
676
- type: FLOAT
677
- },
678
- bypass: {
679
- value: false,
680
- automatable: false,
681
- type: BOOLEAN
682
- }
683
- }
684
- },
685
- delay: {
686
- enumerable: true,
687
- get: function() {
688
- return this._delay;
689
- },
690
- set: function(value) {
691
- this._delay = 0.0002 * (Math.pow(10, value) * 2);
692
- this.lfoL.offset = this._delay;
693
- this.lfoR.offset = this._delay;
694
- this._depth = this._depth;
695
- }
696
- },
697
- depth: {
698
- enumerable: true,
699
- get: function() {
700
- return this._depth;
701
- },
702
- set: function(value) {
703
- this._depth = value;
704
- this.lfoL.oscillation = this._depth * this._delay;
705
- this.lfoR.oscillation = this._depth * this._delay;
706
- }
707
- },
708
- feedback: {
709
- enumerable: true,
710
- get: function() {
711
- return this._feedback;
712
- },
713
- set: function(value) {
714
- this._feedback = value;
715
- this.feedbackGainNodeLR.gain.setTargetAtTime(this._feedback, userContext.currentTime, 0.01);
716
- this.feedbackGainNodeRL.gain.setTargetAtTime(this._feedback, userContext.currentTime, 0.01);
717
- }
718
- },
719
- rate: {
720
- enumerable: true,
721
- get: function() {
722
- return this._rate;
723
- },
724
- set: function(value) {
725
- this._rate = value;
726
- this.lfoL.frequency = this._rate;
727
- this.lfoR.frequency = this._rate;
728
- }
729
- }
730
- });
731
-
732
- Tuna.prototype.Compressor = function(properties) {
733
- if (!properties) {
734
- properties = this.getDefaults();
735
- }
736
- this.input = userContext.createGain();
737
- this.compNode = this.activateNode = userContext.createDynamicsCompressor();
738
- this.makeupNode = userContext.createGain();
739
- this.output = userContext.createGain();
740
-
741
- this.compNode.connect(this.makeupNode);
742
- this.makeupNode.connect(this.output);
743
-
744
- this.automakeup = initValue(properties.automakeup, this.defaults.automakeup.value);
745
-
746
- //don't use makeupGain setter at initialization to avoid smoothing
747
- if (this.automakeup) {
748
- this.makeupNode.gain.value = dbToWAVolume(this.computeMakeup());
749
- } else {
750
- this.makeupNode.gain.value = dbToWAVolume(initValue(properties.makeupGain, this.defaults.makeupGain.value));
751
- }
752
- this.threshold = initValue(properties.threshold, this.defaults.threshold.value);
753
- this.release = initValue(properties.release, this.defaults.release.value);
754
- this.attack = initValue(properties.attack, this.defaults.attack.value);
755
- this.ratio = properties.ratio || this.defaults.ratio.value;
756
- this.knee = initValue(properties.knee, this.defaults.knee.value);
757
- this.bypass = properties.bypass || this.defaults.bypass.value;
758
- };
759
- Tuna.prototype.Compressor.prototype = Object.create(Super, {
760
- name: {
761
- value: "Compressor"
762
- },
763
- defaults: {
764
- writable: true,
765
- value: {
766
- threshold: {
767
- value: -20,
768
- min: -60,
769
- max: 0,
770
- automatable: true,
771
- type: FLOAT
772
- },
773
- release: {
774
- value: 250,
775
- min: 10,
776
- max: 2000,
777
- automatable: true,
778
- type: FLOAT
779
- },
780
- makeupGain: {
781
- value: 1,
782
- min: 1,
783
- max: 100,
784
- automatable: true,
785
- type: FLOAT
786
- },
787
- attack: {
788
- value: 1,
789
- min: 0,
790
- max: 1000,
791
- automatable: true,
792
- type: FLOAT
793
- },
794
- ratio: {
795
- value: 4,
796
- min: 1,
797
- max: 50,
798
- automatable: true,
799
- type: FLOAT
800
- },
801
- knee: {
802
- value: 5,
803
- min: 0,
804
- max: 40,
805
- automatable: true,
806
- type: FLOAT
807
- },
808
- automakeup: {
809
- value: false,
810
- automatable: false,
811
- type: BOOLEAN
812
- },
813
- bypass: {
814
- value: false,
815
- automatable: false,
816
- type: BOOLEAN
817
- }
818
- }
819
- },
820
- computeMakeup: {
821
- value: function() {
822
- var magicCoefficient = 4, // raise me if the output is too hot
823
- c = this.compNode;
824
- return -(c.threshold.value - c.threshold.value / c.ratio.value) / magicCoefficient;
825
- }
826
- },
827
- automakeup: {
828
- enumerable: true,
829
- get: function() {
830
- return this._automakeup;
831
- },
832
- set: function(value) {
833
- this._automakeup = value;
834
- if (this._automakeup) this.makeupGain = this.computeMakeup();
835
- }
836
- },
837
- threshold: {
838
- enumerable: true,
839
- get: function() {
840
- return this.compNode.threshold;
841
- },
842
- set: function(value) {
843
- this.compNode.threshold.value = value;
844
- if (this._automakeup) this.makeupGain = this.computeMakeup();
845
- }
846
- },
847
- ratio: {
848
- enumerable: true,
849
- get: function() {
850
- return this.compNode.ratio;
851
- },
852
- set: function(value) {
853
- this.compNode.ratio.value = value;
854
- if (this._automakeup) this.makeupGain = this.computeMakeup();
855
- }
856
- },
857
- knee: {
858
- enumerable: true,
859
- get: function() {
860
- return this.compNode.knee;
861
- },
862
- set: function(value) {
863
- this.compNode.knee.value = value;
864
- if (this._automakeup) this.makeupGain = this.computeMakeup();
865
- }
866
- },
867
- attack: {
868
- enumerable: true,
869
- get: function() {
870
- return this.compNode.attack;
871
- },
872
- set: function(value) {
873
- this.compNode.attack.value = value / 1000;
874
- }
875
- },
876
- release: {
877
- enumerable: true,
878
- get: function() {
879
- return this.compNode.release;
880
- },
881
- set: function(value) {
882
- this.compNode.release.value = value / 1000;
883
- }
884
- },
885
- makeupGain: {
886
- enumerable: true,
887
- get: function() {
888
- return this.makeupNode.gain;
889
- },
890
- set: function(value) {
891
- this.makeupNode.gain.setTargetAtTime(dbToWAVolume(value), userContext.currentTime, 0.01);
892
- }
893
- }
894
- });
895
-
896
- Tuna.prototype.Convolver = function(properties) {
897
- if (!properties) {
898
- properties = this.getDefaults();
899
- }
900
- this.input = userContext.createGain();
901
- this.activateNode = userContext.createGain();
902
- this.convolver = userContext.createConvolver();
903
- this.dry = userContext.createGain();
904
- this.filterLow = userContext.createBiquadFilter();
905
- this.filterHigh = userContext.createBiquadFilter();
906
- this.wet = userContext.createGain();
907
- this.output = userContext.createGain();
908
-
909
- this.activateNode.connect(this.filterLow);
910
- this.activateNode.connect(this.dry);
911
- this.filterLow.connect(this.filterHigh);
912
- this.filterHigh.connect(this.convolver);
913
- this.convolver.connect(this.wet);
914
- this.wet.connect(this.output);
915
- this.dry.connect(this.output);
916
-
917
- //don't use setters at init to avoid smoothing
918
- this.dry.gain.value = initValue(properties.dryLevel, this.defaults.dryLevel.value);
919
- this.wet.gain.value = initValue(properties.wetLevel, this.defaults.wetLevel.value);
920
- this.filterHigh.frequency.value = properties.highCut || this.defaults.highCut.value;
921
- this.filterLow.frequency.value = properties.lowCut || this.defaults.lowCut.value;
922
- this.output.gain.value = initValue(properties.level, this.defaults.level.value);
923
- this.filterHigh.type = "lowpass";
924
- this.filterLow.type = "highpass";
925
- this.buffer = properties.impulse || "../impulses/ir_rev_short.wav";
926
- this.bypass = properties.bypass || this.defaults.bypass.value;
927
- };
928
- Tuna.prototype.Convolver.prototype = Object.create(Super, {
929
- name: {
930
- value: "Convolver"
931
- },
932
- defaults: {
933
- writable: true,
934
- value: {
935
- highCut: {
936
- value: 22050,
937
- min: 20,
938
- max: 22050,
939
- automatable: true,
940
- type: FLOAT
941
- },
942
- lowCut: {
943
- value: 20,
944
- min: 20,
945
- max: 22050,
946
- automatable: true,
947
- type: FLOAT
948
- },
949
- dryLevel: {
950
- value: 1,
951
- min: 0,
952
- max: 1,
953
- automatable: true,
954
- type: FLOAT
955
- },
956
- wetLevel: {
957
- value: 1,
958
- min: 0,
959
- max: 1,
960
- automatable: true,
961
- type: FLOAT
962
- },
963
- level: {
964
- value: 1,
965
- min: 0,
966
- max: 1,
967
- automatable: true,
968
- type: FLOAT
969
- },
970
- bypass: {
971
- value: false,
972
- automatable: false,
973
- type: BOOLEAN
974
- }
975
- }
976
- },
977
- lowCut: {
978
- get: function() {
979
- return this.filterLow.frequency;
980
- },
981
- set: function(value) {
982
- this.filterLow.frequency.setTargetAtTime(value, userContext.currentTime, 0.01);
983
- }
984
- },
985
- highCut: {
986
- get: function() {
987
- return this.filterHigh.frequency;
988
- },
989
- set: function(value) {
990
- this.filterHigh.frequency.setTargetAtTime(value, userContext.currentTime, 0.01);
991
- }
992
- },
993
- level: {
994
- get: function() {
995
- return this.output.gain;
996
- },
997
- set: function(value) {
998
- this.output.gain.setTargetAtTime(value, userContext.currentTime, 0.01);
999
- }
1000
- },
1001
- dryLevel: {
1002
- get: function() {
1003
- return this.dry.gain;
1004
- },
1005
- set: function(value) {
1006
- this.dry.gain.setTargetAtTime(value, userContext.currentTime, 0.01);
1007
- }
1008
- },
1009
- wetLevel: {
1010
- get: function() {
1011
- return this.wet.gain;
1012
- },
1013
- set: function(value) {
1014
- this.wet.gain.setTargetAtTime(value, userContext.currentTime, 0.01);
1015
- }
1016
- },
1017
- buffer: {
1018
- enumerable: false,
1019
- get: function() {
1020
- return this.convolver.buffer;
1021
- },
1022
- set: function(impulse) {
1023
- var convolver = this.convolver,
1024
- xhr = new XMLHttpRequest();
1025
- if (!impulse) {
1026
- console.log("Tuna.Convolver.setBuffer: Missing impulse path!");
1027
- return;
1028
- }
1029
- xhr.open("GET", impulse, true);
1030
- xhr.responseType = "arraybuffer";
1031
- xhr.onreadystatechange = function() {
1032
- if (xhr.readyState === 4) {
1033
- if (xhr.status < 300 && xhr.status > 199 || xhr.status === 302) {
1034
- userContext.decodeAudioData(xhr.response, function(buffer) {
1035
- convolver.buffer = buffer;
1036
- }, function(e) {
1037
- if (e) console.log("Tuna.Convolver.setBuffer: Error decoding data" + e);
1038
- });
1039
- }
1040
- }
1041
- };
1042
- xhr.send(null);
1043
- }
1044
- }
1045
- });
1046
-
1047
- Tuna.prototype.Delay = function(properties) {
1048
- if (!properties) {
1049
- properties = this.getDefaults();
1050
- }
1051
- this.input = userContext.createGain();
1052
- this.activateNode = userContext.createGain();
1053
- this.dry = userContext.createGain();
1054
- this.wet = userContext.createGain();
1055
- this.filter = userContext.createBiquadFilter();
1056
- this.delay = userContext.createDelay(10);
1057
- this.feedbackNode = userContext.createGain();
1058
- this.output = userContext.createGain();
1059
-
1060
- this.activateNode.connect(this.delay);
1061
- this.activateNode.connect(this.dry);
1062
- this.delay.connect(this.filter);
1063
- this.filter.connect(this.feedbackNode);
1064
- this.feedbackNode.connect(this.delay);
1065
- this.feedbackNode.connect(this.wet);
1066
- this.wet.connect(this.output);
1067
- this.dry.connect(this.output);
1068
-
1069
- this.delayTime = properties.delayTime || this.defaults.delayTime.value;
1070
- //don't use setters at init to avoid smoothing
1071
- this.feedbackNode.gain.value = initValue(properties.feedback, this.defaults.feedback.value);
1072
- this.wet.gain.value = initValue(properties.wetLevel, this.defaults.wetLevel.value);
1073
- this.dry.gain.value = initValue(properties.dryLevel, this.defaults.dryLevel.value);
1074
- this.filter.frequency.value = properties.cutoff || this.defaults.cutoff.value;
1075
- this.filter.type = "lowpass";
1076
- this.bypass = properties.bypass || this.defaults.bypass.value;
1077
- };
1078
- Tuna.prototype.Delay.prototype = Object.create(Super, {
1079
- name: {
1080
- value: "Delay"
1081
- },
1082
- defaults: {
1083
- writable: true,
1084
- value: {
1085
- delayTime: {
1086
- value: 100,
1087
- min: 20,
1088
- max: 1000,
1089
- automatable: false,
1090
- type: FLOAT
1091
- },
1092
- feedback: {
1093
- value: 0.45,
1094
- min: 0,
1095
- max: 0.9,
1096
- automatable: true,
1097
- type: FLOAT
1098
- },
1099
- cutoff: {
1100
- value: 20000,
1101
- min: 20,
1102
- max: 20000,
1103
- automatable: true,
1104
- type: FLOAT
1105
- },
1106
- wetLevel: {
1107
- value: 0.5,
1108
- min: 0,
1109
- max: 1,
1110
- automatable: true,
1111
- type: FLOAT
1112
- },
1113
- dryLevel: {
1114
- value: 1,
1115
- min: 0,
1116
- max: 1,
1117
- automatable: true,
1118
- type: FLOAT
1119
- },
1120
- bypass: {
1121
- value: false,
1122
- automatable: false,
1123
- type: BOOLEAN
1124
- }
1125
- }
1126
- },
1127
- delayTime: {
1128
- enumerable: true,
1129
- get: function() {
1130
- return this.delay.delayTime;
1131
- },
1132
- set: function(value) {
1133
- this.delay.delayTime.value = value / 1000;
1134
- }
1135
- },
1136
- wetLevel: {
1137
- enumerable: true,
1138
- get: function() {
1139
- return this.wet.gain;
1140
- },
1141
- set: function(value) {
1142
- this.wet.gain.setTargetAtTime(value, userContext.currentTime, 0.01);
1143
- }
1144
- },
1145
- dryLevel: {
1146
- enumerable: true,
1147
- get: function() {
1148
- return this.dry.gain;
1149
- },
1150
- set: function(value) {
1151
- this.dry.gain.setTargetAtTime(value, userContext.currentTime, 0.01);
1152
- }
1153
- },
1154
- feedback: {
1155
- enumerable: true,
1156
- get: function() {
1157
- return this.feedbackNode.gain;
1158
- },
1159
- set: function(value) {
1160
- this.feedbackNode.gain.setTargetAtTime(value, userContext.currentTime, 0.01);
1161
- }
1162
- },
1163
- cutoff: {
1164
- enumerable: true,
1165
- get: function() {
1166
- return this.filter.frequency;
1167
- },
1168
- set: function(value) {
1169
- this.filter.frequency.setTargetAtTime(value, userContext.currentTime, 0.01);
1170
- }
1171
- }
1172
- });
1173
-
1174
- Tuna.prototype.Filter = function(properties) {
1175
- if (!properties) {
1176
- properties = this.getDefaults();
1177
- }
1178
- this.input = userContext.createGain();
1179
- this.activateNode = userContext.createGain();
1180
- this.filter = userContext.createBiquadFilter();
1181
- this.output = userContext.createGain();
1182
-
1183
- this.activateNode.connect(this.filter);
1184
- this.filter.connect(this.output);
1185
-
1186
- //don't use setters for freq and gain at init to avoid smoothing
1187
- this.filter.frequency.value = properties.frequency || this.defaults.frequency.value;
1188
- this.Q = properties.resonance || this.defaults.Q.value;
1189
- this.filterType = initValue(properties.filterType, this.defaults.filterType.value);
1190
- this.filter.gain.value = initValue(properties.gain, this.defaults.gain.value);
1191
- this.bypass = properties.bypass || this.defaults.bypass.value;
1192
- };
1193
- Tuna.prototype.Filter.prototype = Object.create(Super, {
1194
- name: {
1195
- value: "Filter"
1196
- },
1197
- defaults: {
1198
- writable: true,
1199
- value: {
1200
- frequency: {
1201
- value: 800,
1202
- min: 20,
1203
- max: 22050,
1204
- automatable: true,
1205
- type: FLOAT
1206
- },
1207
- Q: {
1208
- value: 1,
1209
- min: 0.001,
1210
- max: 100,
1211
- automatable: true,
1212
- type: FLOAT
1213
- },
1214
- gain: {
1215
- value: 0,
1216
- min: -40,
1217
- max: 40,
1218
- automatable: true,
1219
- type: FLOAT
1220
- },
1221
- bypass: {
1222
- value: false,
1223
- automatable: false,
1224
- type: BOOLEAN
1225
- },
1226
- filterType: {
1227
- value: "lowpass",
1228
- automatable: false,
1229
- type: STRING
1230
- }
1231
- }
1232
- },
1233
- filterType: {
1234
- enumerable: true,
1235
- get: function() {
1236
- return this.filter.type;
1237
- },
1238
- set: function(value) {
1239
- this.filter.type = value;
1240
- }
1241
- },
1242
- Q: {
1243
- enumerable: true,
1244
- get: function() {
1245
- return this.filter.Q;
1246
- },
1247
- set: function(value) {
1248
- this.filter.Q.value = value;
1249
- }
1250
- },
1251
- gain: {
1252
- enumerable: true,
1253
- get: function() {
1254
- return this.filter.gain;
1255
- },
1256
- set: function(value) {
1257
- this.filter.gain.setTargetAtTime(value, userContext.currentTime, 0.01);
1258
- }
1259
- },
1260
- frequency: {
1261
- enumerable: true,
1262
- get: function() {
1263
- return this.filter.frequency;
1264
- },
1265
- set: function(value) {
1266
- this.filter.frequency.setTargetAtTime(value, userContext.currentTime, 0.01);
1267
- }
1268
- }
1269
- });
1270
-
1271
- Tuna.prototype.Gain = function(properties) {
1272
- if (!properties) {
1273
- properties = this.getDefaults();
1274
- }
1275
-
1276
- this.input = userContext.createGain();
1277
- this.activateNode = userContext.createGain();
1278
- this.gainNode = userContext.createGain();
1279
- this.output = userContext.createGain();
1280
-
1281
- this.activateNode.connect(this.gainNode);
1282
- this.gainNode.connect(this.output);
1283
-
1284
- //don't use setter at init to avoid smoothing
1285
- this.gainNode.gain.value = initValue(properties.gain, this.defaults.gain.value);
1286
- this.bypass = properties.bypass || this.defaults.bypass.value;
1287
- };
1288
- Tuna.prototype.Gain.prototype = Object.create(Super, {
1289
- name: {
1290
- value: "Gain"
1291
- },
1292
- defaults: {
1293
- writable: true,
1294
- value: {
1295
- bypass: {
1296
- value: false,
1297
- automatable: false,
1298
- type: BOOLEAN
1299
- },
1300
- gain: {
1301
- value: 1.0,
1302
- automatable: true,
1303
- type: FLOAT
1304
- }
1305
- }
1306
- },
1307
- gain: {
1308
- enumerable: true,
1309
- get: function() {
1310
- return this.gainNode.gain;
1311
- },
1312
- set: function(value) {
1313
- this.gainNode.gain.setTargetAtTime(value, userContext.currentTime, 0.01);
1314
- }
1315
- }
1316
- });
1317
-
1318
- Tuna.prototype.MoogFilter = function(properties) {
1319
- if (!properties) {
1320
- properties = this.getDefaults();
1321
- }
1322
- this.bufferSize = properties.bufferSize || this.defaults.bufferSize.value;
1323
-
1324
- this.input = userContext.createGain();
1325
- this.activateNode = userContext.createGain();
1326
- this.processor = userContext.createScriptProcessor(this.bufferSize, 1, 1);
1327
- this.output = userContext.createGain();
1328
-
1329
- this.activateNode.connect(this.processor);
1330
- this.processor.connect(this.output);
1331
-
1332
- var in1, in2, in3, in4, out1, out2, out3, out4;
1333
- in1 = in2 = in3 = in4 = out1 = out2 = out3 = out4 = 0.0;
1334
- var input, output, f, fb, i, length, inputFactor;
1335
- this.processor.onaudioprocess = function(e) {
1336
- input = e.inputBuffer.getChannelData(0);
1337
- output = e.outputBuffer.getChannelData(0);
1338
- f = this.cutoff * 1.16;
1339
- inputFactor = 0.35013 * (f * f) * (f * f);
1340
- fb = this.resonance * (1.0 - 0.15 * f * f);
1341
- length = input.length;
1342
- for (i = 0; i < length; i++) {
1343
- input[i] -= out4 * fb;
1344
- input[i] *= inputFactor;
1345
- out1 = input[i] + 0.3 * in1 + (1 - f) * out1; // Pole 1
1346
- in1 = input[i];
1347
- out2 = out1 + 0.3 * in2 + (1 - f) * out2; // Pole 2
1348
- in2 = out1;
1349
- out3 = out2 + 0.3 * in3 + (1 - f) * out3; // Pole 3
1350
- in3 = out2;
1351
- out4 = out3 + 0.3 * in4 + (1 - f) * out4; // Pole 4
1352
- in4 = out3;
1353
- output[i] = out4;
1354
- }
1355
- };
1356
-
1357
- this.cutoff = initValue(properties.cutoff, this.defaults.cutoff.value);
1358
- this.resonance = initValue(properties.resonance, this.defaults.resonance.value);
1359
- this.bypass = properties.bypass || this.defaults.bypass.value;
1360
- };
1361
- Tuna.prototype.MoogFilter.prototype = Object.create(Super, {
1362
- name: {
1363
- value: "MoogFilter"
1364
- },
1365
- defaults: {
1366
- writable: true,
1367
- value: {
1368
- bufferSize: {
1369
- value: 4096,
1370
- min: 256,
1371
- max: 16384,
1372
- automatable: false,
1373
- type: INT
1374
- },
1375
- bypass: {
1376
- value: false,
1377
- automatable: false,
1378
- type: BOOLEAN
1379
- },
1380
- cutoff: {
1381
- value: 0.065,
1382
- min: 0.0001,
1383
- max: 1.0,
1384
- automatable: false,
1385
- type: FLOAT
1386
- },
1387
- resonance: {
1388
- value: 3.5,
1389
- min: 0.0,
1390
- max: 4.0,
1391
- automatable: false,
1392
- type: FLOAT
1393
- }
1394
- }
1395
- },
1396
- cutoff: {
1397
- enumerable: true,
1398
- get: function() {
1399
- return this.processor.cutoff;
1400
- },
1401
- set: function(value) {
1402
- this.processor.cutoff = value;
1403
- }
1404
- },
1405
- resonance: {
1406
- enumerable: true,
1407
- get: function() {
1408
- return this.processor.resonance;
1409
- },
1410
- set: function(value) {
1411
- this.processor.resonance = value;
1412
- }
1413
- }
1414
- });
1415
-
1416
- Tuna.prototype.Overdrive = function(properties) {
1417
- if (!properties) {
1418
- properties = this.getDefaults();
1419
- }
1420
- this.input = userContext.createGain();
1421
- this.activateNode = userContext.createGain();
1422
- this.inputDrive = userContext.createGain();
1423
- this.waveshaper = userContext.createWaveShaper();
1424
- this.outputDrive = userContext.createGain();
1425
- this.output = userContext.createGain();
1426
-
1427
- this.activateNode.connect(this.inputDrive);
1428
- this.inputDrive.connect(this.waveshaper);
1429
- this.waveshaper.connect(this.outputDrive);
1430
- this.outputDrive.connect(this.output);
1431
-
1432
- this.ws_table = new Float32Array(this.k_nSamples);
1433
- this.drive = initValue(properties.drive, this.defaults.drive.value);
1434
- this.outputGain = initValue(properties.outputGain, this.defaults.outputGain.value);
1435
- this.curveAmount = initValue(properties.curveAmount, this.defaults.curveAmount.value);
1436
- this.algorithmIndex = initValue(properties.algorithmIndex, this.defaults.algorithmIndex.value);
1437
- this.bypass = properties.bypass || this.defaults.bypass.value;
1438
- };
1439
- Tuna.prototype.Overdrive.prototype = Object.create(Super, {
1440
- name: {
1441
- value: "Overdrive"
1442
- },
1443
- defaults: {
1444
- writable: true,
1445
- value: {
1446
- drive: {
1447
- value: 1,
1448
- min: 0,
1449
- max: 1,
1450
- automatable: true,
1451
- type: FLOAT,
1452
- scaled: true
1453
- },
1454
- outputGain: {
1455
- value: 0,
1456
- min: -46,
1457
- max: 0,
1458
- automatable: true,
1459
- type: FLOAT,
1460
- scaled: true
1461
- },
1462
- curveAmount: {
1463
- value: 0.725,
1464
- min: 0,
1465
- max: 1,
1466
- automatable: false,
1467
- type: FLOAT
1468
- },
1469
- algorithmIndex: {
1470
- value: 0,
1471
- min: 0,
1472
- max: 5,
1473
- automatable: false,
1474
- type: INT
1475
- },
1476
- bypass: {
1477
- value: false,
1478
- automatable: false,
1479
- type: BOOLEAN
1480
- }
1481
- }
1482
- },
1483
- k_nSamples: {
1484
- value: 8192
1485
- },
1486
- drive: {
1487
- get: function() {
1488
- return this.inputDrive.gain;
1489
- },
1490
- set: function(value) {
1491
- this._drive = value;
1492
- }
1493
- },
1494
- curveAmount: {
1495
- get: function() {
1496
- return this._curveAmount;
1497
- },
1498
- set: function(value) {
1499
- this._curveAmount = value;
1500
- if (this._algorithmIndex === undefined) {
1501
- this._algorithmIndex = 0;
1502
- }
1503
- this.waveshaperAlgorithms[this._algorithmIndex](this._curveAmount, this.k_nSamples, this.ws_table);
1504
- this.waveshaper.curve = this.ws_table;
1505
- }
1506
- },
1507
- outputGain: {
1508
- get: function() {
1509
- return this.outputDrive.gain;
1510
- },
1511
- set: function(value) {
1512
- this._outputGain = dbToWAVolume(value);
1513
- this.outputDrive.gain.setValueAtTime(this._outputGain, userContext.currentTime, 0.01);
1514
- }
1515
- },
1516
- algorithmIndex: {
1517
- get: function() {
1518
- return this._algorithmIndex;
1519
- },
1520
- set: function(value) {
1521
- this._algorithmIndex = value;
1522
- this.curveAmount = this._curveAmount;
1523
- }
1524
- },
1525
- waveshaperAlgorithms: {
1526
- value: [
1527
- function(amount, n_samples, ws_table) {
1528
- amount = Math.min(amount, 0.9999);
1529
- var k = 2 * amount / (1 - amount),
1530
- i, x;
1531
- for (i = 0; i < n_samples; i++) {
1532
- x = i * 2 / n_samples - 1;
1533
- ws_table[i] = (1 + k) * x / (1 + k * Math.abs(x));
1534
- }
1535
- },
1536
- function(amount, n_samples, ws_table) {
1537
- var i, x, y;
1538
- for (i = 0; i < n_samples; i++) {
1539
- x = i * 2 / n_samples - 1;
1540
- y = ((0.5 * Math.pow((x + 1.4), 2)) - 1) * y >= 0 ? 5.8 : 1.2;
1541
- ws_table[i] = tanh(y);
1542
- }
1543
- },
1544
- function(amount, n_samples, ws_table) {
1545
- var i, x, y, a = 1 - amount;
1546
- for (i = 0; i < n_samples; i++) {
1547
- x = i * 2 / n_samples - 1;
1548
- y = x < 0 ? -Math.pow(Math.abs(x), a + 0.04) : Math.pow(x, a);
1549
- ws_table[i] = tanh(y * 2);
1550
- }
1551
- },
1552
- function(amount, n_samples, ws_table) {
1553
- var i, x, y, abx, a = 1 - amount > 0.99 ? 0.99 : 1 - amount;
1554
- for (i = 0; i < n_samples; i++) {
1555
- x = i * 2 / n_samples - 1;
1556
- abx = Math.abs(x);
1557
- if (abx < a) y = abx;
1558
- else if (abx > a) y = a + (abx - a) / (1 + Math.pow((abx - a) / (1 - a), 2));
1559
- else if (abx > 1) y = abx;
1560
- ws_table[i] = sign(x) * y * (1 / ((a + 1) / 2));
1561
- }
1562
- },
1563
- function(amount, n_samples, ws_table) { // fixed curve, amount doesn't do anything, the distortion is just from the drive
1564
- var i, x;
1565
- for (i = 0; i < n_samples; i++) {
1566
- x = i * 2 / n_samples - 1;
1567
- if (x < -0.08905) {
1568
- ws_table[i] = (-3 / 4) * (1 - (Math.pow((1 - (Math.abs(x) - 0.032857)), 12)) + (1 / 3) * (Math.abs(x) - 0.032847)) + 0.01;
1569
- } else if (x >= -0.08905 && x < 0.320018) {
1570
- ws_table[i] = (-6.153 * (x * x)) + 3.9375 * x;
1571
- } else {
1572
- ws_table[i] = 0.630035;
1573
- }
1574
- }
1575
- },
1576
- function(amount, n_samples, ws_table) {
1577
- var a = 2 + Math.round(amount * 14),
1578
- // we go from 2 to 16 bits, keep in mind for the UI
1579
- bits = Math.round(Math.pow(2, a - 1)),
1580
- // real number of quantization steps divided by 2
1581
- i, x;
1582
- for (i = 0; i < n_samples; i++) {
1583
- x = i * 2 / n_samples - 1;
1584
- ws_table[i] = Math.round(x * bits) / bits;
1585
- }
1586
- }
1587
- ]
1588
- }
1589
- });
1590
-
1591
- Tuna.prototype.Panner = function(properties) {
1592
- if (!properties) {
1593
- properties = this.getDefaults();
1594
- }
1595
-
1596
- this.input = userContext.createGain();
1597
- this.activateNode = userContext.createGain();
1598
- this.panner = userContext.createStereoPanner();
1599
- this.output = userContext.createGain();
1600
-
1601
- this.activateNode.connect(this.panner);
1602
- this.panner.connect(this.output);
1603
-
1604
- this.pan = initValue(properties.pan, this.defaults.pan.value);
1605
- this.bypass = properties.bypass || this.defaults.bypass.value;
1606
- };
1607
- Tuna.prototype.Panner.prototype = Object.create(Super, {
1608
- name: {
1609
- value: "Panner"
1610
- },
1611
- defaults: {
1612
- writable: true,
1613
- value: {
1614
- bypass: {
1615
- value: false,
1616
- automatable: false,
1617
- type: BOOLEAN
1618
- },
1619
- pan: {
1620
- value: 0.0,
1621
- min: -1.0,
1622
- max: 1.0,
1623
- automatable: true,
1624
- type: FLOAT
1625
- }
1626
- }
1627
- },
1628
- pan: {
1629
- enumerable: true,
1630
- get: function() {
1631
- return this.panner.pan;
1632
- },
1633
- set: function(value) {
1634
- this.panner.pan.value = value;
1635
- }
1636
- }
1637
- });
1638
-
1639
- Tuna.prototype.Phaser = function(properties) {
1640
- if (!properties) {
1641
- properties = this.getDefaults();
1642
- }
1643
- this.input = userContext.createGain();
1644
- this.splitter = this.activateNode = userContext.createChannelSplitter(2);
1645
- this.filtersL = [];
1646
- this.filtersR = [];
1647
- this.feedbackGainNodeL = userContext.createGain();
1648
- this.feedbackGainNodeR = userContext.createGain();
1649
- this.merger = userContext.createChannelMerger(2);
1650
- this.filteredSignal = userContext.createGain();
1651
- this.output = userContext.createGain();
1652
- this.lfoL = new userInstance.LFO({
1653
- target: this.filtersL,
1654
- callback: this.callback
1655
- });
1656
- this.lfoR = new userInstance.LFO({
1657
- target: this.filtersR,
1658
- callback: this.callback
1659
- });
1660
-
1661
- var i = this.stage;
1662
- while (i--) {
1663
- this.filtersL[i] = userContext.createBiquadFilter();
1664
- this.filtersR[i] = userContext.createBiquadFilter();
1665
- this.filtersL[i].type = "allpass";
1666
- this.filtersR[i].type = "allpass";
1667
- }
1668
- this.input.connect(this.splitter);
1669
- this.input.connect(this.output);
1670
- this.splitter.connect(this.filtersL[0], 0, 0);
1671
- this.splitter.connect(this.filtersR[0], 1, 0);
1672
- this.connectInOrder(this.filtersL);
1673
- this.connectInOrder(this.filtersR);
1674
- this.filtersL[this.stage - 1].connect(this.feedbackGainNodeL);
1675
- this.filtersL[this.stage - 1].connect(this.merger, 0, 0);
1676
- this.filtersR[this.stage - 1].connect(this.feedbackGainNodeR);
1677
- this.filtersR[this.stage - 1].connect(this.merger, 0, 1);
1678
- this.feedbackGainNodeL.connect(this.filtersL[0]);
1679
- this.feedbackGainNodeR.connect(this.filtersR[0]);
1680
- this.merger.connect(this.output);
1681
-
1682
- this.rate = initValue(properties.rate, this.defaults.rate.value);
1683
- this.baseModulationFrequency = properties.baseModulationFrequency || this.defaults.baseModulationFrequency.value;
1684
- this.depth = initValue(properties.depth, this.defaults.depth.value);
1685
- this.feedback = initValue(properties.feedback, this.defaults.feedback.value);
1686
- this.stereoPhase = initValue(properties.stereoPhase, this.defaults.stereoPhase.value);
1687
-
1688
- this.lfoL.activate(true);
1689
- this.lfoR.activate(true);
1690
- this.bypass = properties.bypass || this.defaults.bypass.value;
1691
- };
1692
- Tuna.prototype.Phaser.prototype = Object.create(Super, {
1693
- name: {
1694
- value: "Phaser"
1695
- },
1696
- stage: {
1697
- value: 4
1698
- },
1699
- defaults: {
1700
- writable: true,
1701
- value: {
1702
- rate: {
1703
- value: 0.1,
1704
- min: 0,
1705
- max: 8,
1706
- automatable: false,
1707
- type: FLOAT
1708
- },
1709
- depth: {
1710
- value: 0.6,
1711
- min: 0,
1712
- max: 1,
1713
- automatable: false,
1714
- type: FLOAT
1715
- },
1716
- feedback: {
1717
- value: 0.7,
1718
- min: 0,
1719
- max: 1,
1720
- automatable: false,
1721
- type: FLOAT
1722
- },
1723
- stereoPhase: {
1724
- value: 40,
1725
- min: 0,
1726
- max: 180,
1727
- automatable: false,
1728
- type: FLOAT
1729
- },
1730
- baseModulationFrequency: {
1731
- value: 700,
1732
- min: 500,
1733
- max: 1500,
1734
- automatable: false,
1735
- type: FLOAT
1736
- },
1737
- bypass: {
1738
- value: false,
1739
- automatable: false,
1740
- type: BOOLEAN
1741
- }
1742
- }
1743
- },
1744
- callback: {
1745
- value: function(filters, value) {
1746
- for (var stage = 0; stage < 4; stage++) {
1747
- filters[stage].frequency.value = value;
1748
- }
1749
- }
1750
- },
1751
- depth: {
1752
- get: function() {
1753
- return this._depth;
1754
- },
1755
- set: function(value) {
1756
- this._depth = value;
1757
- this.lfoL.oscillation = this._baseModulationFrequency * this._depth;
1758
- this.lfoR.oscillation = this._baseModulationFrequency * this._depth;
1759
- }
1760
- },
1761
- rate: {
1762
- get: function() {
1763
- return this._rate;
1764
- },
1765
- set: function(value) {
1766
- this._rate = value;
1767
- this.lfoL.frequency = this._rate;
1768
- this.lfoR.frequency = this._rate;
1769
- }
1770
- },
1771
- baseModulationFrequency: {
1772
- enumerable: true,
1773
- get: function() {
1774
- return this._baseModulationFrequency;
1775
- },
1776
- set: function(value) {
1777
- this._baseModulationFrequency = value;
1778
- this.lfoL.offset = this._baseModulationFrequency;
1779
- this.lfoR.offset = this._baseModulationFrequency;
1780
- this.depth = this._depth;
1781
- }
1782
- },
1783
- feedback: {
1784
- get: function() {
1785
- return this._feedback;
1786
- },
1787
- set: function(value) {
1788
- this._feedback = value;
1789
- this.feedbackGainNodeL.gain.setTargetAtTime(this._feedback, userContext.currentTime, 0.01);
1790
- this.feedbackGainNodeR.gain.setTargetAtTime(this._feedback, userContext.currentTime, 0.01);
1791
- }
1792
- },
1793
- stereoPhase: {
1794
- get: function() {
1795
- return this._stereoPhase;
1796
- },
1797
- set: function(value) {
1798
- this._stereoPhase = value;
1799
- var newPhase = this.lfoL._phase + this._stereoPhase * Math.PI / 180;
1800
- newPhase = fmod(newPhase, 2 * Math.PI);
1801
- this.lfoR._phase = newPhase;
1802
- }
1803
- }
1804
- });
1805
-
1806
- Tuna.prototype.PingPongDelay = function(properties) {
1807
- if (!properties) {
1808
- properties = this.getDefaults();
1809
- }
1810
- this.input = userContext.createGain();
1811
- this.wet = userContext.createGain();
1812
- this.stereoToMonoMix = userContext.createGain();
1813
- this.feedbackLevel = userContext.createGain();
1814
- this.output = userContext.createGain();
1815
- this.delayLeft = userContext.createDelay(10);
1816
- this.delayRight = userContext.createDelay(10);
1817
-
1818
- this.activateNode = userContext.createGain();
1819
- this.splitter = userContext.createChannelSplitter(2);
1820
- this.merger = userContext.createChannelMerger(2);
1821
-
1822
- this.activateNode.connect(this.splitter);
1823
- this.splitter.connect(this.stereoToMonoMix, 0, 0);
1824
- this.splitter.connect(this.stereoToMonoMix, 1, 0);
1825
- this.stereoToMonoMix.gain.value = .5;
1826
- this.stereoToMonoMix.connect(this.wet);
1827
- this.wet.connect(this.delayLeft);
1828
- this.feedbackLevel.connect(this.wet);
1829
- this.delayLeft.connect(this.delayRight);
1830
- this.delayRight.connect(this.feedbackLevel);
1831
- this.delayLeft.connect(this.merger, 0, 0);
1832
- this.delayRight.connect(this.merger, 0, 1);
1833
- this.merger.connect(this.output);
1834
- this.activateNode.connect(this.output);
1835
-
1836
- this.delayTimeLeft = properties.delayTimeLeft !== undefined ? properties.delayTimeLeft : this.defaults.delayTimeLeft.value;
1837
- this.delayTimeRight = properties.delayTimeRight !== undefined ? properties.delayTimeRight : this.defaults.delayTimeRight.value;
1838
- this.feedbackLevel.gain.value = properties.feedback !== undefined ? properties.feedback : this.defaults.feedback.value;
1839
- this.wet.gain.value = properties.wetLevel !== undefined ? properties.wetLevel : this.defaults.wetLevel.value;
1840
- this.bypass = properties.bypass || this.defaults.bypass.value;
1841
- };
1842
- Tuna.prototype.PingPongDelay.prototype = Object.create(Super, {
1843
- name: {
1844
- value: "PingPongDelay"
1845
- },
1846
- delayTimeLeft: {
1847
- enumerable: true,
1848
- get: function() {
1849
- return this._delayTimeLeft;
1850
- },
1851
- set: function(value) {
1852
- this._delayTimeLeft = value;
1853
- this.delayLeft.delayTime.value = value / 1000;
1854
- }
1855
- },
1856
- delayTimeRight: {
1857
- enumerable: true,
1858
- get: function() {
1859
- return this._delayTimeRight;
1860
- },
1861
- set: function(value) {
1862
- this._delayTimeRight = value;
1863
- this.delayRight.delayTime.value = value / 1000;
1864
- }
1865
- },
1866
- wetLevel: {
1867
- enumerable: true,
1868
- get: function () {
1869
- return this.wet.gain;
1870
- },
1871
- set: function (value) {
1872
- this.wet.gain.setTargetAtTime(value, userContext.currentTime, 0.01);
1873
- }
1874
- },
1875
- feedback: {
1876
- enumerable: true,
1877
- get: function () {
1878
- return this.feedbackLevel.gain;
1879
- },
1880
- set: function (value) {
1881
- this.feedbackLevel.gain.setTargetAtTime(value, userContext.currentTime, 0.01);
1882
- }
1883
- },
1884
- defaults: {
1885
- writable: true,
1886
- value: {
1887
- delayTimeLeft: {
1888
- value: 200,
1889
- min: 1,
1890
- max: 10000,
1891
- automatable: false,
1892
- type: INT
1893
- },
1894
- delayTimeRight: {
1895
- value: 400,
1896
- min: 1,
1897
- max: 10000,
1898
- automatable: false,
1899
- type: INT
1900
- },
1901
- feedback: {
1902
- value: 0.3,
1903
- min: 0,
1904
- max: 1,
1905
- automatable: true,
1906
- type: FLOAT
1907
- },
1908
- wetLevel: {
1909
- value: 0.5,
1910
- min: 0,
1911
- max: 1,
1912
- automatable: true,
1913
- type: FLOAT
1914
- },
1915
- bypass: {
1916
- value: false,
1917
- automatable: false,
1918
- type: BOOLEAN
1919
- }
1920
- }
1921
- }
1922
- });
1923
-
1924
- Tuna.prototype.Tremolo = function(properties) {
1925
- if (!properties) {
1926
- properties = this.getDefaults();
1927
- }
1928
- this.input = userContext.createGain();
1929
- this.splitter = this.activateNode = userContext.createChannelSplitter(2);
1930
- this.amplitudeL = userContext.createGain();
1931
- this.amplitudeR = userContext.createGain();
1932
- this.merger = userContext.createChannelMerger(2);
1933
- this.output = userContext.createGain();
1934
- this.lfoL = new userInstance.LFO({
1935
- target: this.amplitudeL.gain,
1936
- callback: pipe
1937
- });
1938
- this.lfoR = new userInstance.LFO({
1939
- target: this.amplitudeR.gain,
1940
- callback: pipe
1941
- });
1942
-
1943
- this.input.connect(this.splitter);
1944
- this.splitter.connect(this.amplitudeL, 0);
1945
- this.splitter.connect(this.amplitudeR, 1);
1946
- this.amplitudeL.connect(this.merger, 0, 0);
1947
- this.amplitudeR.connect(this.merger, 0, 1);
1948
- this.merger.connect(this.output);
1949
-
1950
- this.rate = properties.rate || this.defaults.rate.value;
1951
- this.intensity = initValue(properties.intensity, this.defaults.intensity.value);
1952
- this.stereoPhase = initValue(properties.stereoPhase, this.defaults.stereoPhase.value);
1953
-
1954
- this.lfoL.offset = 1 - (this.intensity / 2);
1955
- this.lfoR.offset = 1 - (this.intensity / 2);
1956
- this.lfoL.phase = this.stereoPhase * Math.PI / 180;
1957
-
1958
- this.lfoL.activate(true);
1959
- this.lfoR.activate(true);
1960
- this.bypass = properties.bypass || this.defaults.bypass.value;
1961
- };
1962
- Tuna.prototype.Tremolo.prototype = Object.create(Super, {
1963
- name: {
1964
- value: "Tremolo"
1965
- },
1966
- defaults: {
1967
- writable: true,
1968
- value: {
1969
- intensity: {
1970
- value: 0.3,
1971
- min: 0,
1972
- max: 1,
1973
- automatable: false,
1974
- type: FLOAT
1975
- },
1976
- stereoPhase: {
1977
- value: 0,
1978
- min: 0,
1979
- max: 180,
1980
- automatable: false,
1981
- type: FLOAT
1982
- },
1983
- rate: {
1984
- value: 5,
1985
- min: 0.1,
1986
- max: 11,
1987
- automatable: false,
1988
- type: FLOAT
1989
- },
1990
- bypass: {
1991
- value: false,
1992
- automatable: false,
1993
- type: BOOLEAN
1994
- }
1995
- }
1996
- },
1997
- intensity: {
1998
- enumerable: true,
1999
- get: function() {
2000
- return this._intensity;
2001
- },
2002
- set: function(value) {
2003
- this._intensity = value;
2004
- this.lfoL.offset = 1 - this._intensity / 2;
2005
- this.lfoR.offset = 1 - this._intensity / 2;
2006
- this.lfoL.oscillation = this._intensity;
2007
- this.lfoR.oscillation = this._intensity;
2008
- }
2009
- },
2010
- rate: {
2011
- enumerable: true,
2012
- get: function() {
2013
- return this._rate;
2014
- },
2015
- set: function(value) {
2016
- this._rate = value;
2017
- this.lfoL.frequency = this._rate;
2018
- this.lfoR.frequency = this._rate;
2019
- }
2020
- },
2021
- stereoPhase: {
2022
- enumerable: true,
2023
- get: function() {
2024
- return this._stereoPhase;
2025
- },
2026
- set: function(value) {
2027
- this._stereoPhase = value;
2028
- var newPhase = this.lfoL._phase + this._stereoPhase * Math.PI / 180;
2029
- newPhase = fmod(newPhase, 2 * Math.PI);
2030
- this.lfoR.phase = newPhase;
2031
- }
2032
- }
2033
- });
2034
-
2035
- Tuna.prototype.WahWah = function(properties) {
2036
- if (!properties) {
2037
- properties = this.getDefaults();
2038
- }
2039
- this.input = userContext.createGain();
2040
- this.activateNode = userContext.createGain();
2041
- this.envelopeFollower = new userInstance.EnvelopeFollower({
2042
- target: this,
2043
- callback: function(context, value) {
2044
- context.sweep = value;
2045
- }
2046
- });
2047
- this.filterBp = userContext.createBiquadFilter();
2048
- this.filterPeaking = userContext.createBiquadFilter();
2049
- this.output = userContext.createGain();
2050
-
2051
- //Connect AudioNodes
2052
- this.activateNode.connect(this.filterBp);
2053
- this.filterBp.connect(this.filterPeaking);
2054
- this.filterPeaking.connect(this.output);
2055
-
2056
- //Set Properties
2057
- this.init();
2058
- this.automode = initValue(properties.automode, this.defaults.automode.value);
2059
- this.resonance = properties.resonance || this.defaults.resonance.value;
2060
- this.sensitivity = initValue(properties.sensitivity, this.defaults.sensitivity.value);
2061
- this.baseFrequency = initValue(properties.baseFrequency, this.defaults.baseFrequency.value);
2062
- this.excursionOctaves = properties.excursionOctaves || this.defaults.excursionOctaves.value;
2063
- this.sweep = initValue(properties.sweep, this.defaults.sweep.value);
2064
-
2065
- this.activateNode.gain.value = 2;
2066
- this.envelopeFollower.activate(true);
2067
- this.bypass = properties.bypass || this.defaults.bypass.value;
2068
- };
2069
- Tuna.prototype.WahWah.prototype = Object.create(Super, {
2070
- name: {
2071
- value: "WahWah"
2072
- },
2073
- defaults: {
2074
- writable: true,
2075
- value: {
2076
- automode: {
2077
- value: true,
2078
- automatable: false,
2079
- type: BOOLEAN
2080
- },
2081
- baseFrequency: {
2082
- value: 0.5,
2083
- min: 0,
2084
- max: 1,
2085
- automatable: false,
2086
- type: FLOAT
2087
- },
2088
- excursionOctaves: {
2089
- value: 2,
2090
- min: 1,
2091
- max: 6,
2092
- automatable: false,
2093
- type: FLOAT
2094
- },
2095
- sweep: {
2096
- value: 0.2,
2097
- min: 0,
2098
- max: 1,
2099
- automatable: false,
2100
- type: FLOAT
2101
- },
2102
- resonance: {
2103
- value: 10,
2104
- min: 1,
2105
- max: 100,
2106
- automatable: false,
2107
- type: FLOAT
2108
- },
2109
- sensitivity: {
2110
- value: 0.5,
2111
- min: -1,
2112
- max: 1,
2113
- automatable: false,
2114
- type: FLOAT
2115
- },
2116
- bypass: {
2117
- value: false,
2118
- automatable: false,
2119
- type: BOOLEAN
2120
- }
2121
- }
2122
- },
2123
- automode: {
2124
- get: function() {
2125
- return this._automode;
2126
- },
2127
- set: function(value) {
2128
- this._automode = value;
2129
- if (value) {
2130
- this.activateNode.connect(this.envelopeFollower.input);
2131
- this.envelopeFollower.activate(true);
2132
- } else {
2133
- this.envelopeFollower.activate(false);
2134
- this.activateNode.disconnect();
2135
- this.activateNode.connect(this.filterBp);
2136
- }
2137
- }
2138
- },
2139
- filterFreqTimeout: {
2140
- value: 0
2141
- },
2142
- setFilterFreq: {
2143
- value: function() {
2144
- try {
2145
- this.filterBp.frequency.value = Math.min(22050, this._baseFrequency + this._excursionFrequency * this._sweep);
2146
- this.filterPeaking.frequency.value = Math.min(22050, this._baseFrequency + this._excursionFrequency * this._sweep);
2147
- } catch (e) {
2148
- clearTimeout(this.filterFreqTimeout);
2149
- //put on the next cycle to let all init properties be set
2150
- this.filterFreqTimeout = setTimeout(function() {
2151
- this.setFilterFreq();
2152
- }.bind(this), 0);
2153
- }
2154
- }
2155
- },
2156
- sweep: {
2157
- enumerable: true,
2158
- get: function() {
2159
- return this._sweep;
2160
- },
2161
- set: function(value) {
2162
- this._sweep = Math.pow(value > 1 ? 1 : value < 0 ? 0 : value, this._sensitivity);
2163
- this.setFilterFreq();
2164
- }
2165
- },
2166
- baseFrequency: {
2167
- enumerable: true,
2168
- get: function() {
2169
- return this._baseFrequency;
2170
- },
2171
- set: function(value) {
2172
- this._baseFrequency = 50 * Math.pow(10, value * 2);
2173
- this._excursionFrequency = Math.min(userContext.sampleRate / 2, this.baseFrequency * Math.pow(2, this._excursionOctaves));
2174
- this.setFilterFreq();
2175
- }
2176
- },
2177
- excursionOctaves: {
2178
- enumerable: true,
2179
- get: function() {
2180
- return this._excursionOctaves;
2181
- },
2182
- set: function(value) {
2183
- this._excursionOctaves = value;
2184
- this._excursionFrequency = Math.min(userContext.sampleRate / 2, this.baseFrequency * Math.pow(2, this._excursionOctaves));
2185
- this.setFilterFreq();
2186
- }
2187
- },
2188
- sensitivity: {
2189
- enumerable: true,
2190
- get: function() {
2191
- return this._sensitivity;
2192
- },
2193
- set: function(value) {
2194
- this._sensitivity = Math.pow(10, value);
2195
- }
2196
- },
2197
- resonance: {
2198
- enumerable: true,
2199
- get: function() {
2200
- return this._resonance;
2201
- },
2202
- set: function(value) {
2203
- this._resonance = value;
2204
- this.filterPeaking.Q = this._resonance;
2205
- }
2206
- },
2207
- init: {
2208
- value: function() {
2209
- this.output.gain.value = 1;
2210
- this.filterPeaking.type = "peaking";
2211
- this.filterBp.type = "bandpass";
2212
- this.filterPeaking.frequency.value = 100;
2213
- this.filterPeaking.gain.value = 20;
2214
- this.filterPeaking.Q.value = 5;
2215
- this.filterBp.frequency.value = 100;
2216
- this.filterBp.Q.value = 1;
2217
- }
2218
- }
2219
- });
2220
-
2221
- Tuna.prototype.EnvelopeFollower = function(properties) {
2222
- if (!properties) {
2223
- properties = this.getDefaults();
2224
- }
2225
- this.input = userContext.createGain();
2226
- this.jsNode = this.output = userContext.createScriptProcessor(this.buffersize, 1, 1);
2227
-
2228
- this.input.connect(this.output);
2229
-
2230
- this.attackTime = initValue(properties.attackTime, this.defaults.attackTime.value);
2231
- this.releaseTime = initValue(properties.releaseTime, this.defaults.releaseTime.value);
2232
- this._envelope = 0;
2233
- this.target = properties.target || {};
2234
- this.callback = properties.callback || function() {};
2235
-
2236
- this.bypass = properties.bypass || this.defaults.bypass.value;
2237
- };
2238
- Tuna.prototype.EnvelopeFollower.prototype = Object.create(Super, {
2239
- name: {
2240
- value: "EnvelopeFollower"
2241
- },
2242
- defaults: {
2243
- value: {
2244
- attackTime: {
2245
- value: 0.003,
2246
- min: 0,
2247
- max: 0.5,
2248
- automatable: false,
2249
- type: FLOAT
2250
- },
2251
- releaseTime: {
2252
- value: 0.5,
2253
- min: 0,
2254
- max: 0.5,
2255
- automatable: false,
2256
- type: FLOAT
2257
- },
2258
- bypass: {
2259
- value: false,
2260
- automatable: false,
2261
- type: BOOLEAN
2262
- }
2263
- }
2264
- },
2265
- buffersize: {
2266
- value: 256
2267
- },
2268
- envelope: {
2269
- value: 0
2270
- },
2271
- sampleRate: {
2272
- value: 44100
2273
- },
2274
- attackTime: {
2275
- enumerable: true,
2276
- get: function() {
2277
- return this._attackTime;
2278
- },
2279
- set: function(value) {
2280
- this._attackTime = value;
2281
- this._attackC = Math.exp(-1 / this._attackTime * this.sampleRate / this.buffersize);
2282
- }
2283
- },
2284
- releaseTime: {
2285
- enumerable: true,
2286
- get: function() {
2287
- return this._releaseTime;
2288
- },
2289
- set: function(value) {
2290
- this._releaseTime = value;
2291
- this._releaseC = Math.exp(-1 / this._releaseTime * this.sampleRate / this.buffersize);
2292
- }
2293
- },
2294
- callback: {
2295
- get: function() {
2296
- return this._callback;
2297
- },
2298
- set: function(value) {
2299
- if (typeof value === "function") {
2300
- this._callback = value;
2301
- } else {
2302
- console.error("tuna.js: " + this.name + ": Callback must be a function!");
2303
- }
2304
- }
2305
- },
2306
- target: {
2307
- get: function() {
2308
- return this._target;
2309
- },
2310
- set: function(value) {
2311
- this._target = value;
2312
- }
2313
- },
2314
- activate: {
2315
- value: function(doActivate) {
2316
- this.activated = doActivate;
2317
- if (doActivate) {
2318
- this.jsNode.connect(userContext.destination);
2319
- this.jsNode.onaudioprocess = this.returnCompute(this);
2320
- } else {
2321
- this.jsNode.disconnect();
2322
- this.jsNode.onaudioprocess = null;
2323
- }
2324
- if (this.activateCallback) {
2325
- this.activateCallback(doActivate);
2326
- }
2327
- }
2328
- },
2329
- returnCompute: {
2330
- value: function(instance) {
2331
- return function(event) {
2332
- instance.compute(event);
2333
- };
2334
- }
2335
- },
2336
- compute: {
2337
- value: function(event) {
2338
- var count = event.inputBuffer.getChannelData(0).length,
2339
- channels = event.inputBuffer.numberOfChannels,
2340
- current, chan, rms, i;
2341
- chan = rms = i = 0;
2342
- if (channels > 1) { //need to mixdown
2343
- for (i = 0; i < count; ++i) {
2344
- for (; chan < channels; ++chan) {
2345
- current = event.inputBuffer.getChannelData(chan)[i];
2346
- rms += (current * current) / channels;
2347
- }
2348
- }
2349
- } else {
2350
- for (i = 0; i < count; ++i) {
2351
- current = event.inputBuffer.getChannelData(0)[i];
2352
- rms += (current * current);
2353
- }
2354
- }
2355
- rms = Math.sqrt(rms);
2356
-
2357
- if (this._envelope < rms) {
2358
- this._envelope *= this._attackC;
2359
- this._envelope += (1 - this._attackC) * rms;
2360
- } else {
2361
- this._envelope *= this._releaseC;
2362
- this._envelope += (1 - this._releaseC) * rms;
2363
- }
2364
- this._callback(this._target, this._envelope);
2365
- }
2366
- }
2367
- });
2368
-
2369
- Tuna.prototype.LFO = function(properties) {
2370
- if (!properties) {
2371
- properties = this.getDefaults();
2372
- }
2373
-
2374
- //Instantiate AudioNode
2375
- this.input = userContext.createGain();
2376
- this.output = userContext.createScriptProcessor(256, 1, 1);
2377
- this.activateNode = userContext.destination;
2378
-
2379
- //Set Properties
2380
- this.frequency = initValue(properties.frequency, this.defaults.frequency.value);
2381
- this.offset = initValue(properties.offset, this.defaults.offset.value);
2382
- this.oscillation = initValue(properties.oscillation, this.defaults.oscillation.value);
2383
- this.phase = initValue(properties.phase, this.defaults.phase.value);
2384
- this.target = properties.target || {};
2385
- this.output.onaudioprocess = this.callback(properties.callback || function() {});
2386
- this.bypass = properties.bypass || this.defaults.bypass.value;
2387
- };
2388
- Tuna.prototype.LFO.prototype = Object.create(Super, {
2389
- name: {
2390
- value: "LFO"
2391
- },
2392
- bufferSize: {
2393
- value: 256
2394
- },
2395
- sampleRate: {
2396
- value: 44100
2397
- },
2398
- defaults: {
2399
- value: {
2400
- frequency: {
2401
- value: 1,
2402
- min: 0,
2403
- max: 20,
2404
- automatable: false,
2405
- type: FLOAT
2406
- },
2407
- offset: {
2408
- value: 0.85,
2409
- min: 0,
2410
- max: 22049,
2411
- automatable: false,
2412
- type: FLOAT
2413
- },
2414
- oscillation: {
2415
- value: 0.3,
2416
- min: -22050,
2417
- max: 22050,
2418
- automatable: false,
2419
- type: FLOAT
2420
- },
2421
- phase: {
2422
- value: 0,
2423
- min: 0,
2424
- max: 2 * Math.PI,
2425
- automatable: false,
2426
- type: FLOAT
2427
- },
2428
- bypass: {
2429
- value: false,
2430
- automatable: false,
2431
- type: BOOLEAN
2432
- }
2433
- }
2434
- },
2435
- frequency: {
2436
- get: function() {
2437
- return this._frequency;
2438
- },
2439
- set: function(value) {
2440
- this._frequency = value;
2441
- this._phaseInc = 2 * Math.PI * this._frequency * this.bufferSize / this.sampleRate;
2442
- }
2443
- },
2444
- offset: {
2445
- get: function() {
2446
- return this._offset;
2447
- },
2448
- set: function(value) {
2449
- this._offset = value;
2450
- }
2451
- },
2452
- oscillation: {
2453
- get: function() {
2454
- return this._oscillation;
2455
- },
2456
- set: function(value) {
2457
- this._oscillation = value;
2458
- }
2459
- },
2460
- phase: {
2461
- get: function() {
2462
- return this._phase;
2463
- },
2464
- set: function(value) {
2465
- this._phase = value;
2466
- }
2467
- },
2468
- target: {
2469
- get: function() {
2470
- return this._target;
2471
- },
2472
- set: function(value) {
2473
- this._target = value;
2474
- }
2475
- },
2476
- activate: {
2477
- value: function(doActivate) {
2478
- if (doActivate) {
2479
- this.output.connect(userContext.destination);
2480
- if (this.activateCallback) {
2481
- this.activateCallback(doActivate);
2482
- }
2483
- } else {
2484
- this.output.disconnect();
2485
- }
2486
- }
2487
- },
2488
- callback: {
2489
- value: function(callback) {
2490
- var that = this;
2491
- return function() {
2492
- that._phase += that._phaseInc;
2493
- if (that._phase > 2 * Math.PI) {
2494
- that._phase = 0;
2495
- }
2496
- callback(that._target, that._offset + that._oscillation * Math.sin(that._phase));
2497
- };
2498
- }
2499
- }
2500
- });
2501
-
2502
- Tuna.toString = Tuna.prototype.toString = function() {
2503
- return "Please visit https://github.com/Theodeus/tuna/wiki for instructions on how to use Tuna.js";
2504
- };
2505
- })();
2506
- });
18
+ if (!isConnectable(input) || !isConnectable(output)) {
19
+ return null;
20
+ }
2507
21
 
2508
- var tuna$1 = /*#__PURE__*/Object.freeze({
2509
- default: tuna,
2510
- __moduleExports: tuna
2511
- });
2512
-
2513
- var effects$2 = ( effects$1 && effects ) || effects$1;
2514
-
2515
- var Tuna = ( tuna$1 && tuna ) || tuna$1;
2516
-
2517
- // Create a Databender instance
2518
- var databender = function (config, audioCtx) {
2519
- this.audioCtx = audioCtx ? audioCtx : new AudioContext();
2520
- this.channels = 1;
2521
- this.config = config;
2522
- this.configKeys = Object.keys(this.config);
2523
- this.previousConfig = this.config;
2524
-
2525
- this.convert = function (image) {
2526
- if (image instanceof Image || image instanceof HTMLVideoElement) {
2527
- var canvas = document.createElement('canvas');
2528
- canvas.width = window.innerWidth;
2529
- canvas.height = window.innerHeight;
2530
- var context = canvas.getContext('2d');
2531
- context.drawImage(image, 0, 0, canvas.width, canvas.height);
2532
- var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
2533
- }
2534
- this.imageData = imageData || image;
2535
- var bufferSize = this.imageData.data.length / this.channels;
2536
-
2537
- // Make an audioBuffer on the audioContext to pass to the offlineAudioCtx AudioBufferSourceNode
2538
- var audioBuffer = this.audioCtx.createBuffer(this.channels, bufferSize, this.audioCtx.sampleRate);
2539
-
2540
- // This gives us the actual ArrayBuffer that contains the data
2541
- var nowBuffering = audioBuffer.getChannelData(0);
2542
-
2543
- nowBuffering.set(this.imageData.data);
2544
-
2545
- return Promise.resolve(audioBuffer);
2546
- };
2547
-
2548
- this.configHasChanged = function () {
2549
- return JSON.stringify(this.previousConfig) !== JSON.stringify(this.config);
2550
- };
2551
-
2552
- this.updateConfig = function (effect, param, value) {
2553
- this.config[effect][param] = value;
2554
- };
2555
-
2556
- this.render = function (buffer, bypass = false) {
2557
-
2558
- // Create offlineAudioCtx that will house our rendered buffer
2559
- var offlineAudioCtx = new OfflineAudioContext(this.channels, buffer.length * this.channels, this.audioCtx.sampleRate);
2560
-
2561
- var tuna = new Tuna(offlineAudioCtx);
2562
-
2563
- // Create an AudioBufferSourceNode, which represents an audio source consisting of in-memory audio data
2564
- var bufferSource = offlineAudioCtx.createBufferSource();
2565
-
2566
- // Set buffer to audio buffer containing image data
2567
- bufferSource.buffer = buffer;
2568
-
2569
- var activeEffects = this.configKeys.reduce((acc, cur) => {
2570
- this.config[cur].active ? acc[cur] = effects$2[cur] : false;
2571
- return acc;
2572
- }, {});
2573
- var activeEffectsIndex = Object.keys(activeEffects);
2574
-
2575
- bufferSource.start();
2576
-
2577
- if (activeEffectsIndex && activeEffectsIndex.length) {
2578
- activeEffectsIndex.forEach((effect) => {
2579
- if (effect === 'detune' || effect === 'playbackRate') {
2580
- effects$2[effect](this.config, tuna, bufferSource);
2581
- activeEffectsIndex.pop();
2582
- }
2583
- });
2584
- }
2585
-
2586
- if (!activeEffectsIndex.length) {
2587
- bufferSource.connect(offlineAudioCtx.destination);
2588
- } else {
2589
- var nodes = activeEffectsIndex.map((effect) => {
2590
- const context = effect === 'biquad' ? offlineAudioCtx : tuna;
2591
- return effects$2[effect](this.config, context, bufferSource);
2592
- }).filter(Boolean);
2593
-
2594
- nodes.forEach((node) => {
2595
- bufferSource.connect(node);
2596
- node.connect(offlineAudioCtx.destination);
2597
- });
2598
- }
2599
-
2600
- this.previousConfig = this.config;
2601
- // Kick off the render, callback will contain rendered buffer in event
2602
- return offlineAudioCtx.startRendering();
22
+ return { input, output };
2603
23
  };
2604
24
 
2605
- this.draw = function (buffer, context, sourceX = 0, sourceY = 0, x = 0, y = 0, sourceWidth = this.imageData.width, sourceHeight = this.imageData.height, targetWidth = window.innerWidth, targetHeight = window.innerHeight) {
2606
- // Get buffer data
2607
- var bufferData = buffer.getChannelData(0);
2608
-
2609
- // ImageData expects a Uint8ClampedArray so we need to make a typed array from our buffer
2610
- // @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
2611
- var clampedDataArray = new Uint8ClampedArray(buffer.length);
2612
-
2613
- // set the renderedBuffer to Uint8ClampedArray to use in ImageData later
2614
- clampedDataArray.set(bufferData);
2615
-
2616
- // putImageData requires an ImageData Object
2617
- // @see https://developer.mozilla.org/en-US/docs/Web/API/ImageData
2618
- const transformedImageData = new ImageData(this.imageData.width, this.imageData.height);
2619
- transformedImageData.data.set(clampedDataArray);
25
+ const asArray = (value) => {
26
+ if (!value) {
27
+ return [];
28
+ }
2620
29
 
2621
- const tmpCanvas = document.createElement('canvas');
2622
- tmpCanvas.width = this.imageData.width;
2623
- tmpCanvas.height = this.imageData.height;
2624
- tmpCanvas.getContext('2d').putImageData(transformedImageData, sourceX, sourceY);
2625
- context.drawImage(tmpCanvas, sourceX, sourceY, sourceWidth, sourceHeight, x, y, targetWidth, targetHeight);
30
+ return Array.isArray(value) ? value : [value];
2626
31
  };
2627
32
 
2628
- this.bend = function (data, context, sourceX = 0, sourceY = 0, x = 0, y = 0, targetWidth = window.innerWidth, targetHeight = window.innerHeight) {
2629
- return this.convert(data)
2630
- .then((buffer) => this.render(buffer))
2631
- .then((buffer) => this.draw(buffer, context, sourceX, sourceY, x, y, this.imageData.width, this.imageData.height, targetWidth, targetHeight))
2632
- };
2633
-
2634
- return this;
2635
- };
33
+ class Databender {
34
+ constructor({
35
+ config = {},
36
+ effectsChain = null,
37
+ chainMode = 'series',
38
+ audioCtx = null
39
+ } = {}) {
40
+ this.audioCtx = audioCtx ? audioCtx : new AudioContext();
41
+ this.channels = 1;
42
+ this.config = config || {};
43
+ this.configKeys = Object.keys(this.config);
44
+ this.previousConfig = this.config;
45
+ this.effectsChain = effectsChain ? asArray(effectsChain) : null;
46
+ this.chainMode = chainMode === 'parallel' ? 'parallel' : 'series';
47
+
48
+ this.convert = function(image) {
49
+ if (image instanceof Image || image instanceof HTMLVideoElement) {
50
+ const canvas = typeof OffscreenCanvas !== 'undefined'
51
+ ? new OffscreenCanvas(window.innerWidth, window.innerHeight)
52
+ : (() => {
53
+ const element = document.createElement('canvas');
54
+ element.width = window.innerWidth;
55
+ element.height = window.innerHeight;
56
+ return element;
57
+ })();
58
+ var context = canvas.getContext('2d');
59
+ context.drawImage(image, 0, 0, canvas.width, canvas.height);
60
+ var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
61
+ }
62
+ this.imageData = imageData || image;
63
+ var bufferSize = this.imageData.data.length / this.channels;
64
+
65
+ // Make an audioBuffer on the audioContext to pass to the offlineAudioCtx AudioBufferSourceNode
66
+ var audioBuffer = this.audioCtx.createBuffer(this.channels, bufferSize, this.audioCtx.sampleRate);
67
+
68
+ // This gives us the actual ArrayBuffer that contains the data
69
+ var nowBuffering = audioBuffer.getChannelData(0);
70
+
71
+ for (var i = 0; i < nowBuffering.length; i++) {
72
+ nowBuffering[i] = (this.imageData.data[i] / 128) - 1;
73
+ }
74
+
75
+ return Promise.resolve(audioBuffer);
76
+ };
77
+
78
+ this.configHasChanged = function() {
79
+ if (!this.configKeys.length) {
80
+ return false;
81
+ }
82
+ return JSON.stringify(this.previousConfig) !== JSON.stringify(this.config);
83
+ };
84
+
85
+ this.updateConfig = function(effect, param, value) {
86
+ if (!this.configKeys.length || !this.config[effect]) {
87
+ return;
88
+ }
89
+ this.config[effect][param] = value;
90
+ };
91
+
92
+ this.render = async function(buffer, bypass = false) {
93
+
94
+ // Create offlineAudioCtx that will house our rendered buffer
95
+ var offlineAudioCtx = new OfflineAudioContext(this.channels, buffer.length * this.channels, this.audioCtx.sampleRate);
96
+
97
+ // Create an AudioBufferSourceNode, which represents an audio source consisting of in-memory audio data
98
+ var bufferSource = offlineAudioCtx.createBufferSource();
99
+
100
+ // Set buffer to audio buffer containing image data
101
+ bufferSource.buffer = buffer;
102
+
103
+ var resolveEffectsChain = async function() {
104
+ if (bypass) {
105
+ return [];
106
+ }
107
+
108
+ var chainDefinition = null;
109
+
110
+ if (this.effectsChain) {
111
+ chainDefinition = this.effectsChain;
112
+ }
113
+
114
+ if (isPromise(chainDefinition)) {
115
+ chainDefinition = await chainDefinition;
116
+ }
117
+
118
+ var candidates = asArray(chainDefinition);
119
+ var resolvedNodes = [];
120
+
121
+ for (var i = 0; i < candidates.length; i++) {
122
+ var nodeCandidate = candidates[i];
123
+ var resolvedNode = nodeCandidate;
124
+
125
+ if (isFunction(resolvedNode)) {
126
+ resolvedNode = resolvedNode({ context: offlineAudioCtx, source: bufferSource, config: this.config });
127
+ }
128
+
129
+ if (isPromise(resolvedNode)) {
130
+ resolvedNode = await resolvedNode;
131
+ }
132
+
133
+ var normalizedNodes = asArray(resolvedNode);
134
+
135
+ for (var j = 0; j < normalizedNodes.length; j++) {
136
+ var node = normalizedNodes[j];
137
+ resolvedNodes.push(isPromise(node) ? await node : node);
138
+ }
139
+ }
140
+
141
+ return resolvedNodes;
142
+ }.bind(this);
143
+
144
+ var effectNodes = (await resolveEffectsChain()).map(normalizeEffectNode).filter(Boolean);
145
+
146
+ if (!effectNodes.length) {
147
+ bufferSource.connect(offlineAudioCtx.destination);
148
+ } else if (this.chainMode === 'parallel') {
149
+ effectNodes.forEach((node) => {
150
+ bufferSource.connect(node.input);
151
+ node.output.connect(offlineAudioCtx.destination);
152
+ });
153
+ } else {
154
+ var previousNode = bufferSource;
155
+ effectNodes.forEach((node) => {
156
+ previousNode.connect(node.input);
157
+ previousNode = node.output;
158
+ });
159
+ previousNode.connect(offlineAudioCtx.destination);
160
+ }
161
+
162
+ bufferSource.start();
163
+
164
+ this.previousConfig = this.config;
165
+ // Kick off the render, callback will contain rendered buffer in event
166
+ return offlineAudioCtx.startRendering();
167
+ };
168
+
169
+ this.draw = function(buffer, context, sourceX = 0, sourceY = 0, x = 0, y = 0, sourceWidth = this.imageData.width, sourceHeight = this.imageData.height, targetWidth = window.innerWidth, targetHeight = window.innerHeight) {
170
+ // Get buffer data
171
+ var bufferData = buffer.getChannelData(0);
172
+
173
+ // ImageData expects a Uint8ClampedArray so we need to make a typed array from our buffer
174
+ // @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
175
+ var clampedDataArray = new Uint8ClampedArray(buffer.length);
176
+
177
+ for (var k = 0; k < bufferData.length; k++) {
178
+ var value = ((bufferData[k] + 1) * 128);
179
+ clampedDataArray[k] = value < 0 ? 0 : (value > 255 ? 255 : value);
180
+ }
181
+
182
+ // putImageData requires an ImageData Object
183
+ // @see https://developer.mozilla.org/en-US/docs/Web/API/ImageData
184
+ const transformedImageData = new ImageData(this.imageData.width, this.imageData.height);
185
+ transformedImageData.data.set(clampedDataArray);
186
+
187
+ const tmpCanvas = typeof OffscreenCanvas !== 'undefined'
188
+ ? new OffscreenCanvas(this.imageData.width, this.imageData.height)
189
+ : (() => {
190
+ const element = document.createElement('canvas');
191
+ element.width = this.imageData.width;
192
+ element.height = this.imageData.height;
193
+ return element;
194
+ })();
195
+ tmpCanvas.getContext('2d').putImageData(transformedImageData, sourceX, sourceY);
196
+ context.drawImage(tmpCanvas, sourceX, sourceY, sourceWidth, sourceHeight, x, y, targetWidth, targetHeight);
197
+ };
198
+
199
+ this.bend = function(data, context, sourceX = 0, sourceY = 0, x = 0, y = 0, targetWidth = window.innerWidth, targetHeight = window.innerHeight) {
200
+ return this.convert(data)
201
+ .then((buffer) => this.render(buffer))
202
+ .then((buffer) => this.draw(buffer, context, sourceX, sourceY, x, y, this.imageData.width, this.imageData.height, targetWidth, targetHeight));
203
+ };
204
+
205
+ return this;
206
+ }
207
+ }
2636
208
 
2637
- return databender;
209
+ return Databender;
2638
210
 
2639
- }());
211
+ })();