12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093 |
- /*
- Copyright (c) 2012 DinahMoe AB
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
- files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
- modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
- is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
- OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
- Bitcrusher & Moog Filter by Zach Denton
- */
- // https://developer.mozilla.org/en-US/docs/Web_Audio_API/Porting_webkitAudioContext_code_to_standards_based_AudioContext
- // Originally written by Alessandro Saccoia, Chris Coniglio and Oskar Eriksson
- (function (window) {
- var userContext;
- var userInstance;
- var Tuna = function (context) {
- if (!window.AudioContext) {
- window.AudioContext = window.webkitAudioContext;
- }
- if (!context) {
- console.log("tuna.js: Missing audio context! Creating a new context for you.");
- context = window.AudioContext && (new window.AudioContext());
- }
- userContext = context;
- userInstance = this;
- },
- version = "0.1",
- set = "setValueAtTime",
- linear = "linearRampToValueAtTime",
- pipe = function (param, val) {
- param.value = val;
- },
- Super = Object.create(null, {
- activate: {
- writable: true,
- value: function (doActivate) {
- if (doActivate) {
- this.input.disconnect();
- this.input.connect(this.activateNode);
- if (this.activateCallback) {
- this.activateCallback(doActivate);
- }
- } else {
- this.input.disconnect();
- this.input.connect(this.output);
- }
- }
- },
- bypass: {
- get: function () {
- return this._bypass;
- },
- set: function (value) {
- if (this._lastBypassValue === value) {
- return;
- }
- this._bypass = value;
- this.activate(!value);
- this._lastBypassValue = value;
- }
- },
- connect: {
- value: function (target) {
- this.output.connect(target);
- }
- },
- disconnect: {
- value: function (target) {
- this.output.disconnect(target);
- }
- },
- connectInOrder: {
- value: function (nodeArray) {
- var i = nodeArray.length - 1;
- while(i--) {
- if (!nodeArray[i].connect) {
- return console.error("AudioNode.connectInOrder: TypeError: Not an AudioNode.", nodeArray[i]);
- }
- if (nodeArray[i + 1].input) {
- nodeArray[i].connect(nodeArray[i + 1].input);
- } else {
- nodeArray[i].connect(nodeArray[i + 1]);
- }
- }
- }
- },
- getDefaults: {
- value: function () {
- var result = {};
- for(var key in this.defaults) {
- result[key] = this.defaults[key].value;
- }
- return result;
- }
- },
- getValues: {
- value: function () {
- var result = {};
- for(var key in this.defaults) {
- result[key] = this[key];
- }
- return result;
- }
- },
- automate: {
- value: function (property, value, duration, startTime) {
- var start = startTime ? ~~ (startTime / 1000) : userContext.currentTime,
- dur = duration ? ~~ (duration / 1000) : 0,
- _is = this.defaults[property],
- param = this[property],
- method;
- if (param) {
- if (_is.automatable) {
- if (!duration) {
- method = set;
- } else {
- method = linear;
- param.cancelScheduledValues(start);
- param.setValueAtTime(param.value, start);
- }
- param[method](value, dur + start);
- } else {
- param = value;
- }
- } else {
- console.error("Invalid Property for " + this.name);
- }
- }
- }
- }),
- FLOAT = "float",
- BOOLEAN = "boolean",
- STRING = "string",
- INT = "int";
- function dbToWAVolume(db) {
- return Math.max(0, Math.round(100 * Math.pow(2, db / 6)) / 100);
- }
- function fmod(x, y) {
- // http://kevin.vanzonneveld.net
- // + original by: Onno Marsman
- // + input by: Brett Zamir (http://brett-zamir.me)
- // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
- // * example 1: fmod(5.7, 1.3);
- // * returns 1: 0.5
- var tmp, tmp2, p = 0,
- pY = 0,
- l = 0.0,
- l2 = 0.0;
- tmp = x.toExponential().match(/^.\.?(.*)e(.+)$/);
- p = parseInt(tmp[2], 10) - (tmp[1] + '').length;
- tmp = y.toExponential().match(/^.\.?(.*)e(.+)$/);
- pY = parseInt(tmp[2], 10) - (tmp[1] + '').length;
- if (pY > p) {
- p = pY;
- }
- tmp2 = (x % y);
- if (p < -100 || p > 20) {
- // toFixed will give an out of bound error so we fix it like this:
- l = Math.round(Math.log(tmp2) / Math.log(10));
- l2 = Math.pow(10, l);
- return(tmp2 / l2).toFixed(l - p) * l2;
- } else {
- return parseFloat(tmp2.toFixed(-p));
- }
- }
- function sign(x) {
- if (x === 0) {
- return 1;
- } else {
- return Math.abs(x) / x;
- }
- }
- function tanh(n) {
- return(Math.exp(n) - Math.exp(-n)) / (Math.exp(n) + Math.exp(-n));
- }
- Tuna.toString = Tuna.prototype.toString = function () {
- return "You are running Tuna version " + version + " by Dinahmoe!";
- };
- Tuna.prototype.Filter = function (properties) {
- if (!properties) {
- properties = this.getDefaults();
- }
- this.input = userContext.createGain();
- this.activateNode = userContext.createGain();
- this.filter = userContext.createBiquadFilter();
- this.output = userContext.createGain();
- this.activateNode.connect(this.filter);
- this.filter.connect(this.output);
- this.frequency = properties.frequency || this.defaults.frequency.value;
- this.Q = properties.resonance || this.defaults.Q.value;
- this.filterType = properties.filterType || this.defaults.filterType.value;
- this.gain = properties.gain || this.defaults.gain.value;
- this.bypass = properties.bypass || false;
- };
- Tuna.prototype.Filter.prototype = Object.create(Super, {
- name: {
- value: "Filter"
- },
- defaults: {
- writable:true,
- value: {
- frequency: {
- value: 800,
- min: 20,
- max: 22050,
- automatable: true
- },
- Q: {
- value: 1,
- min: 0.001,
- max: 100,
- automatable: true
- },
- gain: {
- value: 0,
- min: -40,
- max: 40,
- automatable: true
- },
- bypass: {
- value: true,
- automatable: false,
- type: BOOLEAN
- },
- filterType: {
- value: 1,
- min: 0,
- max: 7,
- automatable: false,
- type: INT
- }
- }
- },
- filterType: {
- enumerable: true,
- get: function () {
- return this.filter.type;
- },
- set: function (value) {
- this.filter.type = value;
- }
- },
- Q: {
- enumerable: true,
- get: function () {
- return this.filter.Q;
- },
- set: function (value) {
- this.filter.Q.value = value;
- }
- },
- gain: {
- enumerable: true,
- get: function () {
- return this.filter.gain;
- },
- set: function (value) {
- this.filter.gain.value = value;
- }
- },
- frequency: {
- enumerable: true,
- get: function () {
- return this.filter.frequency;
- },
- set: function (value) {
- this.filter.frequency.value = value;
- }
- }
- });
- Tuna.prototype.Bitcrusher = function (properties) {
- if (!properties) {
- properties = this.getDefaults();
- }
- this.bufferSize = properties.bufferSize || this.defaults.bufferSize.value;
- this.input = userContext.createGain();
- this.activateNode = userContext.createGain();
- this.processor = userContext.createScriptProcessor(this.bufferSize, 1, 1);
- this.output = userContext.createGain();
- this.activateNode.connect(this.processor);
- this.processor.connect(this.output);
- var phaser = 0, last = 0;
- this.processor.onaudioprocess = function (e) {
- var input = e.inputBuffer.getChannelData(0),
- output = e.outputBuffer.getChannelData(0),
- step = Math.pow(1/2, this.bits);
- for(var i = 0; i < input.length; i++) {
- phaser += this.normfreq;
- if (phaser >= 1.0) {
- phaser -= 1.0;
- last = step * Math.floor(input[i] / step + 0.5);
- }
- output[i] = last;
- }
- };
- this.bits = properties.bits || this.defaults.bits.value;
- this.normfreq = properties.normfreq || this.defaults.normfreq.value;
- this.bypass = properties.bypass || false;
- };
- Tuna.prototype.Bitcrusher.prototype = Object.create(Super, {
- name: {
- value: "Bitcrusher"
- },
- defaults: {
- writable: true,
- value: {
- bits: {
- value: 4,
- min: 1,
- max: 16,
- automatable: false,
- type: INT
- },
- bufferSize: {
- value: 4096,
- min: 256,
- max: 16384,
- automatable: false,
- type: INT
- },
- bypass: {
- value: false,
- automatable: false,
- type: BOOLEAN
- },
- normfreq: {
- value: 0.1,
- min: 0.0001,
- max: 1.0,
- automatable: false,
- }
- }
- },
- bits: {
- enumerable: true,
- get: function () {
- return this.processor.bits;
- },
- set: function (value) {
- this.processor.bits = value;
- }
- },
- normfreq: {
- enumerable: true,
- get: function () {
- return this.processor.normfreq;
- },
- set: function (value) {
- this.processor.normfreq = value;
- }
- }
- });
- Tuna.prototype.Cabinet = function (properties) {
- if (!properties) {
- properties = this.getDefaults();
- }
- this.input = userContext.createGain();
- this.activateNode = userContext.createGain();
- this.convolver = this.newConvolver(properties.impulsePath || "../impulses/impulse_guitar.wav");
- this.makeupNode = userContext.createGain();
- this.output = userContext.createGain();
- this.activateNode.connect(this.convolver.input);
- this.convolver.output.connect(this.makeupNode);
- this.makeupNode.connect(this.output);
- this.makeupGain = properties.makeupGain || this.defaults.makeupGain;
- this.bypass = properties.bypass || false;
- };
- Tuna.prototype.Cabinet.prototype = Object.create(Super, {
- name: {
- value: "Cabinet"
- },
- defaults: {
- writable:true,
- value: {
- makeupGain: {
- value: 1,
- min: 0,
- max: 20,
- automatable: true
- },
- bypass: {
- value: false,
- automatable: false,
- type: BOOLEAN
- }
- }
- },
- makeupGain: {
- enumerable: true,
- get: function () {
- return this.makeupNode.gain;
- },
- set: function (value) {
- this.makeupNode.gain.value = value;
- }
- },
- newConvolver: {
- value: function (impulsePath) {
- return new userInstance.Convolver({
- impulse: impulsePath,
- dryLevel: 0,
- wetLevel: 1
- });
- }
- }
- });
- Tuna.prototype.Chorus = function (properties) {
- if (!properties) {
- properties = this.getDefaults();
- }
- this.input = userContext.createGain();
- this.attenuator = this.activateNode = userContext.createGain();
- this.splitter = userContext.createChannelSplitter(2);
- this.delayL = userContext.createDelayNode();
- this.delayR = userContext.createDelayNode();
- this.feedbackGainNodeLR = userContext.createGain();
- this.feedbackGainNodeRL = userContext.createGain();
- this.merger = userContext.createChannelMerger(2);
- this.output = userContext.createGain();
- this.lfoL = new userInstance.LFO({
- target: this.delayL.delayTime,
- callback: pipe
- });
- this.lfoR = new userInstance.LFO({
- target: this.delayR.delayTime,
- callback: pipe
- });
- this.input.connect(this.attenuator);
- this.attenuator.connect(this.output);
- this.attenuator.connect(this.splitter);
- this.splitter.connect(this.delayL, 0);
- this.splitter.connect(this.delayR, 1);
- this.delayL.connect(this.feedbackGainNodeLR);
- this.delayR.connect(this.feedbackGainNodeRL);
- this.feedbackGainNodeLR.connect(this.delayR);
- this.feedbackGainNodeRL.connect(this.delayL);
- this.delayL.connect(this.merger, 0, 0);
- this.delayR.connect(this.merger, 0, 1);
- this.merger.connect(this.output);
- this.feedback = properties.feedback || this.defaults.feedback.value;
- this.rate = properties.rate || this.defaults.rate.value;
- this.delay = properties.delay || this.defaults.delay.value;
- this.depth = properties.depth || this.defaults.depth.value;
- this.lfoR.phase = Math.PI / 2;
- this.attenuator.gain.value = 0.6934; // 1 / (10 ^ (((20 * log10(3)) / 3) / 20))
- this.lfoL.activate(true);
- this.lfoR.activate(true);
- this.bypass = properties.bypass || false;
- };
- Tuna.prototype.Chorus.prototype = Object.create(Super, {
- name: {
- value: "Chorus"
- },
- defaults: {
- writable:true,
- value: {
- feedback: {
- value: 0.4,
- min: 0,
- max: 0.95,
- automatable: false,
- },
- delay: {
- value: 0.0045,
- min: 0,
- max: 1,
- automatable: false,
- },
- depth: {
- value: 0.7,
- min: 0,
- max: 1,
- automatable: false,
- },
- rate: {
- value: 1.5,
- min: 0,
- max: 8,
- automatable: false,
- },
- bypass: {
- value: true,
- automatable: false,
- type: BOOLEAN
- }
- }
- },
- delay: {
- enumerable: true,
- get: function () {
- return this._delay;
- },
- set: function (value) {
- this._delay = 0.0002 * (Math.pow(10, value) * 2);
- this.lfoL.offset = this._delay;
- this.lfoR.offset = this._delay;
- this._depth = this._depth;
- }
- },
- depth: {
- enumerable: true,
- get: function () {
- return this._depth;
- },
- set: function (value) {
- this._depth = value;
- this.lfoL.oscillation = this._depth * this._delay;
- this.lfoR.oscillation = this._depth * this._delay;
- }
- },
- feedback: {
- enumerable: true,
- get: function () {
- return this._feedback;
- },
- set: function (value) {
- this._feedback = value;
- this.feedbackGainNodeLR.gain.value = this._feedback;
- this.feedbackGainNodeRL.gain.value = this._feedback;
- }
- },
- rate: {
- enumerable: true,
- get: function () {
- return this._rate;
- },
- set: function (value) {
- this._rate = value;
- this.lfoL.frequency = this._rate;
- this.lfoR.frequency = this._rate;
- }
- }
- });
- Tuna.prototype.Compressor = function (properties) {
- if (!properties) {
- properties = this.getDefaults();
- }
- this.input = userContext.createGain();
- this.compNode = this.activateNode = userContext.createDynamicsCompressor();
- this.makeupNode = userContext.createGain();
- this.output = userContext.createGain();
- this.compNode.connect(this.makeupNode);
- this.makeupNode.connect(this.output);
- this.automakeup = properties.automakeup || this.defaults.automakeup.value;
- this.makeupGain = properties.makeupGain || this.defaults.makeupGain.value;
- this.threshold = properties.threshold || this.defaults.threshold.value;
- this.release = properties.release || this.defaults.release.value;
- this.attack = properties.attack || this.defaults.attack.value;
- this.ratio = properties.ratio || this.defaults.ratio.value;
- this.knee = properties.knee || this.defaults.knee.value;
- this.bypass = properties.bypass || false;
- };
- Tuna.prototype.Compressor.prototype = Object.create(Super, {
- name: {
- value: "Compressor"
- },
- defaults: {
- writable:true,
- value: {
- threshold: {
- value: -20,
- min: -60,
- max: 0,
- automatable: true
- },
- release: {
- value: 250,
- min: 10,
- max: 2000,
- automatable: true
- },
- makeupGain: {
- value: 1,
- min: 1,
- max: 100,
- automatable: true
- },
- attack: {
- value: 1,
- min: 0,
- max: 1000,
- automatable: true
- },
- ratio: {
- value: 4,
- min: 1,
- max: 50,
- automatable: true
- },
- knee: {
- value: 5,
- min: 0,
- max: 40,
- automatable: true
- },
- automakeup: {
- value: false,
- automatable: false,
- type: BOOLEAN
- },
- bypass: {
- value: true,
- automatable: false,
- type: BOOLEAN
- }
- }
- },
- computeMakeup: {
- value: function () {
- var magicCoefficient = 4,
- // raise me if the output is too hot
- c = this.compNode;
- return -(c.threshold.value - c.threshold.value / c.ratio.value) / magicCoefficient;
- }
- },
- automakeup: {
- enumerable: true,
- get: function () {
- return this._automakeup;
- },
- set: function (value) {
- this._automakeup = value;
- if (this._automakeup) this.makeupGain = this.computeMakeup();
- }
- },
- threshold: {
- enumerable: true,
- get: function () {
- return this.compNode.threshold;
- },
- set: function (value) {
- this.compNode.threshold.value = value;
- if (this._automakeup) this.makeupGain = this.computeMakeup();
- }
- },
- ratio: {
- enumerable: true,
- get: function () {
- return this.compNode.ratio;
- },
- set: function (value) {
- this.compNode.ratio.value = value;
- if (this._automakeup) this.makeupGain = this.computeMakeup();
- }
- },
- knee: {
- enumerable: true,
- get: function () {
- return this.compNode.knee;
- },
- set: function (value) {
- this.compNode.knee.value = value;
- if (this._automakeup) this.makeupGain = this.computeMakeup();
- }
- },
- attack: {
- enumerable: true,
- get: function () {
- return this.compNode.attack;
- },
- set: function (value) {
- this.compNode.attack.value = value / 1000;
- }
- },
- release: {
- enumerable: true,
- get: function () {
- return this.compNode.release;
- },
- set: function (value) {
- this.compNode.release = value / 1000;
- }
- },
- makeupGain: {
- enumerable: true,
- get: function () {
- return this.makeupNode.gain;
- },
- set: function (value) {
- this.makeupNode.gain.value = dbToWAVolume(value);
- }
- }
- });
- Tuna.prototype.Convolver = function (properties) {
- if (!properties) {
- properties = this.getDefaults();
- }
- this.input = userContext.createGain();
- this.activateNode = userContext.createGain();
- this.convolver = userContext.createConvolver();
- this.dry = userContext.createGain();
- this.filterLow = userContext.createBiquadFilter();
- this.filterHigh = userContext.createBiquadFilter();
- this.wet = userContext.createGain();
- this.output = userContext.createGain();
- this.activateNode.connect(this.filterLow);
- this.activateNode.connect(this.dry);
- this.filterLow.connect(this.filterHigh);
- this.filterHigh.connect(this.convolver);
- this.convolver.connect(this.wet);
- this.wet.connect(this.output);
- this.dry.connect(this.output);
- this.dryLevel = properties.dryLevel || this.defaults.dryLevel.value;
- this.wetLevel = properties.wetLevel || this.defaults.wetLevel.value;
- this.highCut = properties.highCut || this.defaults.highCut.value;
- this.buffer = properties.impulse || "../impulses/ir_rev_short.wav";
- this.lowCut = properties.lowCut || this.defaults.lowCut.value;
- this.level = properties.level || this.defaults.level.value;
- this.filterHigh.type = this.filterHigh.LOWPASS;
- this.filterLow.type = this.filterHigh.HIGHPASS;
- this.bypass = properties.bypass || false;
- };
- Tuna.prototype.Convolver.prototype = Object.create(Super, {
- name: {
- value: "Convolver"
- },
- defaults: {
- writable:true,
- value: {
- highCut: {
- value: 22050,
- min: 20,
- max: 22050,
- automatable: true
- },
- lowCut: {
- value: 20,
- min: 20,
- max: 22050,
- automatable: true
- },
- dryLevel: {
- value: 1,
- min: 0,
- max: 1,
- automatable: true
- },
- wetLevel: {
- value: 1,
- min: 0,
- max: 1,
- automatable: true
- },
- level: {
- value: 1,
- min: 0,
- max: 1,
- automatable: true
- }
- }
- },
- lowCut: {
- get: function () {
- return this.filterLow.frequency;
- },
- set: function (value) {
- this.filterLow.frequency.value = value;
- }
- },
- highCut: {
- get: function () {
- return this.filterHigh.frequency;
- },
- set: function (value) {
- this.filterHigh.frequency.value = value;
- }
- },
- level: {
- get: function () {
- return this.output.gain;
- },
- set: function (value) {
- this.output.gain.value = value;
- }
- },
- dryLevel: {
- get: function () {
- return this.dry.gain
- },
- set: function (value) {
- this.dry.gain.value = value;
- }
- },
- wetLevel: {
- get: function () {
- return this.wet.gain;
- },
- set: function (value) {
- this.wet.gain.value = value;
- }
- },
- buffer: {
- enumerable: false,
- get: function () {
- return this.convolver.buffer;
- },
- set: function (impulse) {
- var convolver = this.convolver,
- xhr = new XMLHttpRequest();
- if (!impulse) {
- console.log("Tuna.Convolver.setBuffer: Missing impulse path!");
- return;
- }
- xhr.open("GET", impulse, true);
- xhr.responseType = "arraybuffer";
- xhr.onreadystatechange = function () {
- if (xhr.readyState === 4) {
- if (xhr.status < 300 && xhr.status > 199 || xhr.status === 302) {
- userContext.decodeAudioData(xhr.response, function (buffer) {
- convolver.buffer = buffer;
- }, function (e) {
- if (e) console.log("Tuna.Convolver.setBuffer: Error decoding data" + e);
- });
- }
- }
- };
- xhr.send(null);
- }
- }
- });
- Tuna.prototype.Delay = function (properties) {
- if (!properties) {
- properties = this.getDefaults();
- }
- this.input = userContext.createGain();
- this.activateNode = userContext.createGain();
- this.dry = userContext.createGain();
- this.wet = userContext.createGain();
- this.filter = userContext.createBiquadFilter();
- this.delay = userContext.createDelayNode();
- this.feedbackNode = userContext.createGain();
- this.output = userContext.createGain();
- this.activateNode.connect(this.delay);
- this.activateNode.connect(this.dry);
- this.delay.connect(this.filter);
- this.filter.connect(this.feedbackNode);
- this.feedbackNode.connect(this.delay);
- this.feedbackNode.connect(this.wet);
- this.wet.connect(this.output);
- this.dry.connect(this.output);
- this.delayTime = properties.delayTime || this.defaults.delayTime.value;
- this.feedback = properties.feedback || this.defaults.feedback.value;
- this.wetLevel = properties.wetLevel || this.defaults.wetLevel.value;
- this.dryLevel = properties.dryLevel || this.defaults.dryLevel.value;
- this.cutoff = properties.cutoff || this.defaults.cutoff.value;
- this.filter.type = this.filter.LOWPASS;
- this.bypass = properties.bypass || false;
- };
-
- Tuna.prototype.Delay.prototype = Object.create(Super, {
- name: {
- value: "Delay"
- },
- defaults: {
- writable:true,
- value: {
- delayTime: {
- value: 100,
- min: 20,
- max: 1000,
- automatable: false,
- },
- feedback: {
- value: 0.45,
- min: 0,
- max: 0.9,
- automatable: true
- },
- cutoff: {
- value: 20000,
- min: 20,
- max: 20000,
- automatable: true
- },
- wetLevel: {
- value: 0.5,
- min: 0,
- max: 1,
- automatable: true
- },
- dryLevel: {
- value: 1,
- min: 0,
- max: 1,
- automatable: true
- }
- }
- },
- delayTime: {
- enumerable: true,
- get: function () {
- return this.delay.delayTime;
- },
- set: function (value) {
- this.delay.delayTime.value = value / 1000;
- }
- },
- wetLevel: {
- enumerable: true,
- get: function () {
- return this.wet.gain;
- },
- set: function (value) {
- this.wet.gain.value = value;
- }
- },
- dryLevel: {
- enumerable: true,
- get: function () {
- return this.dry.gain;
- },
- set: function (value) {
- this.dry.gain.value = value;
- }
- },
- feedback: {
- enumerable: true,
- get: function () {
- return this.feedbackNode.gain;
- },
- set: function (value) {
- this.feedbackNode.gain.value = value;
- }
- },
- cutoff: {
- enumerable: true,
- get: function () {
- return this.filter.frequency;
- },
- set: function (value) {
- this.filter.frequency.value = value;
- }
- }
- });
- Tuna.prototype.MoogFilter = function (properties) {
- if (!properties) {
- properties = this.getDefaults();
- }
- this.bufferSize = properties.bufferSize || this.defaults.bufferSize.value;
- this.input = userContext.createGain();
- this.activateNode = userContext.createGain();
- this.processor = userContext.createScriptProcessor(this.bufferSize, 1, 1);
- this.output = userContext.createGain();
- this.activateNode.connect(this.processor);
- this.processor.connect(this.output);
- var in1, in2, in3, in4, out1, out2, out3, out4;
- in1 = in2 = in3 = in4 = out1 = out2 = out3 = out4 = 0.0;
- this.processor.onaudioprocess = function (e) {
- var input = e.inputBuffer.getChannelData(0),
- output = e.outputBuffer.getChannelData(0),
- f = this.cutoff * 1.16,
- fb = this.resonance * (1.0 - 0.15 * f * f);
- for(var i = 0; i < input.length; i++) {
- input[i] -= out4 * fb;
- input[i] *= 0.35013 * (f*f)*(f*f);
- out1 = input[i] + 0.3 * in1 + (1 - f) * out1; // Pole 1
- in1 = input[i];
- out2 = out1 + 0.3 * in2 + (1 - f) * out2; // Pole 2
- in2 = out1;
- out3 = out2 + 0.3 * in3 + (1 - f) * out3; // Pole 3
- in3 = out2;
- out4 = out3 + 0.3 * in4 + (1 - f) * out4; // Pole 4
- in4 = out3;
- output[i] = out4;
- }
- };
- this.cutoff = properties.cutoff || this.defaults.cutoff.value;
- this.resonance = properties.resonance || this.defaults.resonance.value;
- this.bypass = properties.bypass || false;
- };
- Tuna.prototype.MoogFilter.prototype = Object.create(Super, {
- name: {
- value: "MoogFilter"
- },
- defaults: {
- writable: true,
- value: {
- bufferSize: {
- value: 4096,
- min: 256,
- max: 16384,
- automatable: false,
- type: INT
- },
- bypass: {
- value: false,
- automatable: false,
- type: BOOLEAN
- },
- cutoff: {
- value: 0.065,
- min: 0.0001,
- max: 1.0,
- automatable: false,
- },
- resonance: {
- value: 3.5,
- min: 0.0,
- max: 4.0,
- automatable: false,
- }
- }
- },
- cutoff: {
- enumerable: true,
- get: function () {
- return this.processor.cutoff;
- },
- set: function (value) {
- this.processor.cutoff = value;
- }
- },
- resonance: {
- enumerable: true,
- get: function () {
- return this.processor.resonance;
- },
- set: function (value) {
- this.processor.resonance = value;
- }
- }
- });
- Tuna.prototype.Overdrive = function (properties) {
- if (!properties) {
- properties = this.getDefaults();
- }
- this.input = userContext.createGain();
- this.activateNode = userContext.createGain();
- this.inputDrive = userContext.createGain();
- this.waveshaper = userContext.createWaveShaper();
- this.outputDrive = userContext.createGain();
- this.output = userContext.createGain();
- this.activateNode.connect(this.inputDrive);
- this.inputDrive.connect(this.waveshaper);
- this.waveshaper.connect(this.outputDrive);
- this.outputDrive.connect(this.output);
- this.ws_table = new Float32Array(this.k_nSamples);
- this.drive = properties.drive || this.defaults.drive.value;
- this.outputGain = properties.outputGain || this.defaults.outputGain.value;
- this.curveAmount = properties.curveAmount || this.defaults.curveAmount.value;
- this.algorithmIndex = properties.algorithmIndex || this.defaults.algorithmIndex.value;
- this.bypass = properties.bypass || false;
- };
- Tuna.prototype.Overdrive.prototype = Object.create(Super, {
- name: {
- value: "Overdrive"
- },
- defaults: {
- writable:true,
- value: {
- drive: {
- value: 1,
- min: 0,
- max: 1,
- automatable: true,
- type: FLOAT,
- scaled: true
- },
- outputGain: {
- value: 1,
- min: 0,
- max: 1,
- automatable: true,
- type: FLOAT,
- scaled: true
- },
- curveAmount: {
- value: 0.725,
- min: 0,
- max: 1,
- automatable: false,
- },
- algorithmIndex: {
- value: 0,
- min: 0,
- max: 5,
- automatable: false,
- type: INT
- }
- }
- },
- k_nSamples: {
- value: 8192
- },
- drive: {
- get: function () {
- return this.inputDrive.gain;
- },
- set: function (value) {
- this._drive = value;
- }
- },
- curveAmount: {
- get: function () {
- return this._curveAmount;
- },
- set: function (value) {
- this._curveAmount = value;
- if (this._algorithmIndex === undefined) {
- this._algorithmIndex = 0;
- }
- this.waveshaperAlgorithms[this._algorithmIndex](this._curveAmount, this.k_nSamples, this.ws_table);
- this.waveshaper.curve = this.ws_table;
- }
- },
- outputGain: {
- get: function () {
- return this.outputDrive.gain;
- },
- set: function (value) {
- this._outputGain = dbToWAVolume(value);
- }
- },
- algorithmIndex: {
- get: function () {
- return this._algorithmIndex;
- },
- set: function (value) {
- this._algorithmIndex = value>>0;
- this.curveAmount = this._curveAmount;
- }
- },
- waveshaperAlgorithms: {
- value: [
- function (amount, n_samples, ws_table) {
- amount = Math.min(amount, 0.9999);
- var k = 2 * amount / (1 - amount),
- i, x;
- for(i = 0; i < n_samples; i++) {
- x = i * 2 / n_samples - 1;
- ws_table[i] = (1 + k) * x / (1 + k * Math.abs(x));
- }
- }, function (amount, n_samples, ws_table) {
- var i, x, y;
- for(i = 0; i < n_samples; i++) {
- x = i * 2 / n_samples - 1;
- y = ((0.5 * Math.pow((x + 1.4), 2)) - 1) * y >= 0 ? 5.8 : 1.2;
- ws_table[i] = tanh(y);
- }
- }, function (amount, n_samples, ws_table) {
- var i, x, y, a = 1 - amount;
- for(i = 0; i < n_samples; i++) {
- x = i * 2 / n_samples - 1;
- y = x < 0 ? -Math.pow(Math.abs(x), a + 0.04) : Math.pow(x, a);
- ws_table[i] = tanh(y * 2);
- }
- }, function (amount, n_samples, ws_table) {
- var i, x, y, abx, a = 1 - amount > 0.99 ? 0.99 : 1 - amount;
- for(i = 0; i < n_samples; i++) {
- x = i * 2 / n_samples - 1;
- abx = Math.abs(x);
- if (abx < a) y = abx;
- else if (abx > a) y = a + (abx - a) / (1 + Math.pow((abx - a) / (1 - a), 2));
- else if (abx > 1) y = abx;
- ws_table[i] = sign(x) * y * (1 / ((a + 1) / 2));
- }
- }, function (amount, n_samples, ws_table) { // fixed curve, amount doesn't do anything, the distortion is just from the drive
- var i, x;
- for(i = 0; i < n_samples; i++) {
- x = i * 2 / n_samples - 1;
- if (x < -0.08905) {
- ws_table[i] = (-3 / 4) * (1 - (Math.pow((1 - (Math.abs(x) - 0.032857)), 12)) + (1 / 3) * (Math.abs(x) - 0.032847)) + 0.01;
- } else if (x >= -0.08905 && x < 0.320018) {
- ws_table[i] = (-6.153 * (x * x)) + 3.9375 * x;
- } else {
- ws_table[i] = 0.630035;
- }
- }
- }, function (amount, n_samples, ws_table) {
- var a = 2 + Math.round(amount * 14),
- // we go from 2 to 16 bits, keep in mind for the UI
- bits = Math.round(Math.pow(2, a - 1)),
- // real number of quantization steps divided by 2
- i, x;
- for(i = 0; i < n_samples; i++) {
- x = i * 2 / n_samples - 1;
- ws_table[i] = Math.round(x * bits) / bits;
- }
- }]
- }
- });
- Tuna.prototype.Phaser = function (properties) {
- if (!properties) {
- properties = this.getDefaults();
- }
- this.input = userContext.createGain();
- this.splitter = this.activateNode = userContext.createChannelSplitter(2);
- this.filtersL = [];
- this.filtersR = [];
- this.feedbackGainNodeL = userContext.createGain();
- this.feedbackGainNodeR = userContext.createGain();
- this.merger = userContext.createChannelMerger(2);
- this.filteredSignal = userContext.createGain();
- this.output = userContext.createGain();
- this.lfoL = new userInstance.LFO({
- target: this.filtersL,
- callback: this.callback
- });
- this.lfoR = new userInstance.LFO({
- target: this.filtersR,
- callback: this.callback
- });
- var i = this.stage;
- while(i--) {
- this.filtersL[i] = userContext.createBiquadFilter();
- this.filtersR[i] = userContext.createBiquadFilter();
- this.filtersL[i].type = this.filtersL[i].ALLPASS;
- this.filtersR[i].type = this.filtersR[i].ALLPASS;
- }
- this.input.connect(this.splitter);
- this.input.connect(this.output);
- this.splitter.connect(this.filtersL[0], 0, 0);
- this.splitter.connect(this.filtersR[0], 1, 0);
- this.connectInOrder(this.filtersL);
- this.connectInOrder(this.filtersR);
- this.filtersL[this.stage - 1].connect(this.feedbackGainNodeL);
- this.filtersL[this.stage - 1].connect(this.merger, 0, 0);
- this.filtersR[this.stage - 1].connect(this.feedbackGainNodeR);
- this.filtersR[this.stage - 1].connect(this.merger, 0, 1);
- this.feedbackGainNodeL.connect(this.filtersL[0]);
- this.feedbackGainNodeR.connect(this.filtersR[0]);
- this.merger.connect(this.output);
- this.rate = properties.rate || this.defaults.rate.value;
- this.baseModulationFrequency = properties.baseModulationFrequency || this.defaults.baseModulationFrequency.value;
- this.depth = properties.depth || this.defaults.depth.value;
- this.feedback = properties.feedback || this.defaults.feedback.value;
- this.stereoPhase = properties.stereoPhase || this.defaults.stereoPhase.value;
- this.lfoL.activate(true);
- this.lfoR.activate(true);
- this.bypass = properties.bypass || false;
- };
- Tuna.prototype.Phaser.prototype = Object.create(Super, {
- name: {
- value: "Phaser"
- },
- stage: {
- value: 4
- },
- defaults: {
- writable:true,
- value: {
- rate: {
- value: 0.1,
- min: 0,
- max: 8,
- automatable: false,
- },
- depth: {
- value: 0.6,
- min: 0,
- max: 1,
- automatable: false,
- },
- feedback: {
- value: 0.7,
- min: 0,
- max: 1,
- automatable: false,
- },
- stereoPhase: {
- value: 40,
- min: 0,
- max: 180,
- automatable: false,
- },
- baseModulationFrequency: {
- value: 700,
- min: 500,
- max: 1500,
- automatable: false,
- }
- }
- },
- callback: {
- value: function (filters, value) {
- for(var stage = 0; stage < 4; stage++) {
- filters[stage].frequency.value = value;
- }
- }
- },
- depth: {
- get: function () {
- return this._depth;
- },
- set: function (value) {
- this._depth = value;
- this.lfoL.oscillation = this._baseModulationFrequency * this._depth;
- this.lfoR.oscillation = this._baseModulationFrequency * this._depth;
- }
- },
- rate: {
- get: function () {
- return this._rate;
- },
- set: function (value) {
- this._rate = value;
- this.lfoL.frequency = this._rate;
- this.lfoR.frequency = this._rate;
- }
- },
- baseModulationFrequency: {
- enumerable: true,
- get: function () {
- return this._baseModulationFrequency;
- },
- set: function (value) {
- this._baseModulationFrequency = value;
- this.lfoL.offset = this._baseModulationFrequency;
- this.lfoR.offset = this._baseModulationFrequency;
- this._depth = this._depth;
- }
- },
- feedback: {
- get: function () {
- return this._feedback;
- },
- set: function (value) {
- this._feedback = value;
- this.feedbackGainNodeL.gain.value = this._feedback;
- this.feedbackGainNodeR.gain.value = this._feedback;
- }
- },
- stereoPhase: {
- get: function () {
- return this._stereoPhase;
- },
- set: function (value) {
- this._stereoPhase = value;
- var newPhase = this.lfoL._phase + this._stereoPhase * Math.PI / 180;
- newPhase = fmod(newPhase, 2 * Math.PI);
- this.lfoR._phase = newPhase;
- }
- }
- });
- Tuna.prototype.Tremolo = function (properties) {
- if (!properties) {
- properties = this.getDefaults();
- }
- this.input = userContext.createGain();
- this.splitter = this.activateNode = userContext.createChannelSplitter(2), this.amplitudeL = userContext.createGain(), this.amplitudeR = userContext.createGain(), this.merger = userContext.createChannelMerger(2), this.output = userContext.createGain();
- this.lfoL = new userInstance.LFO({
- target: this.amplitudeL.gain,
- callback: pipe
- });
- this.lfoR = new userInstance.LFO({
- target: this.amplitudeR.gain,
- callback: pipe
- });
- this.input.connect(this.splitter);
- this.splitter.connect(this.amplitudeL, 0);
- this.splitter.connect(this.amplitudeR, 1);
- this.amplitudeL.connect(this.merger, 0, 0);
- this.amplitudeR.connect(this.merger, 0, 1);
- this.merger.connect(this.output);
- this.rate = properties.rate || this.defaults.rate.value;
- this.intensity = properties.intensity || this.defaults.intensity.value;
- this.stereoPhase = properties.stereoPhase || this.defaults.stereoPhase.value;
- this.lfoL.offset = 1 - (this.intensity / 2);
- this.lfoR.offset = 1 - (this.intensity / 2);
- this.lfoL.phase = this.stereoPhase * Math.PI / 180;
- this.lfoL.activate(true);
- this.lfoR.activate(true);
- this.bypass = properties.bypass || false;
- };
- Tuna.prototype.Tremolo.prototype = Object.create(Super, {
- name: {
- value: "Tremolo"
- },
- defaults: {
- writable:true,
- value: {
- intensity: {
- value: 0.3,
- min: 0,
- max: 1,
- automatable: false,
- },
- stereoPhase: {
- value: 0,
- min: 0,
- max: 180,
- automatable: false,
- },
- rate: {
- value: 5,
- min: 0.1,
- max: 11,
- automatable: false,
- }
- }
- },
- intensity: {
- enumerable: true,
- get: function () {
- return this._intensity;
- },
- set: function (value) {
- this._intensity = value;
- this.lfoL.offset = 1 - this._intensity / 2;
- this.lfoR.offset = 1 - this._intensity / 2;
- this.lfoL.oscillation = this._intensity;
- this.lfoR.oscillation = this._intensity;
- }
- },
- rate: {
- enumerable: true,
- get: function () {
- return this._rate;
- },
- set: function (value) {
- this._rate = value;
- this.lfoL.frequency = this._rate;
- this.lfoR.frequency = this._rate;
- }
- },
- stereoPhase: {
- enumerable: true,
- get: function () {
- return this._rate;
- },
- set: function (value) {
- this._stereoPhase = value;
- var newPhase = this.lfoL._phase + this._stereoPhase * Math.PI / 180;
- newPhase = fmod(newPhase, 2 * Math.PI);
- this.lfoR.phase = newPhase;
- }
- }
- });
- Tuna.prototype.WahWah = function (properties) {
- if (!properties) {
- properties = this.getDefaults();
- }
- this.input = userContext.createGain();
- this.activateNode = userContext.createGain();
- this.envelopeFollower = new userInstance.EnvelopeFollower({
- target: this,
- callback: function (context, value) {
- context.sweep = value;
- }
- });
- this.filterBp = userContext.createBiquadFilter();
- this.filterPeaking = userContext.createBiquadFilter();
- this.output = userContext.createGain();
- //Connect AudioNodes
- this.activateNode.connect(this.filterBp);
- this.filterBp.connect(this.filterPeaking);
- this.filterPeaking.connect(this.output);
- //Set Properties
- this.init();
- this.automode = properties.enableAutoMode || this.defaults.automode.value;
- this.resonance = properties.resonance || this.defaults.resonance.value;
- this.sensitivity = properties.sensitivity || this.defaults.sensitivity.value;
- this.baseFrequency = properties.baseFrequency || this.defaults.baseFrequency.value;
- this.excursionOctaves = properties.excursionOctaves || this.defaults.excursionOctaves.value;
- this.sweep = properties.sweep || this.defaults.sweep.value;
- this.activateNode.gain.value = 2;
- this.envelopeFollower.activate(true);
- this.bypass = properties.bypass || false;
- };
- Tuna.prototype.WahWah.prototype = Object.create(Super, {
- name: {
- value: "WahWah"
- },
- defaults: {
- writable:true,
- value: {
- automode: {
- value: true,
- automatable: false,
- type: BOOLEAN
- },
- baseFrequency: {
- value: 0.5,
- min: 0,
- max: 1,
- automatable: false,
- },
- excursionOctaves: {
- value: 2,
- min: 1,
- max: 6,
- automatable: false,
- },
- sweep: {
- value: 0.2,
- min: 0,
- max: 1,
- automatable: false,
- },
- resonance: {
- value: 10,
- min: 1,
- max: 100,
- automatable: false,
- },
- sensitivity: {
- value: 0.5,
- min: -1,
- max: 1,
- automatable: false,
- }
- }
- },
- activateCallback: {
- value: function (value) {
- this.automode = value;
- }
- },
- automode: {
- get: function () {
- return this._automode;
- },
- set: function (value) {
- this._automode = value;
- if (value) {
- this.activateNode.connect(this.envelopeFollower.input);
- this.envelopeFollower.activate(true);
- } else {
- this.envelopeFollower.activate(false);
- this.activateNode.disconnect();
- this.activateNode.connect(this.filterBp);
- }
- }
- },
- sweep: {
- enumerable: true,
- get: function () {
- return this._sweep.value;
- },
- set: function (value) {
- this._sweep = Math.pow(value > 1 ? 1 : value < 0 ? 0 : value, this._sensitivity);
- this.filterBp.frequency.value = this._baseFrequency + this._excursionFrequency * this._sweep;
- this.filterPeaking.frequency.value = this._baseFrequency + this._excursionFrequency * this._sweep;
- }
- },
- baseFrequency: {
- enumerable: true,
- get: function () {
- return this._baseFrequency;
- },
- set: function (value) {
- this._baseFrequency = 50 * Math.pow(10, value * 2);
- this._excursionFrequency = Math.min(this.sampleRate / 2, this.baseFrequency * Math.pow(2, this._excursionOctaves));
- this.filterBp.frequency.value = this._baseFrequency + this._excursionFrequency * this._sweep;
- this.filterPeaking.frequency.value = this._baseFrequency + this._excursionFrequency * this._sweep;
- }
- },
- excursionOctaves: {
- enumerable: true,
- get: function () {
- return this._excursionOctaves;
- },
- set: function (value) {
- this._excursionOctaves = value;
- this._excursionFrequency = Math.min(this.sampleRate / 2, this.baseFrequency * Math.pow(2, this._excursionOctaves));
- this.filterBp.frequency.value = this._baseFrequency + this._excursionFrequency * this._sweep;
- this.filterPeaking.frequency.value = this._baseFrequency + this._excursionFrequency * this._sweep;
- }
- },
- sensitivity: {
- enumerable: true,
- get: function () {
- return this._sensitivity;
- },
- set: function (value) {
- this._sensitivity = Math.pow(10, value);
- }
- },
- resonance: {
- enumerable: true,
- get: function () {
- return this._resonance;
- },
- set: function (value) {
- this._resonance = value;
- this.filterPeaking.Q = this._resonance;
- }
- },
- init: {
- value: function () {
- this.output.gain.value = 1;
- this.filterPeaking.type = 5;
- this.filterBp.type = 2;
- this.filterPeaking.frequency.value = 100;
- this.filterPeaking.gain.value = 20;
- this.filterPeaking.Q.value = 5;
- this.filterBp.frequency.value = 100;
- this.filterBp.Q.value = 1;
- this.sampleRate = userContext.sampleRate;
- }
- }
- });
- Tuna.prototype.EnvelopeFollower = function (properties) {
- if (!properties) {
- properties = this.getDefaults();
- }
- this.input = userContext.createGain();
- this.jsNode = this.output = userContext.createScriptProcessor(this.buffersize, 1, 1);
- this.input.connect(this.output);
- this.attackTime = properties.attackTime || this.defaults.attackTime.value;
- this.releaseTime = properties.releaseTime || this.defaults.releaseTime.value;
- this._envelope = 0;
- this.target = properties.target || {};
- this.callback = properties.callback || function () {};
- };
- Tuna.prototype.EnvelopeFollower.prototype = Object.create(Super, {
- name: {
- value: "EnvelopeFollower"
- },
- defaults: {
- value: {
- attackTime: {
- value: 0.003,
- min: 0,
- max: 0.5,
- automatable: false,
- },
- releaseTime: {
- value: 0.5,
- min: 0,
- max: 0.5,
- automatable: false,
- }
- }
- },
- buffersize: {
- value: 256
- },
- envelope: {
- value: 0
- },
- sampleRate: {
- value: 44100
- },
- attackTime: {
- enumerable: true,
- get: function () {
- return this._attackTime;
- },
- set: function (value) {
- this._attackTime = value;
- this._attackC = Math.exp(-1 / this._attackTime * this.sampleRate / this.buffersize);
- }
- },
- releaseTime: {
- enumerable: true,
- get: function () {
- return this._releaseTime;
- },
- set: function (value) {
- this._releaseTime = value;
- this._releaseC = Math.exp(-1 / this._releaseTime * this.sampleRate / this.buffersize);
- }
- },
- callback: {
- get: function () {
- return this._callback;
- },
- set: function (value) {
- if (typeof value === "function") {
- this._callback = value;
- } else {
- console.error("tuna.js: " + this.name + ": Callback must be a function!");
- }
- }
- },
- target: {
- get: function () {
- return this._target;
- },
- set: function (value) {
- this._target = value;
- }
- },
- activate: {
- value: function (doActivate) {
- this.activated = doActivate;
- if (doActivate) {
- this.jsNode.connect(userContext.destination);
- this.jsNode.onaudioprocess = this.returnCompute(this);
- } else {
- this.jsNode.disconnect();
- this.jsNode.onaudioprocess = null;
- }
- }
- },
- returnCompute: {
- value: function (instance) {
- return function (event) {
- instance.compute(event);
- };
- }
- },
- compute: {
- value: function (event) {
- var count = event.inputBuffer.getChannelData(0).length,
- channels = event.inputBuffer.numberOfChannels,
- current, chan, rms, i;
- chan = rms = i = 0;
- if (channels > 1) { //need to mixdown
- for(i = 0; i < count; ++i) {
- for(; chan < channels; ++chan) {
- current = event.inputBuffer.getChannelData(chan)[i];
- rms += (current * current) / channels;
- }
- }
- } else {
- for(i = 0; i < count; ++i) {
- current = event.inputBuffer.getChannelData(0)[i];
- rms += (current * current);
- }
- }
- rms = Math.sqrt(rms);
- if (this._envelope < rms) {
- this._envelope *= this._attackC;
- this._envelope += (1 - this._attackC) * rms;
- } else {
- this._envelope *= this._releaseC;
- this._envelope += (1 - this._releaseC) * rms;
- }
- this._callback(this._target, this._envelope);
- }
- }
- });
-
- // Low-frequency oscillation
- Tuna.prototype.LFO = function (properties) {
- //Instantiate AudioNode
- this.output = userContext.createScriptProcessor(256, 1, 1);
- this.activateNode = userContext.destination;
- //Set Properties
- this.frequency = properties.frequency || this.defaults.frequency.value;
- this.offset = properties.offset || this.defaults.offset.value;
- this.oscillation = properties.oscillation || this.defaults.oscillation.value;
- this.phase = properties.phase || this.defaults.phase.value;
- this.target = properties.target || {};
- this.output.onaudioprocess = this.callback(properties.callback || function () {});
- this.bypass = properties.bypass || false;
- };
- Tuna.prototype.LFO.prototype = Object.create(Super, {
- name: {
- value: "LFO"
- },
- bufferSize: {
- value: 256
- },
- sampleRate: {
- value: 44100
- },
- defaults: {
- value: {
- frequency: {
- value: 1,
- min: 0,
- max: 20,
- automatable: false,
- },
- offset: {
- value: 0.85,
- min: 0,
- max: 22049,
- automatable: false,
- },
- oscillation: {
- value: 0.3,
- min: -22050,
- max: 22050,
- automatable: false,
- },
- phase: {
- value: 0,
- min: 0,
- max: 2 * Math.PI,
- automatable: false,
- }
- }
- },
- frequency: {
- get: function () {
- return this._frequency;
- },
- set: function (value) {
- this._frequency = value;
- this._phaseInc = 2 * Math.PI * this._frequency * this.bufferSize / this.sampleRate;
- }
- },
- offset: {
- get: function () {
- return this._offset;
- },
- set: function (value) {
- this._offset = value;
- }
- },
- oscillation: {
- get: function () {
- return this._oscillation;
- },
- set: function (value) {
- this._oscillation = value;
- }
- },
- phase: {
- get: function () {
- return this._phase;
- },
- set: function (value) {
- this._phase = value;
- }
- },
- target: {
- get: function () {
- return this._target;
- },
- set: function (value) {
- this._target = value;
- }
- },
- activate: {
- value: function (doActivate) {
- if (!doActivate) {
- this.output.disconnect(userContext.destination);
- } else {
- this.output.connect(userContext.destination);
- }
- }
- },
- callback: {
- value: function (callback) {
- var that = this;
- return function () {
- that._phase += that._phaseInc;
- if (that._phase > 2 * Math.PI) {
- that._phase = 0;
- }
- callback(that._target, that._offset + that._oscillation * Math.sin(that._phase));
- };
- }
- }
- });
- /* Panner
- ------------------------------------------------*/
- Tuna.prototype.Panner = function (properties) {
- if (!properties) {
- properties = this.getDefaults();
- }
- this.input = userContext.createGain();
- this.activateNode = userContext.createGain();
- this.output = userContext.createGain();
- this.panner = userContext.createPanner();
- this.activateNode.connect(this.panner);
- this.panner.connect(this.output);
- this.bypass = properties.bypass || false;
- this.x = properties.x || 0;
- this.y = properties.y || 1;
- this.z = properties.z || 1;
- this.panningModel = properties.panningModel || 0;
- this.distanceModel = properties.distanceModel || 0;
- };
- var PannerPosition = function(type) {
- return {
- enumerable: true,
- get: function () {
- return this["_" + type];
- },
- set: function (value) {
- this["_" + type] = value;
- this.panner.setPosition(this._x || 0, this._y || 0, this._z || 0);
- }
- }
- };
-
- var PannerModel = function(type) {
- return {
- enumerable: true,
- get: function () {
- return this["_" + type];
- },
- set: function (value) {
- this["_" + type] = value;
- this.panner[type] = value;
- }
- }
- };
-
- var Clamp = function(value, min, max, automatable) {
- return {
- value: value,
- min: min,
- max: max,
- automatable: automatable
- };
- };
-
- Tuna.prototype.Panner.prototype = Object.create(Super, {
- name: {
- value: "Panner"
- },
- defaults: {
- writable:true,
- value: {
- x: Clamp(0, -20, 20, false),
- y: Clamp(0, -20, 20, false),
- z: Clamp(0, -20, 20, false),
- distanceModel: Clamp(0, 0, 2, false),
- panningModel: Clamp(0, 0, 2, false)
- }
- },
- x: PannerPosition("x"),
- y: PannerPosition("y"),
- z: PannerPosition("z"),
- panningModel: PannerModel("panningModel"),
- distanceModel: PannerModel("distanceModel")
- });
- /* Volume
- ------------------------------------------------*/
- Tuna.prototype.Volume = function (properties) {
- if (!properties) {
- properties = this.getDefaults();
- }
- this.input = userContext.createGain();
- this.activateNode = userContext.createGain();
- this.output = userContext.createGain();
- this.activateNode.connect(this.output);
- this.bypass = properties.bypass || false;
- this.amount = properties.amount || this.defaults.amount.value;
- };
- Tuna.prototype.Volume.prototype = Object.create(Super, {
- name: {
- value: "Volume"
- },
- defaults: {
- writable:true,
- value: {
- amount: Clamp(0, 0, 2, false)
- }
- },
- amount: {
- enumerable: true,
- get: function () {
- return this._volume;
- },
- set: function (value) {
- this._volume = value;
- this.activateNode.gain.value = value;
- }
- }
- });
- /* Frequency
- ------------------------------------------------*/
- Tuna.prototype.Frequency = function (properties) {
- if (!properties) {
- properties = this.getDefaults();
- }
- this.trebleFilter = userContext.createBiquadFilter();
- this.trebleFilter.type = this.trebleFilter.HIGHSHELF;
- this.trebleFilter.frequency.value = 8000; // 1k+
- this.trebleFilter.Q.value = 0;
- this.midtoneFilter = userContext.createBiquadFilter();
- this.midtoneFilter.type = this.midtoneFilter.PEAKING;
- this.midtoneFilter.frequency.value = 1000; // 200-1k
- this.midtoneFilter.Q.value = 0;
- this.bassFilter = userContext.createBiquadFilter();
- this.bassFilter.type = this.bassFilter.LOWSHELF;
- this.bassFilter.frequency.value = 200; // 60-200k
- this.bassFilter.Q.value = 0;
-
- this.input = userContext.createGain();
- this.activateNode = userContext.createGain();
- this.output = userContext.createGain();
- this.activateNode.connect(this.bassFilter);
- this.bassFilter.connect(this.midtoneFilter);
- this.midtoneFilter.connect(this.trebleFilter);
- this.trebleFilter.connect(this.output);
- this.bypass = properties.bypass || false;
- this.volume = properties.volume || false;
- this.treble = properties.treble || false;
- this.midtone = properties.midtone || false;
- this.bass = properties.bass || false;
- };
- var GainValue = function(type, nodeId) {
- return {
- enumerable: true,
- get: function () {
- return this["_" + type];
- },
- set: function (value) {
- this["_" + type] = value;
- this[nodeId || type + "Filter"].gain.value = value;
- }
- };
- };
- Tuna.prototype.Frequency.prototype = Object.create(Super, {
- name: {
- value: "Frequency"
- },
- defaults: {
- writable:true,
- value: {
- volume: Clamp(1, 0, 2, false),
- treble: Clamp(0, -20, 20, false),
- midtone: Clamp(0, -20, 20, false),
- bass: Clamp(0, -20, 20, false)
- }
- },
- volume: GainValue("volume", "activateNode"),
- treble: GainValue("treble"),
- midtone: GainValue("midtone"),
- bass: GainValue("bass")
- });
- if (typeof define === "function") {
- define("Tuna", [], function () {
- return Tuna;
- });
- } else {
- window.Tuna = Tuna;
- }
- })(this);
|