mercury-engine 1.2.2 → 1.3.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.
- package/dist/mercury.js +926 -330
- package/dist/mercury.min.es5.js +2 -2
- package/dist/mercury.min.js +1 -1
- package/examples/basic/index.html +5 -4
- package/examples/midi/index.html +1 -1
- package/package.json +2 -2
package/dist/mercury.js
CHANGED
|
@@ -6656,6 +6656,12 @@ module.exports={
|
|
|
6656
6656
|
"polyphonic"
|
|
6657
6657
|
],
|
|
6658
6658
|
|
|
6659
|
+
"program" : [
|
|
6660
|
+
"programChange",
|
|
6661
|
+
"pgm",
|
|
6662
|
+
"pc"
|
|
6663
|
+
],
|
|
6664
|
+
|
|
6659
6665
|
"tempo" : [
|
|
6660
6666
|
"bpm"
|
|
6661
6667
|
],
|
|
@@ -7367,13 +7373,21 @@ function traverseTree(tree, code, level, obj){
|
|
|
7367
7373
|
let inst = map['@inst'](el['@inst'], ccode);
|
|
7368
7374
|
delete el['@inst'];
|
|
7369
7375
|
|
|
7376
|
+
// generate unique ID name for object before checking the name()
|
|
7377
|
+
// this ID is used for groups if there are any
|
|
7378
|
+
inst.functions.name = [ uniqueID(8) ];
|
|
7379
|
+
|
|
7370
7380
|
Object.keys(el).forEach((k) => {
|
|
7371
7381
|
inst = map[k](el[k], ccode, '@object', inst);
|
|
7372
7382
|
});
|
|
7383
|
+
// add the name to the all group
|
|
7384
|
+
ccode.groups.all.push(inst.functions.name[0]);
|
|
7385
|
+
|
|
7373
7386
|
// generate unique ID name for object if no name()
|
|
7374
|
-
if (!inst.functions.name){
|
|
7375
|
-
|
|
7376
|
-
}
|
|
7387
|
+
// if (!inst.functions.name){
|
|
7388
|
+
// inst.functions.name = [ uniqueID(8) ];
|
|
7389
|
+
// }
|
|
7390
|
+
// console.log('code', ccode);
|
|
7377
7391
|
// add object to complete code
|
|
7378
7392
|
ccode.objects[inst.functions.name] = inst;
|
|
7379
7393
|
return ccode;
|
|
@@ -7391,15 +7405,27 @@ function traverseTree(tree, code, level, obj){
|
|
|
7391
7405
|
inst = map[k](el[k], ccode, '@object', inst);
|
|
7392
7406
|
});
|
|
7393
7407
|
ccode.objects[inst.functions.name] = inst;
|
|
7394
|
-
} else if (name === 'all'
|
|
7408
|
+
} else if (code.groups[name]){ //name === 'all'
|
|
7395
7409
|
// if set all, set all instrument objects
|
|
7396
|
-
|
|
7410
|
+
code.groups[name].forEach((o) => {
|
|
7397
7411
|
let inst = ccode.objects[o];
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
|
|
7412
|
+
if (inst){
|
|
7413
|
+
Object.keys(el).forEach((k) => {
|
|
7414
|
+
inst = map[k](el[k], ccode, '@object', inst);
|
|
7415
|
+
});
|
|
7416
|
+
ccode.objects[inst.functions.name] = inst;
|
|
7417
|
+
}
|
|
7402
7418
|
});
|
|
7419
|
+
|
|
7420
|
+
// console.log(ccode.objects);
|
|
7421
|
+
|
|
7422
|
+
// Object.keys(ccode.objects).forEach((o) => {
|
|
7423
|
+
// let inst = ccode.objects[o];
|
|
7424
|
+
// Object.keys(el).forEach((k) => {
|
|
7425
|
+
// inst = map[k](el[k], ccode, '@object', inst);
|
|
7426
|
+
// });
|
|
7427
|
+
// ccode.objects[inst.functions.name] = inst;
|
|
7428
|
+
// });
|
|
7403
7429
|
} else {
|
|
7404
7430
|
// if name is part of global settings
|
|
7405
7431
|
let args;
|
|
@@ -7507,12 +7533,16 @@ function traverseTree(tree, code, level, obj){
|
|
|
7507
7533
|
if (func === 'add_fx'){
|
|
7508
7534
|
funcs[func].push(args);
|
|
7509
7535
|
} else {
|
|
7510
|
-
if (func === 'name'){
|
|
7511
|
-
ccode.groups.all.push(...args);
|
|
7512
|
-
}
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7536
|
+
// if (func === 'name'){
|
|
7537
|
+
// ccode.groups.all.push(...args);
|
|
7538
|
+
// } else
|
|
7539
|
+
if (func === 'group'){
|
|
7540
|
+
args.forEach((a) => {
|
|
7541
|
+
// add empty array if the group doesn't exist yet
|
|
7542
|
+
if (!ccode.groups[a]) { ccode.groups[a] = []; }
|
|
7543
|
+
// add the name of the inst to the group array
|
|
7544
|
+
ccode.groups[a].push(funcs.name[0]);
|
|
7545
|
+
});
|
|
7516
7546
|
}
|
|
7517
7547
|
funcs[func] = args;
|
|
7518
7548
|
}
|
|
@@ -7710,6 +7740,18 @@ const functionMap = {
|
|
|
7710
7740
|
'rectF' : (...v) => {
|
|
7711
7741
|
return Gen.squareFloat(...v);
|
|
7712
7742
|
},
|
|
7743
|
+
'binaryBeat' : (...v) => {
|
|
7744
|
+
return Gen.binaryBeat(...v);
|
|
7745
|
+
},
|
|
7746
|
+
'binary' : (...v) => {
|
|
7747
|
+
return Gen.binaryBeat(...v);
|
|
7748
|
+
},
|
|
7749
|
+
'spacingBeat' : (...v) => {
|
|
7750
|
+
return Gen.spacingBeat(...v);
|
|
7751
|
+
},
|
|
7752
|
+
'spacing' : (...v) => {
|
|
7753
|
+
return Gen.spacingBeat(...v);
|
|
7754
|
+
},
|
|
7713
7755
|
//
|
|
7714
7756
|
// Algorithmic Methods
|
|
7715
7757
|
//
|
|
@@ -7850,6 +7892,40 @@ const functionMap = {
|
|
|
7850
7892
|
v[1] = Math.max(2, (Array.isArray(v[1])) ? v[1][0] : v[1]);
|
|
7851
7893
|
return Rand.expand(v[0], v[1]);
|
|
7852
7894
|
},
|
|
7895
|
+
// markov chain methods
|
|
7896
|
+
// combine the markovTrain with markovChain
|
|
7897
|
+
// first train the model based on a list
|
|
7898
|
+
// then generate a list output from the chain
|
|
7899
|
+
'markovTrain' : (...v) => {
|
|
7900
|
+
// generate markovchain from the incoming list
|
|
7901
|
+
let markov = new Rand.DeepMarkovChain(...v);
|
|
7902
|
+
// create string of data
|
|
7903
|
+
let data = markov.stringify();
|
|
7904
|
+
// clear data and delete
|
|
7905
|
+
markov.clear();
|
|
7906
|
+
markov = null;
|
|
7907
|
+
// output the table as a string array to use for generating
|
|
7908
|
+
return [ data ];
|
|
7909
|
+
},
|
|
7910
|
+
'markov' : (...v) => {
|
|
7911
|
+
return functionMap['markovTrain'](...v);
|
|
7912
|
+
},
|
|
7913
|
+
'markovChain' : (...v) => {
|
|
7914
|
+
// train from a markov table and generate a chain
|
|
7915
|
+
let markov = new Rand.DeepMarkovChain();
|
|
7916
|
+
markov.parse(v[1]);
|
|
7917
|
+
// set the seed based on the global seed
|
|
7918
|
+
markov.seed(Rand.getSeed());
|
|
7919
|
+
let gen = markov.chain(v[0]);
|
|
7920
|
+
// clear the data and remove markov
|
|
7921
|
+
markov.clear();
|
|
7922
|
+
markov = null;
|
|
7923
|
+
// return generated array
|
|
7924
|
+
return gen;
|
|
7925
|
+
},
|
|
7926
|
+
'chain' : (...v) => {
|
|
7927
|
+
return functionMap['markovChain'](...v);
|
|
7928
|
+
},
|
|
7853
7929
|
//
|
|
7854
7930
|
// Transformational Methods
|
|
7855
7931
|
//
|
|
@@ -8161,15 +8237,19 @@ const functionMap = {
|
|
|
8161
8237
|
'chordsNamed' : (...v) => {
|
|
8162
8238
|
return functionMap.chordsFromNames(v);
|
|
8163
8239
|
},
|
|
8240
|
+
// translate text to ASCII
|
|
8241
|
+
'textCode' : (...v) => {
|
|
8242
|
+
return TL.textCode(...v);
|
|
8243
|
+
},
|
|
8164
8244
|
//
|
|
8165
8245
|
// Statistic Methods
|
|
8166
8246
|
//
|
|
8167
8247
|
// IMPLEMENTATION NEEDED
|
|
8168
8248
|
// maximum
|
|
8169
8249
|
// minimum
|
|
8170
|
-
// mean
|
|
8171
|
-
// median
|
|
8172
|
-
// mode
|
|
8250
|
+
// mean / average
|
|
8251
|
+
// median / center
|
|
8252
|
+
// mode / common
|
|
8173
8253
|
|
|
8174
8254
|
//
|
|
8175
8255
|
// Utility Methods
|
|
@@ -8271,7 +8351,49 @@ const functionMap = {
|
|
|
8271
8351
|
},
|
|
8272
8352
|
'ceil' : (v) => {
|
|
8273
8353
|
return Util.arrayCalc(v, 0, (a) => Math.ceil(a));
|
|
8274
|
-
}
|
|
8354
|
+
},
|
|
8355
|
+
// compare two lists for equals
|
|
8356
|
+
'equals' : (...v) => {
|
|
8357
|
+
return Util.arrayCalc(v[0], v[1], (a,b) => Number(a === b));
|
|
8358
|
+
},
|
|
8359
|
+
'eq' : (...v) => {
|
|
8360
|
+
return functionMap.equals(...v);
|
|
8361
|
+
},
|
|
8362
|
+
// compare two lists for not equal
|
|
8363
|
+
'notEquals' : (...v) => {
|
|
8364
|
+
return Util.arrayCalc(v[0], v[1], (a,b) => Number(a !== b));
|
|
8365
|
+
},
|
|
8366
|
+
'neq' : (...v) => {
|
|
8367
|
+
return functionMap.notEquals(...v);
|
|
8368
|
+
},
|
|
8369
|
+
// compare left for greater than right list
|
|
8370
|
+
'greater' : (...v) => {
|
|
8371
|
+
return Util.arrayCalc(v[0], v[1], (a,b) => Number(a > b));
|
|
8372
|
+
},
|
|
8373
|
+
'gt' : (...v) => {
|
|
8374
|
+
return functionMap.greater(...v);
|
|
8375
|
+
},
|
|
8376
|
+
// compare left for greater than or equal to right list
|
|
8377
|
+
'greaterEquals' : (...v) => {
|
|
8378
|
+
return Util.arrayCalc(v[0], v[1], (a,b) => Number(a >= b));
|
|
8379
|
+
},
|
|
8380
|
+
'gte' : (...v) => {
|
|
8381
|
+
return functionMap.greaterEquals(...v);
|
|
8382
|
+
},
|
|
8383
|
+
// compare left for less than right list
|
|
8384
|
+
'less' : (...v) => {
|
|
8385
|
+
return Util.arrayCalc(v[0], v[1], (a,b) => Number(a < b));
|
|
8386
|
+
},
|
|
8387
|
+
'lt' : (...v) => {
|
|
8388
|
+
return functionMap.less(...v);
|
|
8389
|
+
},
|
|
8390
|
+
// compare left for less than or equal to right list
|
|
8391
|
+
'lessEquals' : (...v) => {
|
|
8392
|
+
return Util.arrayCalc(v[0], v[1], (a,b) => Number(a <= b));
|
|
8393
|
+
},
|
|
8394
|
+
'lte' : (...v) => {
|
|
8395
|
+
return functionMap.lessEquals(...v);
|
|
8396
|
+
},
|
|
8275
8397
|
}
|
|
8276
8398
|
exports.functionMap = functionMap;
|
|
8277
8399
|
},{"total-serialism":47}],33:[function(require,module,exports){
|
|
@@ -12190,9 +12312,11 @@ class MarkovChain {
|
|
|
12190
12312
|
read(t){
|
|
12191
12313
|
// read a markov chain table from a json file
|
|
12192
12314
|
if (Array.isArray(t) || typeof t !== 'object'){
|
|
12193
|
-
|
|
12315
|
+
console.error(`Error: input is not a valid json formatted table. If your input is an array use train() instead.`);
|
|
12316
|
+
return false;
|
|
12194
12317
|
}
|
|
12195
12318
|
this._table = t;
|
|
12319
|
+
return true;
|
|
12196
12320
|
}
|
|
12197
12321
|
clear(){
|
|
12198
12322
|
// empty the transition probabilities
|
|
@@ -12263,18 +12387,46 @@ exports.MarkovChain = MarkovChain;
|
|
|
12263
12387
|
// @method chain() -> generate an array of values (default length=2)
|
|
12264
12388
|
//
|
|
12265
12389
|
class DeepMarkov {
|
|
12266
|
-
constructor(data){
|
|
12390
|
+
constructor(data, order){
|
|
12267
12391
|
// transition probabilities table
|
|
12268
12392
|
this._table = new Map();
|
|
12269
12393
|
// train if dataset is provided
|
|
12270
|
-
if (data) { this.train(data) };
|
|
12394
|
+
if (data) { this.train(data, order) };
|
|
12271
12395
|
// current state of markov chain
|
|
12272
12396
|
this._state = '';
|
|
12273
12397
|
}
|
|
12274
12398
|
get table(){
|
|
12275
|
-
// return copy of object
|
|
12399
|
+
// return copy of Map object
|
|
12276
12400
|
return new Map(JSON.parse(JSON.stringify(Array.from(this._table))));
|
|
12277
12401
|
}
|
|
12402
|
+
read(t){
|
|
12403
|
+
// read a markov chain table from a Map() generated with DeepMarkov
|
|
12404
|
+
if (Array.isArray(t) || t instanceof Map === false){
|
|
12405
|
+
console.error(`Error: input is not a valid Map() formatted table. If your input is an array use train() instead.`);
|
|
12406
|
+
return false;
|
|
12407
|
+
}
|
|
12408
|
+
this._table = t;
|
|
12409
|
+
return true;
|
|
12410
|
+
}
|
|
12411
|
+
stringify(){
|
|
12412
|
+
// return stringified version of the DeepMarkov table
|
|
12413
|
+
return JSON.stringify(this._table, replacer);
|
|
12414
|
+
}
|
|
12415
|
+
parse(p){
|
|
12416
|
+
// parse an incoming string to a Map() for transition table
|
|
12417
|
+
try {
|
|
12418
|
+
let parsed = JSON.parse(p, reviver);
|
|
12419
|
+
if (parsed instanceof Map === false){
|
|
12420
|
+
console.error(`Error: input is not a valid string that can be parsed to a Map().`)
|
|
12421
|
+
return false;
|
|
12422
|
+
}
|
|
12423
|
+
this._table = parsed;
|
|
12424
|
+
return true;
|
|
12425
|
+
} catch (e) {
|
|
12426
|
+
console.error(`Error: input is not a valid string that can be parsed to a Map().`);
|
|
12427
|
+
return false;
|
|
12428
|
+
}
|
|
12429
|
+
}
|
|
12278
12430
|
clear(){
|
|
12279
12431
|
// empty the transition probabilities
|
|
12280
12432
|
this._table = new Map();
|
|
@@ -12348,6 +12500,30 @@ class DeepMarkov {
|
|
|
12348
12500
|
}
|
|
12349
12501
|
exports.DeepMarkov = DeepMarkov;
|
|
12350
12502
|
exports.DeepMarkovChain = DeepMarkov;
|
|
12503
|
+
|
|
12504
|
+
// functions thanks to:
|
|
12505
|
+
// https://stackoverflow.com/questions/29085197/how-do-you-json-stringify-an-es6-map
|
|
12506
|
+
// helper function for Stringifying a Map() in DeepMarkov
|
|
12507
|
+
function replacer(key, value) {
|
|
12508
|
+
if (value instanceof Map) {
|
|
12509
|
+
return {
|
|
12510
|
+
dataType: 'Map',
|
|
12511
|
+
value: [...value]
|
|
12512
|
+
// value: [Array.from(value.entries())],
|
|
12513
|
+
};
|
|
12514
|
+
}
|
|
12515
|
+
return value;
|
|
12516
|
+
}
|
|
12517
|
+
|
|
12518
|
+
// helper function for parsing a Map() in DeepMarkov
|
|
12519
|
+
function reviver(key, value) {
|
|
12520
|
+
if (typeof value === 'object' && value !== null) {
|
|
12521
|
+
if (value.dataType === 'Map') {
|
|
12522
|
+
return new Map(value.value);
|
|
12523
|
+
}
|
|
12524
|
+
}
|
|
12525
|
+
return value;
|
|
12526
|
+
}
|
|
12351
12527
|
},{"./gen-basic.js":48,"./statistic":51,"./transform.js":52,"./utility":54,"seedrandom":36}],51:[function(require,module,exports){
|
|
12352
12528
|
//=======================================================================
|
|
12353
12529
|
// statistic.js
|
|
@@ -12545,7 +12721,11 @@ exports.diff = change;
|
|
|
12545
12721
|
// by Timo Hoogland (@t.mo / @tmhglnd), www.timohoogland.com
|
|
12546
12722
|
// MIT License
|
|
12547
12723
|
//
|
|
12548
|
-
//
|
|
12724
|
+
// Methods that transform number sequences
|
|
12725
|
+
// These are called the "transformers"
|
|
12726
|
+
// A transformer always takes an input list as the first argument
|
|
12727
|
+
// A transformer never destructively changes the input list
|
|
12728
|
+
// The output of the transformer is the modified input list(s)
|
|
12549
12729
|
//
|
|
12550
12730
|
// TODO:
|
|
12551
12731
|
// - make invert() work with note-values 'c' etc.
|
|
@@ -12561,7 +12741,7 @@ exports.diff = change;
|
|
|
12561
12741
|
// require the Utility methods
|
|
12562
12742
|
// const Rand = require('./gen-stochastic');
|
|
12563
12743
|
const { sort } = require('./statistic');
|
|
12564
|
-
const {
|
|
12744
|
+
const { flat, add, max, min, lerp, toArray, size, unique, arrayCombinations } = require('./utility');
|
|
12565
12745
|
|
|
12566
12746
|
// Duplicate an array multiple times,
|
|
12567
12747
|
// optionaly add an offset to every value when duplicating
|
|
@@ -12574,11 +12754,12 @@ const { flatten, add, max, min, lerp, toArray, size } = require('./utility');
|
|
|
12574
12754
|
//
|
|
12575
12755
|
function clone(a=[0], ...c){
|
|
12576
12756
|
a = toArray(a);
|
|
12577
|
-
// flatten clone array if multi-dimensional
|
|
12578
12757
|
if (!c.length) {
|
|
12758
|
+
// return input if no clone arguments
|
|
12579
12759
|
return a;
|
|
12580
12760
|
} else {
|
|
12581
|
-
|
|
12761
|
+
// flatten clone array if multi-dimensional
|
|
12762
|
+
c = flat(c);
|
|
12582
12763
|
}
|
|
12583
12764
|
let arr = [];
|
|
12584
12765
|
for (let i=0; i<c.length; i++){
|
|
@@ -12594,11 +12775,11 @@ exports.clone = clone;
|
|
|
12594
12775
|
// @params {Array0, Array1, ..., Array-n} -> Arrays to join
|
|
12595
12776
|
// @return {Array}
|
|
12596
12777
|
//
|
|
12597
|
-
function combine(...
|
|
12598
|
-
if (!
|
|
12778
|
+
function combine(...arrs){
|
|
12779
|
+
if (!arrs.length){ return [0]; }
|
|
12599
12780
|
let arr = [];
|
|
12600
|
-
for (let i=0; i<
|
|
12601
|
-
arr = arr.concat(
|
|
12781
|
+
for (let i=0; i<arrs.length; i++){
|
|
12782
|
+
arr = arr.concat(arrs[i]);
|
|
12602
12783
|
}
|
|
12603
12784
|
return arr;
|
|
12604
12785
|
}
|
|
@@ -12644,8 +12825,8 @@ exports.every = every;
|
|
|
12644
12825
|
// flatten a multidimensional array. Optionally set the depth
|
|
12645
12826
|
// for the flattening
|
|
12646
12827
|
//
|
|
12647
|
-
exports.flatten =
|
|
12648
|
-
exports.flat =
|
|
12828
|
+
exports.flatten = flat;
|
|
12829
|
+
exports.flat = flat;
|
|
12649
12830
|
|
|
12650
12831
|
// similar to every(), but instead of specifying bars/divisions
|
|
12651
12832
|
// this method allows you to specify the exact length of the array
|
|
@@ -12734,12 +12915,15 @@ function invert(a=[0], lo, hi){
|
|
|
12734
12915
|
a = toArray(a);
|
|
12735
12916
|
|
|
12736
12917
|
if (lo === undefined){
|
|
12918
|
+
// if no center value set lo/hi based on min/max
|
|
12737
12919
|
hi = max(a);
|
|
12738
12920
|
lo = min(a);
|
|
12739
12921
|
} else if (hi === undefined){
|
|
12922
|
+
// if no hi defined set hi to be same as lo
|
|
12740
12923
|
hi = lo;
|
|
12741
12924
|
}
|
|
12742
12925
|
return a.slice().map(v => {
|
|
12926
|
+
// apply the algorithm recursively for all items
|
|
12743
12927
|
if (Array.isArray(v)){
|
|
12744
12928
|
return invert(v, lo, hi);
|
|
12745
12929
|
}
|
|
@@ -12753,17 +12937,19 @@ exports.invert = invert;
|
|
|
12753
12937
|
// @param {Array0, Array1, ..., Array-n} -> arrays to interleave
|
|
12754
12938
|
// @return {Array}
|
|
12755
12939
|
//
|
|
12756
|
-
function lace(...
|
|
12757
|
-
if (!
|
|
12940
|
+
function lace(...arrs){
|
|
12941
|
+
if (!arrs.length){ return [0]; }
|
|
12942
|
+
// get the length of longest list
|
|
12758
12943
|
var l = 0;
|
|
12759
|
-
for (let i=0; i<
|
|
12760
|
-
|
|
12761
|
-
l = Math.max(
|
|
12944
|
+
for (let i=0; i<arrs.length; i++){
|
|
12945
|
+
arrs[i] = toArray(arrs[i]);
|
|
12946
|
+
l = Math.max(arrs[i].length, l);
|
|
12762
12947
|
}
|
|
12948
|
+
// for the max length push all values of the various lists
|
|
12763
12949
|
var arr = [];
|
|
12764
12950
|
for (var i=0; i<l; i++){
|
|
12765
|
-
for (var k=0; k<
|
|
12766
|
-
let v =
|
|
12951
|
+
for (var k=0; k<arrs.length; k++){
|
|
12952
|
+
let v = arrs[k][i];
|
|
12767
12953
|
if (v !== undefined){ arr.push(v); }
|
|
12768
12954
|
}
|
|
12769
12955
|
}
|
|
@@ -12785,7 +12971,8 @@ function lookup(idx=[0], arr=[0]){
|
|
|
12785
12971
|
arr = toArray(arr);
|
|
12786
12972
|
let a = [];
|
|
12787
12973
|
let len = arr.length;
|
|
12788
|
-
for (let i
|
|
12974
|
+
for (let i=0; i<idx.length; i++){
|
|
12975
|
+
// recursively lookup values for multidimensional arrays
|
|
12789
12976
|
if (Array.isArray(idx[i])){
|
|
12790
12977
|
a.push(lookup(idx[i], arr));
|
|
12791
12978
|
} else {
|
|
@@ -12806,18 +12993,18 @@ exports.lookup = lookup;
|
|
|
12806
12993
|
// @params {Array0, Array1, ..., Array-n} -> Arrays to merge
|
|
12807
12994
|
// @return {Array}
|
|
12808
12995
|
//
|
|
12809
|
-
function merge(...
|
|
12810
|
-
if (!
|
|
12996
|
+
function merge(...arrs){
|
|
12997
|
+
if (!arrs.length){ return [0]; }
|
|
12811
12998
|
let l = 0;
|
|
12812
|
-
for (let i=0; i<
|
|
12813
|
-
|
|
12814
|
-
l = Math.max(
|
|
12999
|
+
for (let i=0; i<arrs.length; i++){
|
|
13000
|
+
arrs[i] = toArray(arrs[i]);
|
|
13001
|
+
l = Math.max(arrs[i].length, l);
|
|
12815
13002
|
}
|
|
12816
13003
|
let arr = [];
|
|
12817
13004
|
for (let i=0; i<l; i++){
|
|
12818
13005
|
let a = [];
|
|
12819
|
-
for (let k=0; k<
|
|
12820
|
-
let v =
|
|
13006
|
+
for (let k=0; k<arrs.length; k++){
|
|
13007
|
+
let v = arrs[k][i];
|
|
12821
13008
|
if (v !== undefined){
|
|
12822
13009
|
if (Array.isArray(v)) a.push(...v);
|
|
12823
13010
|
else a.push(v);
|
|
@@ -12862,7 +13049,7 @@ function repeat(arr=[0], rep=1){
|
|
|
12862
13049
|
rep = toArray(rep);
|
|
12863
13050
|
|
|
12864
13051
|
let a = [];
|
|
12865
|
-
for (let i
|
|
13052
|
+
for (let i=0; i<arr.length; i++){
|
|
12866
13053
|
let r = rep[i % rep.length];
|
|
12867
13054
|
r = (isNaN(r) || r < 0)? 0 : r;
|
|
12868
13055
|
for (let k=0; k<r; k++){
|
|
@@ -12916,7 +13103,7 @@ exports.sort = sort;
|
|
|
12916
13103
|
// @params {Number|Array} -> slice points
|
|
12917
13104
|
// @return {Array}
|
|
12918
13105
|
//
|
|
12919
|
-
function slice(a=[0], s=[
|
|
13106
|
+
function slice(a=[0], s=[0], r=true){
|
|
12920
13107
|
a = toArray(a);
|
|
12921
13108
|
s = toArray(s);
|
|
12922
13109
|
|
|
@@ -12930,7 +13117,9 @@ function slice(a=[0], s=[1], r=true){
|
|
|
12930
13117
|
}
|
|
12931
13118
|
}
|
|
12932
13119
|
if (r){
|
|
12933
|
-
|
|
13120
|
+
let rest = a.slice(_s, a.length);
|
|
13121
|
+
// attach the rest if not an empty array and r=true
|
|
13122
|
+
if (rest.length > 0){ arr.push(rest); }
|
|
12934
13123
|
}
|
|
12935
13124
|
return arr;
|
|
12936
13125
|
}
|
|
@@ -12986,6 +13175,20 @@ function spray(values=[0], beats=[0]){
|
|
|
12986
13175
|
}
|
|
12987
13176
|
exports.spray = spray;
|
|
12988
13177
|
|
|
13178
|
+
// Alternate through 2 or multiple lists consecutively
|
|
13179
|
+
// Gives a similar result as lace except the output
|
|
13180
|
+
// length is the lowest common denominator of the input lists
|
|
13181
|
+
// so that every combination of consecutive values is included
|
|
13182
|
+
//
|
|
13183
|
+
// @param {Array0, Array1, ..., Array-n} -> arrays to interleave
|
|
13184
|
+
// @return {Array} -> array of results 1 dimension less
|
|
13185
|
+
//
|
|
13186
|
+
function step(...arrs){
|
|
13187
|
+
if (!arrs.length){ return [ 0 ] }
|
|
13188
|
+
return flat(arrayCombinations(...arrs), 1);
|
|
13189
|
+
}
|
|
13190
|
+
exports.step = step;
|
|
13191
|
+
|
|
12989
13192
|
// stretch (or shrink) an array of numbers to a specified length
|
|
12990
13193
|
// interpolating the values to fill in the gaps.
|
|
12991
13194
|
// TO-DO: Interpolations options are: none, linear, cosine, cubic
|
|
@@ -13019,15 +13222,9 @@ function stretch(a=[0], len=1, mode='linear'){
|
|
|
13019
13222
|
}
|
|
13020
13223
|
exports.stretch = stretch;
|
|
13021
13224
|
|
|
13225
|
+
// placeholder for unique from Utils.js
|
|
13022
13226
|
// filter duplicate items from an array
|
|
13023
13227
|
// does not account for 2-dimensional arrays in the array
|
|
13024
|
-
//
|
|
13025
|
-
// @param {Array} -> array to filter
|
|
13026
|
-
// @return {Array}
|
|
13027
|
-
//
|
|
13028
|
-
function unique(a=[0]){
|
|
13029
|
-
return [...new Set(toArray(a))];
|
|
13030
|
-
}
|
|
13031
13228
|
exports.unique = unique;
|
|
13032
13229
|
|
|
13033
13230
|
},{"./statistic":51,"./utility":54}],53:[function(require,module,exports){
|
|
@@ -13406,22 +13603,43 @@ exports.rtof = relativeToFreq;
|
|
|
13406
13603
|
// Also offsets the values with the root note selected
|
|
13407
13604
|
//
|
|
13408
13605
|
// @params {Array/Number} -> Array of relative semitones
|
|
13606
|
+
// @params {String} -> Scale name (optional)
|
|
13607
|
+
// @params {String/Int} -> Root offset
|
|
13409
13608
|
// @return {Array/Number} -> mapped to scale
|
|
13410
13609
|
//
|
|
13411
|
-
function mapToScale(a=[0]){
|
|
13412
|
-
|
|
13610
|
+
function mapToScale(a=[0], s, r){
|
|
13611
|
+
// get the global settings
|
|
13612
|
+
let scale = getSettings().map;
|
|
13613
|
+
let root = getSettings().rootInt;
|
|
13614
|
+
// if a scale is provided and is not undefined
|
|
13615
|
+
if (s && Scales[s]){
|
|
13616
|
+
scale = Scales[s];
|
|
13617
|
+
}
|
|
13618
|
+
// if a root is provided
|
|
13619
|
+
if (r) {
|
|
13620
|
+
root = isNaN(Number(r)) ? chromaToRelative(r) : Math.floor(r);
|
|
13621
|
+
}
|
|
13622
|
+
// apply recursively through the entire array
|
|
13623
|
+
return _mapToScale(a, scale, root);
|
|
13624
|
+
}
|
|
13625
|
+
exports.mapToScale = mapToScale;
|
|
13626
|
+
exports.toScale = mapToScale;
|
|
13627
|
+
|
|
13628
|
+
// private function for mapToScale()
|
|
13629
|
+
//
|
|
13630
|
+
function _mapToScale(arr, scale, root){
|
|
13631
|
+
if (!Array.isArray(arr)) {
|
|
13413
13632
|
// detuning float
|
|
13414
|
-
let d =
|
|
13633
|
+
let d = arr - Math.floor(arr);
|
|
13415
13634
|
// selected semitone
|
|
13416
|
-
let s = Math.floor(((
|
|
13635
|
+
let s = Math.floor(((arr % 12) + 12) % 12);
|
|
13417
13636
|
// octave offset
|
|
13418
|
-
let o = Math.floor(
|
|
13419
|
-
return notation.map[s] + o + d + notation.rootInt;
|
|
13637
|
+
let o = Math.floor(arr / 12) * 12;
|
|
13638
|
+
// return notation.map[s] + o + d + notation.rootInt;
|
|
13639
|
+
return scale[s] + o + d + root;
|
|
13420
13640
|
}
|
|
13421
|
-
return
|
|
13641
|
+
return arr.map(x => _mapToScale(x, scale, root));
|
|
13422
13642
|
}
|
|
13423
|
-
exports.mapToScale = mapToScale;
|
|
13424
|
-
exports.toScale = mapToScale;
|
|
13425
13643
|
|
|
13426
13644
|
// Map an array of relative semitone intervals to scale and
|
|
13427
13645
|
// output in specified octave as midi value
|
|
@@ -13671,6 +13889,23 @@ function timevalueRatio(x){
|
|
|
13671
13889
|
// function tonetimeRatio(x){
|
|
13672
13890
|
// }
|
|
13673
13891
|
|
|
13892
|
+
// Convert a string or array of strings to the
|
|
13893
|
+
// ASCII code values that belong to those characters
|
|
13894
|
+
// ASCII is the American Standard Code for Information Interchange
|
|
13895
|
+
//
|
|
13896
|
+
// @param {String/Array} -> string to convert
|
|
13897
|
+
// @return {Array} -> array of integers
|
|
13898
|
+
//
|
|
13899
|
+
function textToCode(a=[0]){
|
|
13900
|
+
if (!Array.isArray(a)){
|
|
13901
|
+
return String(a).split('').map(c => c.charCodeAt(0));
|
|
13902
|
+
}
|
|
13903
|
+
return a.map(x => textToCode(x));
|
|
13904
|
+
}
|
|
13905
|
+
exports.textToCode = textToCode;
|
|
13906
|
+
exports.textCode = textToCode;
|
|
13907
|
+
exports.ttoc = textToCode;
|
|
13908
|
+
|
|
13674
13909
|
//=======================================================================
|
|
13675
13910
|
// Scala class
|
|
13676
13911
|
//
|
|
@@ -14231,6 +14466,31 @@ function arrayCalc(a=0, v=0, func=()=>{return a;}){
|
|
|
14231
14466
|
}
|
|
14232
14467
|
exports.arrayCalc = arrayCalc;
|
|
14233
14468
|
|
|
14469
|
+
// Alternate through 2 or multiple lists consecutively
|
|
14470
|
+
// The output length is the lowest common denominator of the input lists
|
|
14471
|
+
// so that every combination of consecutive values is included
|
|
14472
|
+
// This function is used to allow arrays as input for Generators
|
|
14473
|
+
// And for the step function for algorithmic composition
|
|
14474
|
+
//
|
|
14475
|
+
// @param {Array0, Array1, ..., Array-n} -> arrays to interleave
|
|
14476
|
+
// @return {Array} -> outputs a 2D array of the results
|
|
14477
|
+
//
|
|
14478
|
+
function arrayCombinations(...arrs){
|
|
14479
|
+
// make sure all values are array
|
|
14480
|
+
arrs = arrs.map(a => toArray(a));
|
|
14481
|
+
// the output is the unique list sizes multiplied
|
|
14482
|
+
let sizes = unique(arrs.map(a => a.length));
|
|
14483
|
+
let iters = 1;
|
|
14484
|
+
sizes.forEach((l) => iters *= l);
|
|
14485
|
+
// iterate over the total amount pushing the items to array
|
|
14486
|
+
let arr = [];
|
|
14487
|
+
for (let i=0; i<iters; i++){
|
|
14488
|
+
arr.push(arrs.map((e) => e[i % e.length] ));
|
|
14489
|
+
}
|
|
14490
|
+
return arr;
|
|
14491
|
+
}
|
|
14492
|
+
exports.arrayCombinations = arrayCombinations;
|
|
14493
|
+
|
|
14234
14494
|
// flatten a multidimensional array. Optionally set the depth
|
|
14235
14495
|
// for the flattening
|
|
14236
14496
|
//
|
|
@@ -14329,6 +14589,17 @@ function signedNormalize(a=[0]){
|
|
|
14329
14589
|
exports.signedNormalize = signedNormalize;
|
|
14330
14590
|
exports.snorm = signedNormalize;
|
|
14331
14591
|
|
|
14592
|
+
// filter duplicate items from an array
|
|
14593
|
+
// does not account for 2-dimensional arrays in the array
|
|
14594
|
+
//
|
|
14595
|
+
// @param {Array} -> array to filter
|
|
14596
|
+
// @return {Array}
|
|
14597
|
+
//
|
|
14598
|
+
function unique(a=[0]){
|
|
14599
|
+
return [...new Set(toArray(a))];
|
|
14600
|
+
}
|
|
14601
|
+
exports.unique = unique;
|
|
14602
|
+
|
|
14332
14603
|
// Plot an array of values to the console in the form of an
|
|
14333
14604
|
// ascii chart and return chart from function. If you just want the
|
|
14334
14605
|
// chart returned as text and not log to console set { log: false }.
|
|
@@ -14934,7 +15205,7 @@ const fxMap = {
|
|
|
14934
15205
|
'room' : (params) => {
|
|
14935
15206
|
return new Reverb(params);
|
|
14936
15207
|
},
|
|
14937
|
-
'
|
|
15208
|
+
'hall' : (params) => {
|
|
14938
15209
|
return new Reverb(params);
|
|
14939
15210
|
},
|
|
14940
15211
|
'reverb' : (params) => {
|
|
@@ -14970,9 +15241,9 @@ const fxMap = {
|
|
|
14970
15241
|
'echo' : (params) => {
|
|
14971
15242
|
return new Delay(params);
|
|
14972
15243
|
},
|
|
14973
|
-
'ppDelay' : (params) => {
|
|
14974
|
-
|
|
14975
|
-
},
|
|
15244
|
+
// 'ppDelay' : (params) => {
|
|
15245
|
+
// return new PingPongDelay(params);
|
|
15246
|
+
// },
|
|
14976
15247
|
// 'freeverb' : (params) => {
|
|
14977
15248
|
// return new FreeVerb(params);
|
|
14978
15249
|
// },
|
|
@@ -14990,32 +15261,52 @@ module.exports = fxMap;
|
|
|
14990
15261
|
// Programmed with a custom AudioWorkletProcessor, see effects/Processors.js
|
|
14991
15262
|
//
|
|
14992
15263
|
const DownSampler = function(_params){
|
|
14993
|
-
|
|
14994
|
-
|
|
15264
|
+
// apply the default values and convert to arrays where necessary
|
|
15265
|
+
_params = Util.mapDefaults(_params, [ 0.5, 1 ]);
|
|
15266
|
+
this._down = Util.toArray(_params[0]);
|
|
15267
|
+
this._wet = Util.toArray(_params[1]);
|
|
15268
|
+
|
|
14995
15269
|
// ToneAudioNode has all the tone effect parameters
|
|
14996
15270
|
this._fx = new Tone.ToneAudioNode();
|
|
15271
|
+
|
|
15272
|
+
// The crossfader mix
|
|
15273
|
+
this._mix = new Tone.Add();
|
|
15274
|
+
this._mixDry = new Tone.Gain(0).connect(this._mix.input);
|
|
15275
|
+
// this._mixWet = new Tone.Gain(0.5).connect(this._mix.addend);
|
|
15276
|
+
|
|
14997
15277
|
// A gain node for connecting with input and output
|
|
14998
|
-
this._fx.input = new Tone.Gain(1);
|
|
14999
|
-
this._fx.output = new Tone.Gain(1);
|
|
15278
|
+
this._fx.input = new Tone.Gain(1).connect(this._mixDry);
|
|
15279
|
+
this._fx.output = new Tone.Gain(1).connect(this._mix.addend);
|
|
15280
|
+
|
|
15000
15281
|
// the fx processor
|
|
15001
15282
|
this._fx.workletNode = Tone.getContext().createAudioWorkletNode('downsampler-processor');
|
|
15283
|
+
|
|
15002
15284
|
// connect input, fx and output
|
|
15003
15285
|
this._fx.input.chain(this._fx.workletNode, this._fx.output);
|
|
15004
15286
|
|
|
15005
15287
|
this.set = function(c, time, bpm){
|
|
15006
15288
|
// some parameter mapping changing input range 0-1 to 1-inf
|
|
15007
|
-
|
|
15008
|
-
|
|
15289
|
+
const p = this._fx.workletNode.parameters.get('down');
|
|
15290
|
+
const d = Math.floor(1 / (1 - Util.clip(Util.getParam(this._down, c) ** 0.25, 0, 0.999)));
|
|
15291
|
+
|
|
15009
15292
|
p.setValueAtTime(Util.assureNum(d), time);
|
|
15293
|
+
|
|
15294
|
+
const w = Util.clip(Util.getParam(this._wet, c), 0, 1);
|
|
15295
|
+
this._fx.output.gain.setValueAtTime(w, time);
|
|
15296
|
+
this._mixDry.gain.setValueAtTime(1 - w, time);
|
|
15010
15297
|
}
|
|
15011
15298
|
|
|
15012
15299
|
this.chain = function(){
|
|
15013
|
-
return { 'send' : this._fx, 'return' : this.
|
|
15300
|
+
return { 'send' : this._fx, 'return' : this._mix }
|
|
15014
15301
|
}
|
|
15015
15302
|
|
|
15016
15303
|
this.delete = function(){
|
|
15017
|
-
this._fx.
|
|
15018
|
-
|
|
15304
|
+
const nodes = [ this._fx, this._mix, this._mixDry ];
|
|
15305
|
+
|
|
15306
|
+
nodes.forEach((n) => {
|
|
15307
|
+
n.disconnect();
|
|
15308
|
+
n.dispose();
|
|
15309
|
+
});
|
|
15019
15310
|
}
|
|
15020
15311
|
}
|
|
15021
15312
|
|
|
@@ -15024,37 +15315,63 @@ const DownSampler = function(_params){
|
|
|
15024
15315
|
// distortion is applied on the overdrive parameter
|
|
15025
15316
|
//
|
|
15026
15317
|
const TanhDistortion = function(_params){
|
|
15027
|
-
|
|
15318
|
+
_params = Util.mapDefaults(_params, [ 4, 1 ]);
|
|
15319
|
+
// apply the default values and convert to arrays where necessary
|
|
15320
|
+
this._drive = Util.toArray(_params[0]);
|
|
15321
|
+
this._wet = Util.toArray(_params[1]);
|
|
15322
|
+
|
|
15323
|
+
console.log('Distortion with:', this._drive, this._wet);
|
|
15324
|
+
|
|
15325
|
+
// The crossfader for wet-dry (originally implemented with CrossFade)
|
|
15326
|
+
// this._mix = new Tone.CrossFade();
|
|
15327
|
+
this._mix = new Tone.Add();
|
|
15328
|
+
this._mixWet = new Tone.Gain(0).connect(this._mix.input);
|
|
15329
|
+
this._mixDry = new Tone.Gain(1).connect(this._mix.addend);
|
|
15028
15330
|
|
|
15029
15331
|
// ToneAudioNode has all the tone effect parameters
|
|
15030
15332
|
this._fx = new Tone.ToneAudioNode();
|
|
15031
15333
|
// A gain node for connecting with input and output
|
|
15032
|
-
this._fx.input = new Tone.Gain(1);
|
|
15033
|
-
this._fx.output = new Tone.Gain(1);
|
|
15334
|
+
this._fx.input = new Tone.Gain(1).connect(this._mixDry);
|
|
15335
|
+
this._fx.output = new Tone.Gain(1).connect(this._mixWet);
|
|
15336
|
+
|
|
15034
15337
|
// the fx processor
|
|
15035
15338
|
this._fx.workletNode = Tone.getContext().createAudioWorkletNode('tanh-distortion-processor');
|
|
15036
|
-
|
|
15339
|
+
|
|
15340
|
+
// connect input, fx, output and wetdry
|
|
15037
15341
|
this._fx.input.chain(this._fx.workletNode, this._fx.output);
|
|
15038
15342
|
|
|
15039
15343
|
this.set = function(c, time, bpm){
|
|
15040
15344
|
// drive amount, minimum drive of 1
|
|
15041
|
-
const d = Util.assureNum(Math.max(
|
|
15345
|
+
const d = Util.assureNum(Math.max(0, Util.getParam(this._drive, c)) + 1);
|
|
15346
|
+
|
|
15042
15347
|
// preamp gain reduction for linear at drive = 1
|
|
15043
|
-
const p = 0.
|
|
15348
|
+
const p = 0.8;
|
|
15044
15349
|
// makeup gain
|
|
15045
|
-
const m = 1.0 / p
|
|
15046
|
-
|
|
15047
|
-
|
|
15048
|
-
this._fx.
|
|
15350
|
+
const m = 1.0 / (p * (d ** 1.1));
|
|
15351
|
+
|
|
15352
|
+
// set the parameters in the workletNode
|
|
15353
|
+
const amount = this._fx.workletNode.parameters.get('amount');
|
|
15354
|
+
amount.setValueAtTime(p * d * d, time);
|
|
15355
|
+
|
|
15356
|
+
const makeup = this._fx.workletNode.parameters.get('makeup');
|
|
15357
|
+
makeup.setValueAtTime(m, time);
|
|
15358
|
+
|
|
15359
|
+
const wet = Util.clip(Util.getParam(this._wet, c), 0, 1);
|
|
15360
|
+
this._mixWet.gain.setValueAtTime(wet);
|
|
15361
|
+
this._mixDry.gain.setValueAtTime(1 - wet);
|
|
15049
15362
|
}
|
|
15050
15363
|
|
|
15051
15364
|
this.chain = function(){
|
|
15052
|
-
return { 'send' : this._fx, 'return' : this.
|
|
15365
|
+
return { 'send' : this._fx, 'return' : this._mix }
|
|
15053
15366
|
}
|
|
15054
15367
|
|
|
15055
15368
|
this.delete = function(){
|
|
15056
|
-
this._fx.
|
|
15057
|
-
|
|
15369
|
+
let nodes = [ this._fx, this._mix, this._mixDry, this._mixWet ];
|
|
15370
|
+
|
|
15371
|
+
nodes.forEach((n) => {
|
|
15372
|
+
n.disconnect();
|
|
15373
|
+
n.dispose();
|
|
15374
|
+
});
|
|
15058
15375
|
}
|
|
15059
15376
|
}
|
|
15060
15377
|
|
|
@@ -15064,9 +15381,7 @@ const TanhDistortion = function(_params){
|
|
|
15064
15381
|
//
|
|
15065
15382
|
const Compressor = function(_params){
|
|
15066
15383
|
// replace defaults with provided params
|
|
15067
|
-
|
|
15068
|
-
this.defaults.splice(0, _params.length, ..._params);
|
|
15069
|
-
_params = this.defaults.map(p => Util.toArray(p));
|
|
15384
|
+
_params = Util.mapDefaults(_params, [-30, 6, 10, 80]);
|
|
15070
15385
|
|
|
15071
15386
|
this._fx = new Tone.Compressor({
|
|
15072
15387
|
threshold: -24,
|
|
@@ -15111,7 +15426,9 @@ const Chorus = function(_params){
|
|
|
15111
15426
|
this._fx.frequency.setValueAtTime(f, time);
|
|
15112
15427
|
// delaytime/2 because of up and down through center
|
|
15113
15428
|
// eg. 25 goes from 0 to 50, 40 goes from 0 to 80, etc.
|
|
15114
|
-
|
|
15429
|
+
Util.atTime(() => {
|
|
15430
|
+
this._fx.delayTime = Util.getParam(_params[1], c) / 2;
|
|
15431
|
+
}, time);
|
|
15115
15432
|
|
|
15116
15433
|
// waveform for chorus is not supported in browser instead change wetdry
|
|
15117
15434
|
let w = Util.getParam(_params[2], c);
|
|
@@ -15137,49 +15454,71 @@ const Chorus = function(_params){
|
|
|
15137
15454
|
// Based on an algorithm by Peter McCulloch
|
|
15138
15455
|
//
|
|
15139
15456
|
const Squash = function(_params){
|
|
15140
|
-
|
|
15457
|
+
_params = Util.mapDefaults(_params, [ 4, 1, 0.28 ]);
|
|
15458
|
+
// apply the default values and convert to arrays where necessary
|
|
15459
|
+
this._squash = Util.toArray(_params[0]);
|
|
15460
|
+
this._wet = Util.toArray(_params[1]);
|
|
15461
|
+
|
|
15462
|
+
// The crossfader for wet-dry (originally implemented with CrossFade)
|
|
15463
|
+
this._mix = new Tone.Add();
|
|
15464
|
+
this._mixDry = new Tone.Gain(0).connect(this._mix.input);
|
|
15141
15465
|
|
|
15142
15466
|
// ToneAudioNode has all the tone effect parameters
|
|
15143
15467
|
this._fx = new Tone.ToneAudioNode();
|
|
15144
15468
|
// A gain node for connecting with input and output
|
|
15145
|
-
this._fx.input = new Tone.Gain(1);
|
|
15146
|
-
this._fx.output = new Tone.Gain(1);
|
|
15469
|
+
this._fx.input = new Tone.Gain(1).connect(this._mixDry);
|
|
15470
|
+
this._fx.output = new Tone.Gain(1).connect(this._mix.addend);
|
|
15471
|
+
|
|
15147
15472
|
// the fx processor
|
|
15148
15473
|
this._fx.workletNode = Tone.getContext().createAudioWorkletNode('squash-processor');
|
|
15149
15474
|
// connect input, fx and output
|
|
15150
15475
|
this._fx.input.chain(this._fx.workletNode, this._fx.output);
|
|
15151
15476
|
|
|
15152
15477
|
this.set = function(c, time, bpm){
|
|
15153
|
-
|
|
15154
|
-
|
|
15155
|
-
|
|
15156
|
-
|
|
15478
|
+
const d = Util.assureNum(Math.max(1, Util.getParam(this._squash, c)));
|
|
15479
|
+
const m = 1.0 / Math.sqrt(d);
|
|
15480
|
+
|
|
15481
|
+
const amount = this._fx.workletNode.parameters.get('amount');
|
|
15482
|
+
amount.setValueAtTime(d, time);
|
|
15483
|
+
|
|
15484
|
+
const makeup = this._fx.workletNode.parameters.get('makeup');
|
|
15485
|
+
makeup.setValueAtTime(m, time);
|
|
15486
|
+
|
|
15487
|
+
const wet = Util.clip(Util.getParam(this._wet, c));
|
|
15157
15488
|
this._fx.output.gain.setValueAtTime(m, time);
|
|
15489
|
+
this._mixDry.gain.setValueAtTime(1 - wet, time);
|
|
15158
15490
|
}
|
|
15159
15491
|
|
|
15160
15492
|
this.chain = function(){
|
|
15161
|
-
return { 'send' : this._fx, 'return' : this.
|
|
15493
|
+
return { 'send' : this._fx, 'return' : this._mix }
|
|
15162
15494
|
}
|
|
15163
15495
|
|
|
15164
15496
|
this.delete = function(){
|
|
15165
|
-
this._fx.
|
|
15166
|
-
|
|
15497
|
+
let nodes = [ this._fx, this._mix, this._mixDry ];
|
|
15498
|
+
|
|
15499
|
+
nodes.forEach((n) => {
|
|
15500
|
+
n.disconnect();
|
|
15501
|
+
n.dispose();
|
|
15502
|
+
});
|
|
15167
15503
|
}
|
|
15168
15504
|
}
|
|
15169
15505
|
|
|
15170
15506
|
// Reverb FX
|
|
15171
15507
|
// Add a reverb to the sound to give it a feel of space
|
|
15508
|
+
// Using a decaying noise reverb algorithm, that does seem to
|
|
15509
|
+
// increase the memory usage over time slowly
|
|
15172
15510
|
//
|
|
15173
15511
|
const Reverb = function(_params){
|
|
15174
|
-
|
|
15512
|
+
_params = Util.mapDefaults(_params, [ 0.5, 1.5 ]);
|
|
15513
|
+
this._wet = _params[0];
|
|
15514
|
+
this._size = _params[1];
|
|
15175
15515
|
|
|
15176
|
-
this.
|
|
15177
|
-
this._size = (_params[1] !== undefined)? Util.toArray(_params[1]) : [ 1.5 ];
|
|
15516
|
+
this._fx = new Tone.Reverb();
|
|
15178
15517
|
|
|
15179
15518
|
this.set = function(c, time){
|
|
15180
|
-
let tmp = Math.min(
|
|
15519
|
+
let tmp = Math.min(15, Math.max(0.1, Util.getParam(this._size, c)));
|
|
15181
15520
|
if (this._fx.decay != tmp){
|
|
15182
|
-
this._fx.decay = tmp;
|
|
15521
|
+
Util.atTime(() => this._fx.decay = tmp, time);
|
|
15183
15522
|
}
|
|
15184
15523
|
|
|
15185
15524
|
let wet = Math.min(1, Math.max(0, Util.getParam(this._wet, c)));
|
|
@@ -15198,19 +15537,21 @@ const Reverb = function(_params){
|
|
|
15198
15537
|
|
|
15199
15538
|
// PitchShift FX
|
|
15200
15539
|
// Shift the pitch up or down with semitones
|
|
15540
|
+
// Utilizes the default PitchShift FX from ToneJS
|
|
15201
15541
|
//
|
|
15202
15542
|
const PitchShift = function(_params){
|
|
15203
|
-
|
|
15543
|
+
_params = Util.mapDefaults(_params, [ -12, 1 ]);
|
|
15544
|
+
// apply the default values and convert to arrays where necessary
|
|
15545
|
+
this._pitch = _params[0];
|
|
15546
|
+
this._wet = _params[1];
|
|
15204
15547
|
|
|
15205
|
-
this.
|
|
15206
|
-
this._wet = (_params[1] !== undefined)? Util.toArray(_params[1]) : [1];
|
|
15548
|
+
this._fx = new Tone.PitchShift();
|
|
15207
15549
|
|
|
15208
15550
|
this.set = function(c, time){
|
|
15209
15551
|
let p = Util.getParam(this._pitch, c);
|
|
15210
15552
|
let w = Util.getParam(this._wet, c);
|
|
15211
15553
|
|
|
15212
|
-
this._fx.pitch = TL.toScale(p);
|
|
15213
|
-
// this._fx.pitch = p;
|
|
15554
|
+
Util.atTime(() => this._fx.pitch = TL.toScale(p), time);
|
|
15214
15555
|
this._fx.wet.setValueAtTime(w, time);
|
|
15215
15556
|
}
|
|
15216
15557
|
|
|
@@ -15228,47 +15569,74 @@ const PitchShift = function(_params){
|
|
|
15228
15569
|
// a Low Frequency Oscillator effect, control tempo, type and depth
|
|
15229
15570
|
//
|
|
15230
15571
|
const LFO = function(_params){
|
|
15572
|
+
_params = Util.mapDefaults(_params, [ '1/8', 'sine', 1 ]);
|
|
15573
|
+
_params = _params.map(x => Util.toArray(x));
|
|
15574
|
+
this._speed = _params[0];
|
|
15575
|
+
this._type = _params[1];
|
|
15576
|
+
this._depth = _params[2];
|
|
15577
|
+
|
|
15231
15578
|
this._waveMap = {
|
|
15232
15579
|
sine : 'sine',
|
|
15233
|
-
|
|
15580
|
+
sineUp : 'sine',
|
|
15581
|
+
sineDown : 'sine',
|
|
15582
|
+
sawUp: 'sawtooth',
|
|
15583
|
+
sawDown: 'sawtooth',
|
|
15584
|
+
up: 'sawtooth',
|
|
15585
|
+
down: 'sawtooth',
|
|
15234
15586
|
square : 'square',
|
|
15587
|
+
squareUp : 'square',
|
|
15588
|
+
squareDown : 'square',
|
|
15235
15589
|
rect : 'square',
|
|
15236
15590
|
triangle : 'triangle',
|
|
15237
15591
|
tri : 'triangle',
|
|
15238
|
-
up: 'sawtooth',
|
|
15239
|
-
sawUp: 'sawtooth'
|
|
15240
15592
|
}
|
|
15241
15593
|
|
|
15242
15594
|
this._lfo = new Tone.LFO();
|
|
15243
15595
|
this._fx = new Tone.Gain();
|
|
15244
15596
|
this._lfo.connect(this._fx.gain);
|
|
15245
|
-
// this._fx = new Tone.Tremolo('8n').start();
|
|
15246
|
-
|
|
15247
|
-
this._speed = (_params[0]) ? Util.toArray(_params[0]) : ['1/8'];
|
|
15248
|
-
this._type = (_params[1]) ? Util.toArray(_params[1]) : ['sine'];
|
|
15249
|
-
this._depth = (_params[2] !== undefined) ? Util.toArray(_params[2]) : [ 1 ];
|
|
15250
15597
|
|
|
15251
15598
|
this.set = function(c, time, bpm){
|
|
15252
15599
|
let w = Util.getParam(this._type, c);
|
|
15253
|
-
if (this._waveMap[w]){
|
|
15254
|
-
w = this._waveMap[w];
|
|
15255
|
-
} else {
|
|
15600
|
+
if (!this._waveMap[w]){
|
|
15256
15601
|
Util.log(`'${w} is not a valid waveshape`);
|
|
15257
15602
|
// default wave if wave does not exist
|
|
15258
15603
|
w = 'sine';
|
|
15259
15604
|
}
|
|
15260
|
-
this._lfo.set({ type: w });
|
|
15605
|
+
this._lfo.set({ type: this._waveMap[w] });
|
|
15261
15606
|
|
|
15262
15607
|
let s = Util.getParam(this._speed, c);
|
|
15263
|
-
let
|
|
15608
|
+
let t = Util.divToS(s, bpm);
|
|
15609
|
+
let f = Math.max(0.0001, t);
|
|
15264
15610
|
this._lfo.frequency.setValueAtTime(1/f, time);
|
|
15265
15611
|
|
|
15266
15612
|
let a = Util.getParam(this._depth, c);
|
|
15267
|
-
|
|
15268
|
-
|
|
15269
|
-
|
|
15270
|
-
|
|
15613
|
+
Util.atTime(() => {
|
|
15614
|
+
this._lfo.min = Math.min(1, Math.max(0, 1 - a));
|
|
15615
|
+
this._lfo.max = 1;
|
|
15616
|
+
// fix for squarewave not going to 0 fully
|
|
15617
|
+
if (this._waveMap[w] === 'square'){
|
|
15618
|
+
this._lfo.min += -0.1;
|
|
15271
15619
|
}
|
|
15620
|
+
|
|
15621
|
+
// swap high and low point to create a saw down
|
|
15622
|
+
if (w === 'down' || w === 'sawDown' || w === 'squareUp' || w === 'sineDown' ){
|
|
15623
|
+
let tmp = this._lfo.min;
|
|
15624
|
+
this._lfo.min = this._lfo.max;
|
|
15625
|
+
this._lfo.max = tmp;
|
|
15626
|
+
}
|
|
15627
|
+
}, time);
|
|
15628
|
+
|
|
15629
|
+
if (this._lfo.state !== 'started'){
|
|
15630
|
+
// fix incorrect phases for sawtooth sine and triangle
|
|
15631
|
+
// simply by starting them a bit later.
|
|
15632
|
+
switch (this._waveMap[w]) {
|
|
15633
|
+
case 'sine' :
|
|
15634
|
+
time += t * 0.25; break;
|
|
15635
|
+
case 'triangle' :
|
|
15636
|
+
time += t * 0.25; break;
|
|
15637
|
+
case 'sawtooth' :
|
|
15638
|
+
time += t * 0.5; break;
|
|
15639
|
+
}
|
|
15272
15640
|
this._lfo.start(time);
|
|
15273
15641
|
}
|
|
15274
15642
|
}
|
|
@@ -15278,9 +15646,9 @@ const LFO = function(_params){
|
|
|
15278
15646
|
}
|
|
15279
15647
|
|
|
15280
15648
|
this.delete = function(){
|
|
15281
|
-
let
|
|
15649
|
+
let nodes = [ this._fx, this._lfo ];
|
|
15282
15650
|
|
|
15283
|
-
|
|
15651
|
+
nodes.forEach((b) => {
|
|
15284
15652
|
b.disconnect();
|
|
15285
15653
|
b.dispose();
|
|
15286
15654
|
});
|
|
@@ -15289,10 +15657,35 @@ const LFO = function(_params){
|
|
|
15289
15657
|
|
|
15290
15658
|
// A filter FX, choose between highpass, lowpass and bandpass
|
|
15291
15659
|
// Set the cutoff frequency and Q factor
|
|
15660
|
+
// Optionally with extra arguments you can apply a modulation
|
|
15292
15661
|
//
|
|
15293
15662
|
const Filter = function(_params){
|
|
15663
|
+
// parameter mapping changes based on amount of arguments
|
|
15664
|
+
this._static = true;
|
|
15665
|
+
if (_params.length < 4){
|
|
15666
|
+
if (typeof _params[0] === 'string'){
|
|
15667
|
+
_params = Util.mapDefaults(_params, ['low', 1200, 0.45]);
|
|
15668
|
+
} else {
|
|
15669
|
+
_params = [['low']].concat(Util.mapDefaults(_params, [1200, 0.45]));
|
|
15670
|
+
}
|
|
15671
|
+
}
|
|
15672
|
+
else {
|
|
15673
|
+
_params = Util.mapDefaults(_params, ['low', '1/1', 200, 3000, 0.45, 'sine', 0.5]);
|
|
15674
|
+
this._static = false;
|
|
15675
|
+
}
|
|
15676
|
+
|
|
15294
15677
|
this._fx = new Tone.Filter();
|
|
15295
15678
|
|
|
15679
|
+
// the following is only used if the parameters for modulation
|
|
15680
|
+
// are added as arguments to the fx(filter) function
|
|
15681
|
+
if (!this._static){
|
|
15682
|
+
this._lfo = new Tone.LFO();
|
|
15683
|
+
this._scale = new Tone.ScaleExp();
|
|
15684
|
+
this._lfo.connect(this._scale);
|
|
15685
|
+
this._scale.connect(this._fx.frequency);
|
|
15686
|
+
}
|
|
15687
|
+
|
|
15688
|
+
// available filter types for the filter
|
|
15296
15689
|
this._types = {
|
|
15297
15690
|
'lo' : 'lowpass',
|
|
15298
15691
|
'low' : 'lowpass',
|
|
@@ -15301,32 +15694,107 @@ const Filter = function(_params){
|
|
|
15301
15694
|
'high' : 'highpass',
|
|
15302
15695
|
'highpass' : 'highpass',
|
|
15303
15696
|
'band' : 'bandpass',
|
|
15304
|
-
'bandpass': 'bandpass'
|
|
15697
|
+
'bandpass': 'bandpass',
|
|
15305
15698
|
}
|
|
15306
15699
|
if (this._types[_params[0]]){
|
|
15307
15700
|
this._fx.set({ type: this._types[_params[0]] });
|
|
15308
15701
|
} else {
|
|
15309
|
-
|
|
15702
|
+
console.log(`'${_params[0]}' is not a valid filter type. Defaults to lowpass`);
|
|
15310
15703
|
this._fx.set({ type: 'lowpass' });
|
|
15311
15704
|
}
|
|
15312
15705
|
this._fx.set({ rolloff: -24 });
|
|
15313
15706
|
|
|
15314
|
-
|
|
15315
|
-
this.
|
|
15316
|
-
|
|
15707
|
+
// available waveforms for the LFO
|
|
15708
|
+
this._waveMap = {
|
|
15709
|
+
sine : 'sine',
|
|
15710
|
+
// sineUp : 'sine',
|
|
15711
|
+
// sineDown : 'sine',
|
|
15712
|
+
saw : 'sawtooth',
|
|
15713
|
+
sawUp: 'sawtooth',
|
|
15714
|
+
sawDown: 'sawtooth',
|
|
15715
|
+
up: 'sawtooth',
|
|
15716
|
+
down: 'sawtooth',
|
|
15717
|
+
// square : 'square',
|
|
15718
|
+
// squareUp : 'square',
|
|
15719
|
+
// squareDown : 'square',
|
|
15720
|
+
// rect : 'square',
|
|
15721
|
+
triangle : 'triangle',
|
|
15722
|
+
tri : 'triangle',
|
|
15723
|
+
}
|
|
15317
15724
|
|
|
15318
15725
|
this.set = function(c, time, bpm){
|
|
15319
|
-
let
|
|
15320
|
-
|
|
15321
|
-
|
|
15726
|
+
let _q;
|
|
15727
|
+
// if the filter is static use the settings of frequency and resonance
|
|
15728
|
+
if (this._static){
|
|
15729
|
+
let f = Util.getParam(_params[1], c);
|
|
15730
|
+
_q = _params[2];
|
|
15322
15731
|
|
|
15323
|
-
if (rt > 0){
|
|
15324
|
-
this._fx.frequency.rampTo(f, rt, time);
|
|
15325
|
-
} else {
|
|
15326
15732
|
this._fx.frequency.setValueAtTime(f, time);
|
|
15733
|
+
// let rt = Util.divToS(Util.getParam(this._rt, c), bpm);
|
|
15734
|
+
} else {
|
|
15735
|
+
_q = _params[4];
|
|
15736
|
+
let t = Util.divToS(Util.getParam(_params[1], c), bpm);
|
|
15737
|
+
let f = 1 / t;
|
|
15738
|
+
let lo = Util.clip(Util.getParam(_params[2], c), 5, 19000);
|
|
15739
|
+
let hi = Util.clip(Util.getParam(_params[3], c), 5, 19000);
|
|
15740
|
+
|
|
15741
|
+
let w = Util.getParam(_params[5], c);
|
|
15742
|
+
if (this._waveMap[w]){
|
|
15743
|
+
w = this._waveMap[w];
|
|
15744
|
+
} else {
|
|
15745
|
+
if (isNaN(w)){
|
|
15746
|
+
log(`${w} is not a valid waveshape. Defaults to sine`);
|
|
15747
|
+
// default wave if wave does not exist
|
|
15748
|
+
w = 'sine';
|
|
15749
|
+
} else {
|
|
15750
|
+
// w = value between 0 and 1, map to up, down, triangle
|
|
15751
|
+
// 0=down, 0.5=triangle, 1=up
|
|
15752
|
+
switch(Math.floor(Util.clip(w, 0, 1)*2.99)){
|
|
15753
|
+
case 0:
|
|
15754
|
+
// regular saw up
|
|
15755
|
+
w = 'sawtooth'; break;
|
|
15756
|
+
case 1:
|
|
15757
|
+
w = 'sine'; break;
|
|
15758
|
+
case 2:
|
|
15759
|
+
w = 'sawtooth';
|
|
15760
|
+
// swap hi/lo range for saw down effect
|
|
15761
|
+
let tmp = lo; lo = hi; hi = tmp; break;
|
|
15762
|
+
}
|
|
15763
|
+
}
|
|
15764
|
+
}
|
|
15765
|
+
this._lfo.set({ type: w });
|
|
15766
|
+
|
|
15767
|
+
let exp = Util.clip(Util.getParam(_params[6], c), 0.01, 100);
|
|
15768
|
+
|
|
15769
|
+
this._scale.min = lo;
|
|
15770
|
+
this._scale.max = hi;
|
|
15771
|
+
this._scale.exponent = exp;
|
|
15772
|
+
this._lfo.frequency.setValueAtTime(f, time);
|
|
15773
|
+
|
|
15774
|
+
this._lfo.max = 1;
|
|
15775
|
+
|
|
15776
|
+
if (this._lfo.state !== 'started'){
|
|
15777
|
+
switch (w) {
|
|
15778
|
+
case 'sine' :
|
|
15779
|
+
time += t * 0.25; break;
|
|
15780
|
+
case 'triangle' :
|
|
15781
|
+
time += t * 0.25; break;
|
|
15782
|
+
case 'sawtooth' :
|
|
15783
|
+
time += t * 0.5; break;
|
|
15784
|
+
}
|
|
15785
|
+
this._lfo.start(time);
|
|
15786
|
+
}
|
|
15327
15787
|
}
|
|
15328
15788
|
|
|
15789
|
+
let r = 1 / (1 - Math.min(0.95, Math.max(0, Util.getParam(_q, c))));
|
|
15329
15790
|
this._fx.Q.setValueAtTime(r, time);
|
|
15791
|
+
|
|
15792
|
+
// ramptime removed now that modulation is possible
|
|
15793
|
+
// if (rt > 0){
|
|
15794
|
+
// this._fx.frequency.rampTo(f, rt, time);
|
|
15795
|
+
// } else {
|
|
15796
|
+
// this._fx.frequency.setValueAtTime(f, time);
|
|
15797
|
+
// }
|
|
15330
15798
|
}
|
|
15331
15799
|
|
|
15332
15800
|
this.chain = function(){
|
|
@@ -15334,8 +15802,12 @@ const Filter = function(_params){
|
|
|
15334
15802
|
}
|
|
15335
15803
|
|
|
15336
15804
|
this.delete = function(){
|
|
15337
|
-
this._fx.
|
|
15338
|
-
|
|
15805
|
+
let nodes = [ this._fx, this._lfo, this._scale ];
|
|
15806
|
+
|
|
15807
|
+
nodes.forEach((n) => {
|
|
15808
|
+
n?.disconnect();
|
|
15809
|
+
n?.dispose();
|
|
15810
|
+
});
|
|
15339
15811
|
}
|
|
15340
15812
|
}
|
|
15341
15813
|
|
|
@@ -15372,10 +15844,10 @@ const TriggerFilter = function(_params){
|
|
|
15372
15844
|
'bandpass': 'bandpass'
|
|
15373
15845
|
}
|
|
15374
15846
|
|
|
15375
|
-
this.defaults = ['low', 1, '1/16', 4000, 30];
|
|
15376
15847
|
// replace defaults with provided arguments
|
|
15377
|
-
|
|
15378
|
-
|
|
15848
|
+
_params = Util.mapDefaults(_params, ['low', 1, '1/16', 4000, 100, 1]);
|
|
15849
|
+
// this.defaults.splice(0, _params.length, ..._params);
|
|
15850
|
+
_params = _params.map(p => Util.toArray(p));
|
|
15379
15851
|
|
|
15380
15852
|
if (this._types[_params[0][0]]){
|
|
15381
15853
|
this._fx.set({ type: this._types[_params[0][0]] });
|
|
@@ -15388,6 +15860,7 @@ const TriggerFilter = function(_params){
|
|
|
15388
15860
|
this._rel = _params[2];
|
|
15389
15861
|
this._high = _params[3];
|
|
15390
15862
|
this._low = _params[4];
|
|
15863
|
+
this._exp = _params[5];
|
|
15391
15864
|
|
|
15392
15865
|
this.set = function(c, time, bpm){
|
|
15393
15866
|
this._adsr.attack = Util.divToS(Util.getParam(this._att, c), bpm);
|
|
@@ -15397,9 +15870,11 @@ const TriggerFilter = function(_params){
|
|
|
15397
15870
|
let max = Util.getParam(this._high, c);
|
|
15398
15871
|
let range = Math.abs(max - min);
|
|
15399
15872
|
let lower = Math.min(max, min);
|
|
15873
|
+
let exp = 1 / Util.getParam(this._exp, c);
|
|
15400
15874
|
|
|
15401
15875
|
this._mul.setValueAtTime(range, time);
|
|
15402
15876
|
this._add.setValueAtTime(lower, time);
|
|
15877
|
+
Util.atTime(() => { this._pow.value = exp }, time);
|
|
15403
15878
|
|
|
15404
15879
|
// fade-out running envelope over 5 ms
|
|
15405
15880
|
if (this._adsr.value > 0){
|
|
@@ -15414,9 +15889,9 @@ const TriggerFilter = function(_params){
|
|
|
15414
15889
|
}
|
|
15415
15890
|
|
|
15416
15891
|
this.delete = function(){
|
|
15417
|
-
let
|
|
15892
|
+
let nodes = [ this._fx, this._adsr, this._mul, this._add, this._pow ];
|
|
15418
15893
|
|
|
15419
|
-
|
|
15894
|
+
nodes.forEach((b) => {
|
|
15420
15895
|
b.disconnect();
|
|
15421
15896
|
b.dispose();
|
|
15422
15897
|
});
|
|
@@ -15444,27 +15919,31 @@ const TriggerFilter = function(_params){
|
|
|
15444
15919
|
|
|
15445
15920
|
// Custom stereo delay implementation with lowpass filter in feedback loop
|
|
15446
15921
|
const Delay = function(_params){
|
|
15922
|
+
// apply the default values and convert to arrays where necessary
|
|
15923
|
+
if (_params.length === 1){ _params[1] = _params[0] }
|
|
15924
|
+
else if (_params.length === 2){
|
|
15925
|
+
_params[2] = _params[1];
|
|
15926
|
+
_params[1] = _params[0];
|
|
15927
|
+
}
|
|
15928
|
+
|
|
15929
|
+
_params = Util.mapDefaults(_params, [ '3/16', '2/8', 0.7, 0.6, 0.5 ]);
|
|
15930
|
+
this._timeL = Util.toArray(_params[0]);
|
|
15931
|
+
this._timeR = Util.toArray(_params[1]);
|
|
15932
|
+
this._feedBack = Util.toArray(_params[2]);
|
|
15933
|
+
this._fbDamp = Util.toArray(_params[3]);
|
|
15934
|
+
this._wet = Util.toArray(_params[4]);
|
|
15935
|
+
|
|
15447
15936
|
this._fx = new Tone.Gain(1);
|
|
15448
15937
|
this._fb = new Tone.Gain(0.5);
|
|
15449
15938
|
this._mix = new Tone.CrossFade(0.5);
|
|
15450
15939
|
this._split = new Tone.Split(2);
|
|
15451
15940
|
this._merge = new Tone.Merge(2);
|
|
15452
|
-
this._maxDelay =
|
|
15941
|
+
this._maxDelay = 3;
|
|
15453
15942
|
|
|
15454
15943
|
this._delayL = new Tone.Delay({ maxDelay: this._maxDelay });
|
|
15455
15944
|
this._delayR = new Tone.Delay({ maxDelay: this._maxDelay });
|
|
15456
15945
|
this._flt = new Tone.Filter(1000, 'lowpass', '-12');
|
|
15457
15946
|
|
|
15458
|
-
if (_params.length === 2){
|
|
15459
|
-
_params[2] = _params[1];
|
|
15460
|
-
_params[1] = _params[0];
|
|
15461
|
-
}
|
|
15462
|
-
// All params and defaults
|
|
15463
|
-
this._timeL = (_params[0] !== undefined)? Util.toArray(_params[0]) : [ '2/16' ];
|
|
15464
|
-
this._timeR = (_params[1] !== undefined)? Util.toArray(_params[1]) : [ '3/16' ];
|
|
15465
|
-
this._feedBack = (_params[2] !== undefined)? Util.toArray(_params[2]) : [ 0.7 ];
|
|
15466
|
-
this._fbDamp = (_params[3] !== undefined)? Util.toArray(_params[3]) : [ 0.6 ];
|
|
15467
|
-
|
|
15468
15947
|
// split the signal
|
|
15469
15948
|
this._fx.connect(this._mix.a);
|
|
15470
15949
|
this._fx.connect(this._fb);
|
|
@@ -15487,13 +15966,16 @@ const Delay = function(_params){
|
|
|
15487
15966
|
this.set = function(c, time, bpm){
|
|
15488
15967
|
let dL = Math.min(this._maxDelay, Math.max(0, Util.formatRatio(Util.getParam(this._timeL, c), bpm)));
|
|
15489
15968
|
let dR = Math.min(this._maxDelay, Math.max(0, Util.formatRatio(Util.getParam(this._timeR, c), bpm)));
|
|
15490
|
-
let ct = Math.max(10, Util.getParam(this._fbDamp, c) * 5000);
|
|
15491
15969
|
let fb = Math.max(0, Math.min(0.99, Util.getParam(this._feedBack, c) * 0.707));
|
|
15970
|
+
let cf = Math.max(10, Util.getParam(this._fbDamp, c) * 8000);
|
|
15971
|
+
|
|
15972
|
+
this._delayL.delayTime.setValueAtTime(dL + Math.random() * 0.001, time);
|
|
15973
|
+
this._delayR.delayTime.setValueAtTime(dR + Math.random() * 0.001, time);
|
|
15974
|
+
this._fb.gain.setValueAtTime(Util.assureNum(fb, 0.7), time);
|
|
15975
|
+
this._flt.frequency.setValueAtTime(cf, time);
|
|
15492
15976
|
|
|
15493
|
-
|
|
15494
|
-
this.
|
|
15495
|
-
this._flt.frequency.setValueAtTime(ct, time);
|
|
15496
|
-
this._fb.gain.setValueAtTime(fb, time);
|
|
15977
|
+
const wet = Util.clip(Util.getParam(this._wet, c));
|
|
15978
|
+
this._mix.fade.setValueAtTime(wet, time);
|
|
15497
15979
|
}
|
|
15498
15980
|
|
|
15499
15981
|
this.chain = function(){
|
|
@@ -15501,9 +15983,9 @@ const Delay = function(_params){
|
|
|
15501
15983
|
}
|
|
15502
15984
|
|
|
15503
15985
|
this.delete = function(){
|
|
15504
|
-
let
|
|
15986
|
+
let nodes = [ this._fx, this._fb, this._mix, this._split, this._merge, this._delayL, this._delayR, this._flt ];
|
|
15505
15987
|
|
|
15506
|
-
|
|
15988
|
+
nodes.forEach((b) => {
|
|
15507
15989
|
b.disconnect();
|
|
15508
15990
|
b.dispose();
|
|
15509
15991
|
});
|
|
@@ -15511,124 +15993,124 @@ const Delay = function(_params){
|
|
|
15511
15993
|
}
|
|
15512
15994
|
|
|
15513
15995
|
// Old pingpong delay implementation, just using the Tone.PingPongDelay()
|
|
15514
|
-
const PingPongDelay = function(_params){
|
|
15515
|
-
|
|
15516
|
-
|
|
15517
|
-
|
|
15518
|
-
|
|
15519
|
-
|
|
15520
|
-
|
|
15521
|
-
|
|
15522
|
-
|
|
15523
|
-
|
|
15524
|
-
|
|
15525
|
-
|
|
15526
|
-
|
|
15527
|
-
|
|
15528
|
-
|
|
15529
|
-
|
|
15530
|
-
|
|
15531
|
-
|
|
15532
|
-
|
|
15533
|
-
|
|
15534
|
-
|
|
15535
|
-
|
|
15536
|
-
|
|
15537
|
-
|
|
15538
|
-
|
|
15539
|
-
}
|
|
15996
|
+
// const PingPongDelay = function(_params){
|
|
15997
|
+
// this._fx = new Tone.PingPongDelay();
|
|
15998
|
+
// this._fx.set({ wet: 0.4 });
|
|
15999
|
+
|
|
16000
|
+
// // console.log('delay', param);
|
|
16001
|
+
// this._dTime = (_params[0] !== undefined)? Util.toArray(_params[0]) : [ '3/16' ];
|
|
16002
|
+
// this._fb = (_params[1] !== undefined)? Util.toArray(_params[1]) : [ 0.3 ];
|
|
16003
|
+
// // let del = new Tone.PingPongDelay(formatRatio(t), fb);
|
|
16004
|
+
|
|
16005
|
+
// this.set = function(c, time, bpm){
|
|
16006
|
+
// let t = Math.max(0, Util.formatRatio(Util.getParam(this._dTime, c), bpm));
|
|
16007
|
+
// let fb = Math.max(0, Math.min(0.99, Util.getParam(this._fb, c)));
|
|
16008
|
+
|
|
16009
|
+
// this._fx.delayTime.setValueAtTime(t, time);
|
|
16010
|
+
// this._fx.feedback.setValueAtTime(fb, time);
|
|
16011
|
+
// }
|
|
16012
|
+
|
|
16013
|
+
// this.chain = function(){
|
|
16014
|
+
// return { 'send' : this._fx, 'return' : this._fx };
|
|
16015
|
+
// }
|
|
16016
|
+
|
|
16017
|
+
// this.delete = function(){
|
|
16018
|
+
// this._fx.disconnect();
|
|
16019
|
+
// this._fx.dispose();
|
|
16020
|
+
// }
|
|
16021
|
+
// }
|
|
15540
16022
|
|
|
15541
|
-
const FreeVerb = function(_params){
|
|
15542
|
-
|
|
16023
|
+
// const FreeVerb = function(_params){
|
|
16024
|
+
// this._fx = new Tone.Freeverb(_params[0], _params[1]);
|
|
15543
16025
|
|
|
15544
|
-
|
|
16026
|
+
// this.set = function(c, time, bpm){
|
|
15545
16027
|
|
|
15546
|
-
|
|
16028
|
+
// }
|
|
15547
16029
|
|
|
15548
|
-
|
|
15549
|
-
|
|
15550
|
-
|
|
16030
|
+
// this.chain = function(){
|
|
16031
|
+
// return { 'send' : this._fx, 'return' : this._fx };
|
|
16032
|
+
// }
|
|
15551
16033
|
|
|
15552
|
-
|
|
15553
|
-
|
|
16034
|
+
// this.delete = function(){
|
|
16035
|
+
// let nodes = [ this._fx ];
|
|
15554
16036
|
|
|
15555
|
-
|
|
15556
|
-
|
|
15557
|
-
|
|
15558
|
-
|
|
15559
|
-
|
|
15560
|
-
}
|
|
16037
|
+
// nodes.forEach((b) => {
|
|
16038
|
+
// b.disconnect();
|
|
16039
|
+
// b.dispose();
|
|
16040
|
+
// });
|
|
16041
|
+
// }
|
|
16042
|
+
// }
|
|
15561
16043
|
|
|
15562
16044
|
// squash/compress an incoming signal
|
|
15563
16045
|
// based on algorithm by Peter McCulloch
|
|
15564
|
-
const SquashDeprecated = function(_params){
|
|
15565
|
-
|
|
15566
|
-
|
|
15567
|
-
|
|
15568
|
-
|
|
15569
|
-
|
|
15570
|
-
|
|
15571
|
-
|
|
15572
|
-
|
|
15573
|
-
|
|
15574
|
-
|
|
15575
|
-
|
|
15576
|
-
|
|
15577
|
-
|
|
15578
|
-
|
|
15579
|
-
|
|
16046
|
+
// const SquashDeprecated = function(_params){
|
|
16047
|
+
// this._compress = (_params[0] !== undefined)? Util.toArray(_params[0]) : [1];
|
|
16048
|
+
|
|
16049
|
+
// this._fx = new Tone.WaveShaper();
|
|
16050
|
+
|
|
16051
|
+
// this.shaper = function(amount){
|
|
16052
|
+
// // (a * c) / ((a * c)^2 * 0.28 + 1) / √c
|
|
16053
|
+
// // drive amount, minimum of 1
|
|
16054
|
+
// const c = amount;
|
|
16055
|
+
// // makeup gain
|
|
16056
|
+
// const m = 1.0 / Math.sqrt(c);
|
|
16057
|
+
// // set the waveshaper effect
|
|
16058
|
+
// this._fx.setMap((x) => {
|
|
16059
|
+
// return (x * c) / ((x * c) * (x * c) * 0.28 + 1) * m;
|
|
16060
|
+
// });
|
|
16061
|
+
// }
|
|
15580
16062
|
|
|
15581
|
-
|
|
15582
|
-
|
|
15583
|
-
|
|
15584
|
-
|
|
15585
|
-
|
|
15586
|
-
|
|
15587
|
-
|
|
15588
|
-
|
|
15589
|
-
|
|
15590
|
-
|
|
15591
|
-
|
|
15592
|
-
|
|
15593
|
-
|
|
15594
|
-
}
|
|
16063
|
+
// this.set = function(c){
|
|
16064
|
+
// let d = Util.getParam(this._compress, c);
|
|
16065
|
+
// this.shaper(isNaN(d)? 1 : Math.max(1, d));
|
|
16066
|
+
// }
|
|
16067
|
+
|
|
16068
|
+
// this.chain = function(){
|
|
16069
|
+
// return { 'send' : this._fx, 'return' : this._fx };
|
|
16070
|
+
// }
|
|
16071
|
+
|
|
16072
|
+
// this.delete = function(){
|
|
16073
|
+
// this._fx.disconnect();
|
|
16074
|
+
// this._fx.dispose();
|
|
16075
|
+
// }
|
|
16076
|
+
// }
|
|
15595
16077
|
|
|
15596
16078
|
// A distortion algorithm using the tanh (hyperbolic-tangent) as a
|
|
15597
16079
|
// waveshaping technique. Some mapping to apply a more equal loudness
|
|
15598
16080
|
// distortion is applied on the overdrive parameter
|
|
15599
16081
|
//
|
|
15600
|
-
const DriveDeprecated = function(_params){
|
|
15601
|
-
|
|
15602
|
-
|
|
15603
|
-
|
|
15604
|
-
|
|
15605
|
-
|
|
15606
|
-
|
|
15607
|
-
|
|
15608
|
-
|
|
15609
|
-
|
|
15610
|
-
|
|
15611
|
-
|
|
15612
|
-
|
|
15613
|
-
|
|
15614
|
-
|
|
15615
|
-
|
|
15616
|
-
|
|
16082
|
+
// const DriveDeprecated = function(_params){
|
|
16083
|
+
// this._drive = (_params[0] !== undefined)? Util.toArray(_params[0]) : [1.5];
|
|
16084
|
+
|
|
16085
|
+
// this._fx = new Tone.WaveShaper();
|
|
16086
|
+
|
|
16087
|
+
// this.shaper = function(amount){
|
|
16088
|
+
// // drive curve, minimum drive of 1
|
|
16089
|
+
// const d = Math.pow(amount, 2);
|
|
16090
|
+
// // makeup gain
|
|
16091
|
+
// const m = Math.pow(d, 0.6);
|
|
16092
|
+
// // preamp gain reduction for linear at drive = 1
|
|
16093
|
+
// const p = 0.4;
|
|
16094
|
+
// // set the waveshaping effect
|
|
16095
|
+
// this._fx.setMap((x) => {
|
|
16096
|
+
// return Math.tanh(x * p * d) / p / m;
|
|
16097
|
+
// });
|
|
16098
|
+
// }
|
|
15617
16099
|
|
|
15618
|
-
|
|
15619
|
-
|
|
15620
|
-
|
|
15621
|
-
|
|
15622
|
-
|
|
15623
|
-
|
|
15624
|
-
|
|
15625
|
-
|
|
15626
|
-
|
|
15627
|
-
|
|
15628
|
-
|
|
15629
|
-
|
|
15630
|
-
|
|
15631
|
-
}
|
|
16100
|
+
// this.set = function(c){
|
|
16101
|
+
// let d = Util.getParam(this._drive, c);
|
|
16102
|
+
// this.shaper(isNaN(d)? 1 : Math.max(1, d));
|
|
16103
|
+
// }
|
|
16104
|
+
|
|
16105
|
+
// this.chain = function(){
|
|
16106
|
+
// return { 'send' : this._fx, 'return' : this._fx };
|
|
16107
|
+
// }
|
|
16108
|
+
|
|
16109
|
+
// this.delete = function(){
|
|
16110
|
+
// this._fx.disconnect();
|
|
16111
|
+
// this._fx.dispose();
|
|
16112
|
+
// }
|
|
16113
|
+
// }
|
|
15632
16114
|
},{"./Util.js":66,"tone":44,"total-serialism":47}],57:[function(require,module,exports){
|
|
15633
16115
|
const Tone = require('tone');
|
|
15634
16116
|
const Util = require('./Util.js');
|
|
@@ -15709,13 +16191,14 @@ class Instrument extends Sequencer {
|
|
|
15709
16191
|
this.sourceEvent(c, e, time);
|
|
15710
16192
|
|
|
15711
16193
|
// fade-out running envelope over 5 ms
|
|
15712
|
-
|
|
15713
|
-
|
|
15714
|
-
|
|
15715
|
-
|
|
15716
|
-
|
|
15717
|
-
|
|
15718
|
-
|
|
16194
|
+
// retrigger temporarily disabled to reduce distortion
|
|
16195
|
+
// if (this.adsr.value > 0){
|
|
16196
|
+
// let tmp = this.adsr.release;
|
|
16197
|
+
// this.adsr.release = 0.004;
|
|
16198
|
+
// this.adsr.triggerRelease(time-0.004);
|
|
16199
|
+
// this.adsr.release = tmp;
|
|
16200
|
+
// time += 0.010;
|
|
16201
|
+
// }
|
|
15719
16202
|
|
|
15720
16203
|
// set shape for playback (fade-in / out and length)
|
|
15721
16204
|
if (this._att){
|
|
@@ -15723,15 +16206,21 @@ class Instrument extends Sequencer {
|
|
|
15723
16206
|
let dec = Util.divToS(Util.getParam(this._sus, c), this.bpm());
|
|
15724
16207
|
let rel = Util.divToS(Util.getParam(this._rel, c), this.bpm());
|
|
15725
16208
|
|
|
15726
|
-
|
|
16209
|
+
// minimum attaack and release times are 1 millisecond
|
|
16210
|
+
this.adsr.attack = Math.max(0.001, att);
|
|
15727
16211
|
this.adsr.decay = dec;
|
|
15728
|
-
this.adsr.release = rel;
|
|
16212
|
+
this.adsr.release = Math.max(0.001, rel);
|
|
15729
16213
|
|
|
15730
|
-
|
|
16214
|
+
// trigger the envelope and release after a short while
|
|
16215
|
+
// a better working alternative for the code below
|
|
16216
|
+
this.adsr.triggerAttack(time);
|
|
16217
|
+
this.adsr.triggerRelease(time + att + dec);
|
|
16218
|
+
|
|
16219
|
+
// e = Math.min(this._time, att + dec + rel);
|
|
15731
16220
|
// e = Math.min(t, att + dec + rel);
|
|
15732
16221
|
|
|
15733
|
-
let rt = Math.max(0.001, e - this.adsr.release);
|
|
15734
|
-
this.adsr.triggerAttackRelease(rt, time);
|
|
16222
|
+
// let rt = Math.max(0.001, e - this.adsr.release);
|
|
16223
|
+
// this.adsr.triggerAttackRelease(rt, time);
|
|
15735
16224
|
} else {
|
|
15736
16225
|
// if shape is 'off' only trigger attack
|
|
15737
16226
|
this.adsr.triggerAttack(time);
|
|
@@ -15762,7 +16251,10 @@ class Instrument extends Sequencer {
|
|
|
15762
16251
|
// delete super class
|
|
15763
16252
|
super.delete();
|
|
15764
16253
|
// disconnect the sound dispose the player
|
|
16254
|
+
this.gain.disconnect();
|
|
15765
16255
|
this.gain.dispose();
|
|
16256
|
+
|
|
16257
|
+
this.panner.disconnect();
|
|
15766
16258
|
this.panner.dispose();
|
|
15767
16259
|
// this.adsr.dispose();
|
|
15768
16260
|
// remove all fx
|
|
@@ -15920,11 +16412,14 @@ class MonoMidi extends Sequencer {
|
|
|
15920
16412
|
}
|
|
15921
16413
|
|
|
15922
16414
|
// Midi specific parameters
|
|
15923
|
-
this._note = [];
|
|
16415
|
+
this._note = [];
|
|
16416
|
+
this._midinote = [];
|
|
15924
16417
|
this._velocity = [ 127, 0 ];
|
|
15925
16418
|
this._dur = [ 100 ];
|
|
15926
16419
|
this._cc = [];
|
|
15927
16420
|
this._channel = [ 1 ];
|
|
16421
|
+
this._program = false;
|
|
16422
|
+
this._pgm = null;
|
|
15928
16423
|
this._chord = false;
|
|
15929
16424
|
this._bend = [];
|
|
15930
16425
|
|
|
@@ -15935,8 +16430,8 @@ class MonoMidi extends Sequencer {
|
|
|
15935
16430
|
// normalized velocity (0 - 1)
|
|
15936
16431
|
let g = Util.getParam(this._velocity[0], c);
|
|
15937
16432
|
|
|
15938
|
-
// get the duration
|
|
15939
|
-
let d = Util.divToS(Util.getParam(this._dur, c), this.bpm()) * 1000;
|
|
16433
|
+
// get the duration (minus 5ms to ensure note-off send before note-on)
|
|
16434
|
+
let d = Util.divToS(Util.getParam(this._dur, c), this.bpm()) * 1000 - 5;
|
|
15940
16435
|
|
|
15941
16436
|
// get the channel
|
|
15942
16437
|
let ch = Util.getParam(this._channel, c);
|
|
@@ -15945,6 +16440,15 @@ class MonoMidi extends Sequencer {
|
|
|
15945
16440
|
let offset = WebMidi.time - Tone.context.currentTime * 1000;
|
|
15946
16441
|
let sync = time * 1000 + offset;
|
|
15947
16442
|
|
|
16443
|
+
// send program change messages on specified channel
|
|
16444
|
+
// only if the value is an integer
|
|
16445
|
+
let pc = Util.getParam(this._program, c);
|
|
16446
|
+
if (!isNaN(pc) && this._pgm !== pc){
|
|
16447
|
+
this._device.sendProgramChange(pc, ch, { time: sync - 1 });
|
|
16448
|
+
// only send value if different from previous one
|
|
16449
|
+
this._pgm = pc;
|
|
16450
|
+
}
|
|
16451
|
+
|
|
15948
16452
|
// send pitchbend message in hires -1 1 at specified channel
|
|
15949
16453
|
if (this._bend.length > 0){
|
|
15950
16454
|
let b = Util.lookup(this._bend, c);
|
|
@@ -15965,6 +16469,20 @@ class MonoMidi extends Sequencer {
|
|
|
15965
16469
|
// only play a note if the notes are provided in the function
|
|
15966
16470
|
// if (this._note.length > 0){
|
|
15967
16471
|
|
|
16472
|
+
let noteOptions = { duration: d, velocity: g, time: sync };
|
|
16473
|
+
|
|
16474
|
+
// if a midinote is selected instead of note
|
|
16475
|
+
// play the value without mapping
|
|
16476
|
+
let m = Util.getParam(this._midinote, c);
|
|
16477
|
+
if (m){
|
|
16478
|
+
if (this._chord){
|
|
16479
|
+
m = Util.lookup(this._midinote, c);
|
|
16480
|
+
m = Util.toArray(m);
|
|
16481
|
+
}
|
|
16482
|
+
this._device.playNote(m, ch, noteOptions);
|
|
16483
|
+
return;
|
|
16484
|
+
}
|
|
16485
|
+
|
|
15968
16486
|
// note as interval / octave coordinate
|
|
15969
16487
|
let o = Util.getParam(this._note[1], c);
|
|
15970
16488
|
let n = [];
|
|
@@ -15975,17 +16493,18 @@ class MonoMidi extends Sequencer {
|
|
|
15975
16493
|
} else {
|
|
15976
16494
|
i = [ Util.getParam(this._note[0], c) ];
|
|
15977
16495
|
}
|
|
16496
|
+
|
|
16497
|
+
// if the note is 'off' don't play the note
|
|
16498
|
+
// useful when only CC or Programchange is needed
|
|
16499
|
+
if (i[0] === 'off'){ return; }
|
|
15978
16500
|
|
|
15979
16501
|
for (let x=0; x<i.length; x++){
|
|
15980
16502
|
// reconstruct midi note value, (0, 0) = 36
|
|
15981
16503
|
// convert to scale and include the octave
|
|
15982
16504
|
n[x] = Util.toMidi(i[x], o);
|
|
15983
16505
|
}
|
|
15984
|
-
|
|
15985
16506
|
// play the note(s)! updated for webmidi 3.x
|
|
15986
|
-
this._device.playNote(n, ch,
|
|
15987
|
-
|
|
15988
|
-
// }
|
|
16507
|
+
this._device.playNote(n, ch, noteOptions);
|
|
15989
16508
|
}
|
|
15990
16509
|
|
|
15991
16510
|
amp(g, r){
|
|
@@ -16010,6 +16529,10 @@ class MonoMidi extends Sequencer {
|
|
|
16010
16529
|
this._bend = Util.toArray(b);
|
|
16011
16530
|
}
|
|
16012
16531
|
|
|
16532
|
+
midinote(n=[60]){
|
|
16533
|
+
this._midinote = Util.toArray(n);
|
|
16534
|
+
}
|
|
16535
|
+
|
|
16013
16536
|
chord(c){
|
|
16014
16537
|
this._chord = false;
|
|
16015
16538
|
if (c === 'on' || c === 1){
|
|
@@ -16017,6 +16540,10 @@ class MonoMidi extends Sequencer {
|
|
|
16017
16540
|
}
|
|
16018
16541
|
}
|
|
16019
16542
|
|
|
16543
|
+
program(p){
|
|
16544
|
+
this._program = Util.toArray(p);
|
|
16545
|
+
}
|
|
16546
|
+
|
|
16020
16547
|
add_fx(...cc){
|
|
16021
16548
|
// control parameters via control change midi messages
|
|
16022
16549
|
this._cc = [];
|
|
@@ -16036,7 +16563,14 @@ class MonoMidi extends Sequencer {
|
|
|
16036
16563
|
// send out midiclock messages to sync external devices
|
|
16037
16564
|
// on this specific midi output and channel
|
|
16038
16565
|
// this._sync;
|
|
16039
|
-
}
|
|
16566
|
+
}
|
|
16567
|
+
|
|
16568
|
+
delete(){
|
|
16569
|
+
// delete super class
|
|
16570
|
+
super.delete();
|
|
16571
|
+
|
|
16572
|
+
console.log('=> disposed MonoMidi()', this._device);
|
|
16573
|
+
}
|
|
16040
16574
|
}
|
|
16041
16575
|
module.exports = MonoMidi;
|
|
16042
16576
|
},{"./Sequencer.js":65,"./Util.js":66,"tone":44,"webmidi":55}],60:[function(require,module,exports){
|
|
@@ -16083,16 +16617,19 @@ class MonoSample extends Instrument {
|
|
|
16083
16617
|
// clean-up previous buffer
|
|
16084
16618
|
this.sample.buffer.dispose();
|
|
16085
16619
|
}
|
|
16620
|
+
|
|
16621
|
+
if (!this._bufs.has(f)){
|
|
16622
|
+
Util.log(`${w} is not a valid sample name`);
|
|
16623
|
+
// defaul sample if file doesn not exist
|
|
16624
|
+
f = 'kick_909';
|
|
16625
|
+
}
|
|
16626
|
+
|
|
16086
16627
|
if (this._bufs.has(f)){
|
|
16087
16628
|
this.sample.buffer = this._bufs.get(f);
|
|
16088
|
-
// this.sample.buffer = this._bufs.get(f).slice(0);
|
|
16089
16629
|
} else {
|
|
16090
16630
|
// default sample if file does not exist
|
|
16091
|
-
this.sample.buffer = this._bufs.get('
|
|
16092
|
-
// this.sample.buffer = this._bufs.get('kick_min').slice(0);
|
|
16631
|
+
this.sample.buffer = this._bufs.get('kick_909');
|
|
16093
16632
|
}
|
|
16094
|
-
// the duration of the buffer in seconds
|
|
16095
|
-
let dur = this.sample.buffer.duration;
|
|
16096
16633
|
|
|
16097
16634
|
// get speed and if 2d array pick randomly
|
|
16098
16635
|
let s = Util.getParam(this._speed, c);
|
|
@@ -16115,6 +16652,8 @@ class MonoSample extends Instrument {
|
|
|
16115
16652
|
// it becomes normal playback again) no fix yet
|
|
16116
16653
|
// this.sample.reverse = s < 0.0;
|
|
16117
16654
|
|
|
16655
|
+
// the duration of the buffer in seconds
|
|
16656
|
+
let dur = this.sample.buffer.duration;
|
|
16118
16657
|
let l = Util.lookup(this._stretch, c);
|
|
16119
16658
|
let n = 1;
|
|
16120
16659
|
if (l){
|
|
@@ -16186,7 +16725,12 @@ class MonoSample extends Instrument {
|
|
|
16186
16725
|
// delete super class
|
|
16187
16726
|
super.delete();
|
|
16188
16727
|
// disconnect the sound dispose the player
|
|
16728
|
+
this.source.stop();
|
|
16729
|
+
this.source.disconnect();
|
|
16189
16730
|
this.source.dispose();
|
|
16731
|
+
|
|
16732
|
+
this.sample.stop();
|
|
16733
|
+
this.sample.disconnect();
|
|
16190
16734
|
this.sample.dispose();
|
|
16191
16735
|
|
|
16192
16736
|
console.log('=> disposed MonoSample()', this._sound);
|
|
@@ -16213,10 +16757,11 @@ class MonoSynth extends Instrument {
|
|
|
16213
16757
|
triangle : 'triangle',
|
|
16214
16758
|
tri : 'triangle',
|
|
16215
16759
|
rect : 'square',
|
|
16216
|
-
|
|
16217
|
-
|
|
16218
|
-
|
|
16219
|
-
|
|
16760
|
+
organ : 'sine4',
|
|
16761
|
+
// fm: 'fmsine',
|
|
16762
|
+
// am: 'amsine',
|
|
16763
|
+
// pwm: 'pwm',
|
|
16764
|
+
// organ: 'sine4',
|
|
16220
16765
|
}
|
|
16221
16766
|
// // synth specific variables;
|
|
16222
16767
|
this._note = [ 0, 0 ];
|
|
@@ -16277,8 +16822,9 @@ class MonoSynth extends Instrument {
|
|
|
16277
16822
|
this.synth.frequency.rampTo(f, s, time);
|
|
16278
16823
|
} else {
|
|
16279
16824
|
this.synth.frequency.setValueAtTime(f, time);
|
|
16280
|
-
this._firstSlide = false;
|
|
16281
16825
|
}
|
|
16826
|
+
// first time the synth plays don't slide!
|
|
16827
|
+
this._firstSlide = false;
|
|
16282
16828
|
}
|
|
16283
16829
|
|
|
16284
16830
|
super(v=[3], d=[0.111]){
|
|
@@ -16303,6 +16849,11 @@ class MonoSynth extends Instrument {
|
|
|
16303
16849
|
super.delete();
|
|
16304
16850
|
// dispose the sound source
|
|
16305
16851
|
// this.source.delete();
|
|
16852
|
+
// this.adsr.dispose();
|
|
16853
|
+
this.synth.stop();
|
|
16854
|
+
this.synth.disconnect();
|
|
16855
|
+
this.synth.dispose();
|
|
16856
|
+
|
|
16306
16857
|
this.adsr.dispose();
|
|
16307
16858
|
this.synth.dispose();
|
|
16308
16859
|
this.source.dispose();
|
|
@@ -16314,10 +16865,10 @@ module.exports = MonoSynth;
|
|
|
16314
16865
|
},{"./Instrument":57,"./Util.js":66,"tone":44,"total-serialism":47}],62:[function(require,module,exports){
|
|
16315
16866
|
const Tone = require('tone');
|
|
16316
16867
|
const Util = require('./Util.js');
|
|
16317
|
-
const fxMap = require('./Effects.js');
|
|
16318
|
-
const TL = require('total-serialism').Translate;
|
|
16319
|
-
// const Sequencer = require('./Sequencer.js');
|
|
16320
16868
|
const Instrument = require('./Instrument.js');
|
|
16869
|
+
// const fxMap = require('./Effects.js');
|
|
16870
|
+
// const TL = require('total-serialism').Translate;
|
|
16871
|
+
// const Sequencer = require('./Sequencer.js');
|
|
16321
16872
|
|
|
16322
16873
|
// Basic class for a poly-instrument
|
|
16323
16874
|
class PolyInstrument extends Instrument {
|
|
@@ -16425,15 +16976,17 @@ class PolyInstrument extends Instrument {
|
|
|
16425
16976
|
let dec = Util.divToS(Util.lookup(this._sus, c), this.bpm());
|
|
16426
16977
|
let rel = Util.divToS(Util.lookup(this._rel, c), this.bpm());
|
|
16427
16978
|
|
|
16428
|
-
this.adsrs[i].attack = att;
|
|
16979
|
+
this.adsrs[i].attack = Math.max(0.001, att);
|
|
16429
16980
|
this.adsrs[i].decay = dec;
|
|
16430
|
-
this.adsrs[i].release = rel;
|
|
16981
|
+
this.adsrs[i].release = Math.max(0.001, rel);
|
|
16431
16982
|
|
|
16432
|
-
e = Math.min(this._time, att + dec + rel);
|
|
16433
|
-
|
|
16983
|
+
// e = Math.min(this._time, att + dec + rel);
|
|
16984
|
+
// let rt = Math.max(0.001, e - this.adsrs[i].release);
|
|
16985
|
+
// this.adsrs[i].triggerAttackRelease(rt, time);
|
|
16986
|
+
|
|
16434
16987
|
// trigger the envelope
|
|
16435
|
-
|
|
16436
|
-
this.adsrs[i].
|
|
16988
|
+
this.adsrs[i].triggerAttack(time);
|
|
16989
|
+
this.adsrs[i].triggerRelease(time + att + dec);
|
|
16437
16990
|
} else {
|
|
16438
16991
|
// if shape is off only trigger attack
|
|
16439
16992
|
// when voice stealing is 'off' this will lead to all
|
|
@@ -16469,18 +17022,27 @@ class PolyInstrument extends Instrument {
|
|
|
16469
17022
|
// delete super class
|
|
16470
17023
|
super.delete();
|
|
16471
17024
|
// disconnect the sound dispose the player
|
|
16472
|
-
|
|
16473
|
-
|
|
17025
|
+
this.gain.disconnect();
|
|
17026
|
+
this.gain.dispose();
|
|
17027
|
+
this.panner.disconnect();
|
|
17028
|
+
this.panner.dispose();
|
|
16474
17029
|
|
|
16475
|
-
this.adsrs.map((a) =>
|
|
16476
|
-
|
|
17030
|
+
this.adsrs.map((a) => {
|
|
17031
|
+
a.disconnect();
|
|
17032
|
+
a.dispose();
|
|
17033
|
+
});
|
|
17034
|
+
this.sources.map((s) => {
|
|
17035
|
+
s.stop();
|
|
17036
|
+
s.disconnect();
|
|
17037
|
+
s.dispose();
|
|
17038
|
+
});
|
|
16477
17039
|
// remove all fx
|
|
16478
17040
|
// this._fx.map((f) => f.delete());
|
|
16479
17041
|
console.log('=> disposed PolyInstrument() with FX:', this._fx);
|
|
16480
17042
|
}
|
|
16481
17043
|
}
|
|
16482
17044
|
module.exports = PolyInstrument;
|
|
16483
|
-
},{"./
|
|
17045
|
+
},{"./Instrument.js":57,"./Util.js":66,"tone":44}],63:[function(require,module,exports){
|
|
16484
17046
|
const Tone = require('tone');
|
|
16485
17047
|
const Util = require('./Util.js');
|
|
16486
17048
|
const PolyInstrument = require('./PolyInstrument.js');
|
|
@@ -16545,7 +17107,7 @@ class PolySample extends PolyInstrument {
|
|
|
16545
17107
|
this.sources[id].buffer = this._bufs.get(b);
|
|
16546
17108
|
} else {
|
|
16547
17109
|
// default sample if file does not exist
|
|
16548
|
-
this.sources[id].buffer = this._bufs.get('
|
|
17110
|
+
this.sources[id].buffer = this._bufs.get('kick_909');
|
|
16549
17111
|
}
|
|
16550
17112
|
// the duration of the buffer in seconds
|
|
16551
17113
|
let dur = this.sources[id].buffer.duration;
|
|
@@ -16738,6 +17300,11 @@ class PolySynth extends PolyInstrument {
|
|
|
16738
17300
|
this._detune = Util.toArray(d);
|
|
16739
17301
|
}
|
|
16740
17302
|
|
|
17303
|
+
fat(...a){
|
|
17304
|
+
// alias for super/unison synth
|
|
17305
|
+
this.super(...a);
|
|
17306
|
+
}
|
|
17307
|
+
|
|
16741
17308
|
slide(s){
|
|
16742
17309
|
// portamento from one note to another
|
|
16743
17310
|
this._slide = Util.toArray(s);
|
|
@@ -16751,14 +17318,14 @@ class PolySynth extends PolyInstrument {
|
|
|
16751
17318
|
// delete super class
|
|
16752
17319
|
super.delete();
|
|
16753
17320
|
|
|
16754
|
-
console.log('disposed
|
|
17321
|
+
console.log('disposed PolySynth()', this._wave);
|
|
16755
17322
|
}
|
|
16756
17323
|
}
|
|
16757
17324
|
module.exports = PolySynth;
|
|
16758
17325
|
},{"./PolyInstrument":62,"./Util.js":66,"tone":44}],65:[function(require,module,exports){
|
|
16759
17326
|
const Tone = require('tone');
|
|
16760
17327
|
const Util = require('./Util.js');
|
|
16761
|
-
const WebMidi = require("webmidi");
|
|
17328
|
+
// const WebMidi = require("webmidi");
|
|
16762
17329
|
|
|
16763
17330
|
// Basic Sequencer class for triggering events
|
|
16764
17331
|
class Sequencer {
|
|
@@ -16770,10 +17337,12 @@ class Sequencer {
|
|
|
16770
17337
|
// Sequencer specific parameters
|
|
16771
17338
|
this._count = 0;
|
|
16772
17339
|
this._beatCount = 0;
|
|
17340
|
+
this._subdivCnt = 0;
|
|
16773
17341
|
this._time = 1;
|
|
16774
17342
|
this._subdiv = [ 1 ];
|
|
16775
17343
|
this._offset = 0;
|
|
16776
17344
|
this._beat = [ 1 ];
|
|
17345
|
+
this._wait = null;
|
|
16777
17346
|
this._human = 0;
|
|
16778
17347
|
|
|
16779
17348
|
// visual code
|
|
@@ -16815,6 +17384,9 @@ class Sequencer {
|
|
|
16815
17384
|
}
|
|
16816
17385
|
// set subdivision speeds
|
|
16817
17386
|
this._loop.playbackRate = Util.getParam(this._subdiv, this._count);
|
|
17387
|
+
|
|
17388
|
+
// get the subdivision count (always 0, except when subdividing)
|
|
17389
|
+
this._subdivCnt = (this._subdivCnt + 1) % this._loop.playbackRate;
|
|
16818
17390
|
|
|
16819
17391
|
// humanize method is interesting to add
|
|
16820
17392
|
this._loop.humanize = Util.getParam(this._human, this._count);
|
|
@@ -16826,6 +17398,12 @@ class Sequencer {
|
|
|
16826
17398
|
if (Math.random() < b){
|
|
16827
17399
|
// get the count value
|
|
16828
17400
|
let c = this._beatCount;
|
|
17401
|
+
|
|
17402
|
+
// get the wait time (delay) convert to seconds
|
|
17403
|
+
if (this._wait !== null){
|
|
17404
|
+
let w = Util.getParam(this._wait, c);
|
|
17405
|
+
time += Util.divToS(w, this.bpm());
|
|
17406
|
+
}
|
|
16829
17407
|
|
|
16830
17408
|
// trigger some events for this instrument based
|
|
16831
17409
|
// on the current count and time
|
|
@@ -16848,9 +17426,12 @@ class Sequencer {
|
|
|
16848
17426
|
this._canvas.eval(Util.getParam(this._visual, c));
|
|
16849
17427
|
}
|
|
16850
17428
|
}
|
|
16851
|
-
|
|
17429
|
+
|
|
16852
17430
|
// increment internal beat counter
|
|
16853
|
-
|
|
17431
|
+
// only increment if ratchetcounter is 0
|
|
17432
|
+
if (this._subdivCnt < 1) {
|
|
17433
|
+
this._beatCount++;
|
|
17434
|
+
}
|
|
16854
17435
|
}
|
|
16855
17436
|
// increment count for sequencing
|
|
16856
17437
|
this._count++;
|
|
@@ -16868,7 +17449,9 @@ class Sequencer {
|
|
|
16868
17449
|
// calculate the scheduling
|
|
16869
17450
|
let schedule = Tone.Time(this._offset).toSeconds();
|
|
16870
17451
|
// create new loop for synth
|
|
16871
|
-
this._loop = new Tone.Loop((time) => {
|
|
17452
|
+
this._loop = new Tone.Loop((time) => {
|
|
17453
|
+
this._event(time)
|
|
17454
|
+
}, this._time).start(schedule);
|
|
16872
17455
|
}
|
|
16873
17456
|
// else {
|
|
16874
17457
|
// // generate a listener for the osc-address
|
|
@@ -16962,6 +17545,11 @@ class Sequencer {
|
|
|
16962
17545
|
}
|
|
16963
17546
|
}
|
|
16964
17547
|
|
|
17548
|
+
wait(w=[0]){
|
|
17549
|
+
// wait a specified amount of milliseconds before triggering
|
|
17550
|
+
this._wait = Util.toArray(w);
|
|
17551
|
+
}
|
|
17552
|
+
|
|
16965
17553
|
human(h){
|
|
16966
17554
|
// set the humanizing factor for the instrument in seconds
|
|
16967
17555
|
this._human = Util.toArray(h).map(x => Util.divToS(x));
|
|
@@ -16986,7 +17574,8 @@ class Sequencer {
|
|
|
16986
17574
|
}
|
|
16987
17575
|
}
|
|
16988
17576
|
module.exports = Sequencer;
|
|
16989
|
-
},{"./Util.js":66,"tone":44
|
|
17577
|
+
},{"./Util.js":66,"tone":44}],66:[function(require,module,exports){
|
|
17578
|
+
const Tone = require('tone');
|
|
16990
17579
|
const { noteToMidi, toScale, mtof } = require('total-serialism').Translate;
|
|
16991
17580
|
|
|
16992
17581
|
// replace defaults with incoming parameters
|
|
@@ -16995,6 +17584,13 @@ function mapDefaults(params, defaults){
|
|
|
16995
17584
|
return defaults.map(p => toArray(p));
|
|
16996
17585
|
}
|
|
16997
17586
|
|
|
17587
|
+
// Function that is evaluated at a specific time from Tone Transpor
|
|
17588
|
+
// More precise than Tone.Transport.ScheduleOnce()
|
|
17589
|
+
// Workaround for Tone objects that don't have setValueAtTime
|
|
17590
|
+
function atTime(callback, time){
|
|
17591
|
+
setTimeout(callback, (time - Tone.context.currentTime) * 1000);
|
|
17592
|
+
}
|
|
17593
|
+
|
|
16998
17594
|
// convert amplitude to dBFS scale
|
|
16999
17595
|
function atodb(a=0){
|
|
17000
17596
|
return 20 * Math.log(a);
|
|
@@ -17005,8 +17601,8 @@ function dbtoa(db=0){
|
|
|
17005
17601
|
return 10 ** (db/20);
|
|
17006
17602
|
}
|
|
17007
17603
|
|
|
17008
|
-
// clip a value between a specified range
|
|
17009
|
-
function clip(v, l, h){
|
|
17604
|
+
// clip a value between a specified range, defaults to 0 and 1 clip
|
|
17605
|
+
function clip(v, l=0, h=1){
|
|
17010
17606
|
return Math.max(l, Math.min(h, v));
|
|
17011
17607
|
}
|
|
17012
17608
|
|
|
@@ -17203,8 +17799,8 @@ function log(msg){
|
|
|
17203
17799
|
}
|
|
17204
17800
|
}
|
|
17205
17801
|
|
|
17206
|
-
module.exports = { mapDefaults, atodb, dbtoa, clip, assureNum, lookup, randLookup, isRandom, getParam, toArray, msToS, formatRatio, divToS, divToF, toMidi, mtof, noteToMidi, noteToFreq, assureWave, log }
|
|
17207
|
-
},{"total-serialism":47}],67:[function(require,module,exports){
|
|
17802
|
+
module.exports = { mapDefaults, atTime, atodb, dbtoa, clip, assureNum, lookup, randLookup, isRandom, getParam, toArray, msToS, formatRatio, divToS, divToF, toMidi, mtof, noteToMidi, noteToFreq, assureWave, log }
|
|
17803
|
+
},{"tone":44,"total-serialism":47}],67:[function(require,module,exports){
|
|
17208
17804
|
module.exports={
|
|
17209
17805
|
"uptempo" : 10,
|
|
17210
17806
|
"downtempo" : 10,
|
|
@@ -17565,7 +18161,7 @@ const { WebMidi } = require("webmidi");
|
|
|
17565
18161
|
// load extra AudioWorkletProcessors from file
|
|
17566
18162
|
// transformed to inline with browserify brfs
|
|
17567
18163
|
|
|
17568
|
-
const fxExtensions = "\n// A white noise generator at -6dBFS to test AudioWorkletProcessor\n//\
|
|
18164
|
+
const fxExtensions = "\n// A white noise generator at -6dBFS to test AudioWorkletProcessor\n//\n// class NoiseProcessor extends AudioWorkletProcessor {\n// \tprocess(inputs, outputs, parameters){\n// \t\tconst output = outputs[0];\n\n// \t\toutput.forEach((channel) => {\n// \t\t\tfor (let i=0; i<channel.length; i++) {\n// \t\t\t\tchannel[i] = Math.random() - 0.5;\n// \t\t\t}\n// \t\t});\n// \t\treturn true;\n// \t}\n// }\n// registerProcessor('noise-processor', NoiseProcessor);\n\n// A Downsampling Chiptune effect. Downsamples the signal by a specified amount\n// Resulting in a lower samplerate, making it sound more like 8bit/chiptune\n// Programmed with a custom AudioWorkletProcessor, see effects/Processors.js\n//\nclass DownSampleProcessor extends AudioWorkletProcessor {\n\tstatic get parameterDescriptors() {\n\t\treturn [{\n\t\t\tname: 'down',\n\t\t\tdefaultValue: 8,\n\t\t\tminValue: 1,\n\t\t\tmaxValue: 2048\n\t\t}];\n\t}\n\n\tconstructor(){\n\t\tsuper();\n\t\t// the frame counter\n\t\tthis.count = 0;\n\t\t// sample and hold variable array\n\t\tthis.sah = [];\n\t}\n\n\tprocess(inputs, outputs, parameters){\n\t\tconst input = inputs[0];\n\t\tconst output = outputs[0];\n\n\t\t// if there is anything to process\n\t\tif (input.length > 0){\n\t\t\t// for the length of the sample array (generally 128)\n\t\t\tfor (let i=0; i<input[0].length; i++){\n\t\t\t\tconst d = (parameters.down.length > 1) ? parameters.down[i] : parameters.down[0];\n\t\t\t\t// for every channel\n\t\t\t\tfor (let channel=0; channel<input.length; ++channel){\n\t\t\t\t\t// if counter equals 0, sample and hold\n\t\t\t\t\tif (this.count % d === 0){\n\t\t\t\t\t\tthis.sah[channel] = input[channel][i];\n\t\t\t\t\t}\n\t\t\t\t\t// output the currently held sample\n\t\t\t\t\toutput[channel][i] = this.sah[channel];\n\t\t\t\t}\n\t\t\t\t// increment sample counter\n\t\t\t\tthis.count++;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n}\nregisterProcessor('downsampler-processor', DownSampleProcessor);\n\n// A distortion algorithm using the tanh (hyperbolic-tangent) as a \n// waveshaping technique. Some mapping to apply a more equal loudness \n// distortion is applied on the overdrive parameter\n//\nclass TanhDistortionProcessor extends AudioWorkletProcessor {\n\tstatic get parameterDescriptors(){\n\t\treturn [{\n\t\t\tname: 'amount',\n\t\t\tdefaultValue: 4,\n\t\t\tminValue: 1\n\t\t}, {\n\t\t\tname: 'makeup',\n\t\t\tdefaultValue: 0.5,\n\t\t\tminValue: 0,\n\t\t\tmaxValue: 2\n\t\t}]\n\t}\n\n\tconstructor(){\n\t\tsuper();\n\t}\n\n\tprocess(inputs, outputs, parameters){\n\t\tconst input = inputs[0];\n\t\tconst output = outputs[0];\n\n\t\tif (input.length > 0){\n\t\t\tfor (let channel=0; channel<input.length; ++channel){\n\t\t\t\tfor (let i=0; i<input[channel].length; i++){\n\t\t\t\t\tconst a = (parameters.amount.length > 1)? parameters.amount[i] : parameters.amount[0];\n\t\t\t\t\tconst m = (parameters.makeup.length > 1)? parameters.makeup[i] : parameters.makeup[0];\n\t\t\t\t\t// simple waveshaping with tanh\n\t\t\t\t\toutput[channel][i] = Math.tanh(input[channel][i] * a) * m;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n}\nregisterProcessor('tanh-distortion-processor', TanhDistortionProcessor);\n\n// A distortion/compression effect of an incoming signal\n// Based on an algorithm by Peter McCulloch\n// \nclass SquashProcessor extends AudioWorkletProcessor {\n\tstatic get parameterDescriptors(){\n\t\treturn [{\n\t\t\tname: 'amount',\n\t\t\tdefaultValue: 4,\n\t\t\tminValue: 1,\n\t\t\tmaxValue: 1024\n\t\t}, {\n\t\t\tname: 'makeup',\n\t\t\tdefaultValue: 0.5,\n\t\t\tminValue: 0,\n\t\t\tmaxValue: 2\n\t\t}];\n\t}\n\n\tconstructor(){\n\t\tsuper();\n\t}\n\n\tprocess(inputs, outputs, parameters){\n\t\tconst input = inputs[0];\n\t\tconst output = outputs[0];\n\t\t\n\t\tif (input.length > 0){\n\t\t\tfor (let channel=0; channel<input.length; ++channel){\n\t\t\t\tfor (let i=0; i<input[channel].length; i++){\n\t\t\t\t\t// (s * a) / ((s * a)^2 * 0.28 + 1) / √a\n\t\t\t\t\t// drive amount, minimum of 1\n\t\t\t\t\tconst a = (parameters.amount.length > 1)? parameters.amount[i] : parameters.amount[0];\n\t\t\t\t\t// makeup gain\n\t\t\t\t\tconst m = (parameters.makeup.length > 1)? parameters.makeup[i] : parameters.makeup[0];\n\t\t\t\t\t// set the waveshaper effect\n\t\t\t\t\tconst s = input[channel][i];\n\t\t\t\t\tconst x = s * a * 1.412;\n\t\t\t\t\toutput[channel][i] = (x / (x * x * 0.28 + 1.0)) * m * 0.708;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n}\nregisterProcessor('squash-processor', SquashProcessor);";
|
|
17569
18165
|
Tone.getContext().addAudioWorkletModule(URL.createObjectURL(new Blob([ fxExtensions ], { type: 'text/javascript' })));
|
|
17570
18166
|
|
|
17571
18167
|
// Mercury main class controls Tone and loads samples
|