mercury-engine 1.0.9 → 1.2.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/README.md CHANGED
@@ -180,6 +180,36 @@ Stop the recording and download the file `myRecording.webm`
180
180
  Engine.record(false, 'myRecording');
181
181
  ```
182
182
 
183
+ ### Meter
184
+
185
+ You can add a meter to the main audio output and poll for the amplitude value from the meter to for example create audio-reactive visuals in other programming languages such as Hydra or P5.js.
186
+
187
+ First add the meter, optionally with a smoothing factor (default=0.7)
188
+
189
+ ```js
190
+ Engine.addMeter();
191
+ ```
192
+
193
+ Get the meter value as floating-point between 0-1
194
+
195
+ ```js
196
+ Engine.getMeter();
197
+ ```
198
+
199
+ Store the meters amplitude value in a global variable for usage in other places and update regularly with a setInterval at a defined interval in milliseconds.
200
+
201
+ ```js
202
+ let amp;
203
+
204
+ setInterval(() => amp = Engine.getMeter(), 100);
205
+ ```
206
+
207
+ For example control some visual parameter in [Hydra](https://hydra.ojack.xyz)
208
+
209
+ ```js
210
+ osc(10, 0.2, () => amp * 20).out();
211
+ ```
212
+
183
213
  ### MIDI
184
214
 
185
215
  WebMIDI is included and started if the browser is compatible with it. If not, an error will be printed to the console. You can provide a callback function `onmidi` to execute some code when the WebMIDI enabling was succesful.
package/dist/mercury.js CHANGED
@@ -6590,6 +6590,11 @@ module.exports={
6590
6590
  "low"
6591
6591
  ],
6592
6592
 
6593
+ "super" : [
6594
+ "fat",
6595
+ "unison"
6596
+ ],
6597
+
6593
6598
  "add_fx" : [
6594
6599
  "fx",
6595
6600
  "withFX",
@@ -6702,7 +6707,6 @@ const emptyDefault = {
6702
6707
  'beat' : [ 1, -1 ],
6703
6708
  'amp' : [ 1 ],
6704
6709
  'env' : [ 1, 250 ],
6705
- 'pan' : [ 0 ],
6706
6710
  'note' : [ 0, 0 ],
6707
6711
  'add_fx' : []
6708
6712
  }
@@ -6714,14 +6718,16 @@ const instrumentDefaults = {
6714
6718
  'type' : 'saw',
6715
6719
  'functions' : {
6716
6720
  'amp' : [ 0.7 ],
6717
- 'wave2' : [ 'saw', 0 ]
6721
+ 'wave2' : [ 'saw', 0 ],
6722
+ 'pan' : [ 0 ]
6718
6723
  }
6719
6724
  },
6720
6725
  'polySynth' : {
6721
6726
  'type' : 'saw',
6722
6727
  'functions' : {
6723
6728
  'amp' : [ 0.7 ],
6724
- 'wave2' : [ 'saw', 0 ]
6729
+ 'wave2' : [ 'saw', 0 ],
6730
+ 'pan' : [ 0 ]
6725
6731
  }
6726
6732
  },
6727
6733
  'sample' : {
@@ -6732,7 +6738,8 @@ const instrumentDefaults = {
6732
6738
  'stretch' : [ 0, 1, 1 ],
6733
6739
  'speed' : [ 1 ],
6734
6740
  'note' : [ 'off' ],
6735
- 'tune' : [ 60 ]
6741
+ 'tune' : [ 60 ],
6742
+ 'pan' : [ 0 ]
6736
6743
  }
6737
6744
  },
6738
6745
  'loop' : {
@@ -6743,7 +6750,8 @@ const instrumentDefaults = {
6743
6750
  'stretch' : [ 1, 1, 1 ],
6744
6751
  'speed' : [ 1 ],
6745
6752
  'note' : [ 'off' ],
6746
- 'tune' : [ 60 ]
6753
+ 'tune' : [ 60 ],
6754
+ 'pan' : [ 0 ]
6747
6755
  }
6748
6756
  },
6749
6757
  'midi' : {
@@ -6760,7 +6768,8 @@ const instrumentDefaults = {
6760
6768
  'functions' : {
6761
6769
  'env' : [ -1 ],
6762
6770
  'amp' : [ 0.9 ],
6763
- 'note' : [ 'off' ]
6771
+ 'note' : [ 'off' ],
6772
+ 'pan' : [ 0 ]
6764
6773
  }
6765
6774
  }
6766
6775
  }
@@ -14922,6 +14931,12 @@ const fxMap = {
14922
14931
  'degrade' : (params) => {
14923
14932
  return new DownSampler(params);
14924
14933
  },
14934
+ 'room' : (params) => {
14935
+ return new Reverb(params);
14936
+ },
14937
+ 'verb' : (params) => {
14938
+ return new Reverb(params);
14939
+ },
14925
14940
  'reverb' : (params) => {
14926
14941
  return new Reverb(params);
14927
14942
  },
@@ -14931,9 +14946,9 @@ const fxMap = {
14931
14946
  'pitchShift' : (params) => {
14932
14947
  return new PitchShift(params);
14933
14948
  },
14934
- 'tune' : (params) => {
14935
- return new PitchShift(params);
14936
- },
14949
+ // 'tune' : (params) => {
14950
+ // return new PitchShift(params);
14951
+ // },
14937
14952
  'filter' : (params) => {
14938
14953
  return new Filter(params);
14939
14954
  },
@@ -14958,8 +14973,14 @@ const fxMap = {
14958
14973
  'ppDelay' : (params) => {
14959
14974
  return new PingPongDelay(params);
14960
14975
  },
14961
- 'freeverb' : (params) => {
14962
- return new FreeVerb(params);
14976
+ // 'freeverb' : (params) => {
14977
+ // return new FreeVerb(params);
14978
+ // },
14979
+ 'chorus' : (params) => {
14980
+ return new Chorus(Util.mapDefaults(params, ['4/1', 45, 0.5]));
14981
+ },
14982
+ 'double' : (params) => {
14983
+ return new Chorus(Util.mapDefaults(params, ['8/1', 8, 1]));
14963
14984
  }
14964
14985
  }
14965
14986
  module.exports = fxMap;
@@ -15077,6 +15098,41 @@ const Compressor = function(_params){
15077
15098
  }
15078
15099
  }
15079
15100
 
15101
+ // A Chorus effect based on the default ToneJS effect
15102
+ // Also the Double effect if the wetdry is set to 1 (only wet signal)
15103
+ //
15104
+ const Chorus = function(_params){
15105
+ // also start the oscillators for the effect
15106
+ this._fx = new Tone.Chorus().start();
15107
+
15108
+ this.set = (c, time, bpm) => {
15109
+ // convert division to frequency
15110
+ let f = Util.divToF(Util.getParam(_params[0], c), bpm);
15111
+ this._fx.frequency.setValueAtTime(f, time);
15112
+ // delaytime/2 because of up and down through center
15113
+ // eg. 25 goes from 0 to 50, 40 goes from 0 to 80, etc.
15114
+ this._fx.delayTime = Util.getParam(_params[1], c) / 2;
15115
+
15116
+ // waveform for chorus is not supported in browser instead change wetdry
15117
+ let w = Util.getParam(_params[2], c);
15118
+ if (isNaN(w)){
15119
+ log(`Wavetype is not supported currently, instead change wet/dry with this argument, defaults to 0.5`);
15120
+ w = 0.5;
15121
+ }
15122
+ this._fx.wet.setValueAtTime(w, time);
15123
+ }
15124
+
15125
+ this.chain = () => {
15126
+ return { 'send' : this._fx, 'return' : this._fx }
15127
+ }
15128
+
15129
+ this.delete = () => {
15130
+ this._fx.disconnect();
15131
+ this._fx.dispose();
15132
+ }
15133
+ }
15134
+
15135
+
15080
15136
  // A distortion/compression effect of an incoming signal
15081
15137
  // Based on an algorithm by Peter McCulloch
15082
15138
  //
@@ -15179,9 +15235,11 @@ const LFO = function(_params){
15179
15235
  rect : 'square',
15180
15236
  triangle : 'triangle',
15181
15237
  tri : 'triangle',
15238
+ up: 'sawtooth',
15239
+ sawUp: 'sawtooth'
15182
15240
  }
15183
15241
 
15184
- this._lfo = new Tone.LFO('8n', 0, 1);
15242
+ this._lfo = new Tone.LFO();
15185
15243
  this._fx = new Tone.Gain();
15186
15244
  this._lfo.connect(this._fx.gain);
15187
15245
  // this._fx = new Tone.Tremolo('8n').start();
@@ -15206,9 +15264,13 @@ const LFO = function(_params){
15206
15264
  this._lfo.frequency.setValueAtTime(1/f, time);
15207
15265
 
15208
15266
  let a = Util.getParam(this._depth, c);
15209
- this._lfo.min = Math.min(1, Math.max(0, 1 - a));
15210
-
15211
- this._lfo.start(time);
15267
+ this._lfo.min = Math.min(1, Math.max(0, 1 - a));
15268
+ if (this._lfo.state !== 'started'){
15269
+ if (w === 'sawtooth') {
15270
+ this._lfo.phase = 180;
15271
+ }
15272
+ this._lfo.start(time);
15273
+ }
15212
15274
  }
15213
15275
 
15214
15276
  this.chain = function(){
@@ -15640,7 +15702,7 @@ class Instrument extends Sequencer {
15640
15702
  this.panner.pan.setValueAtTime(p, time);
15641
15703
 
15642
15704
  // ramp volume
15643
- let g = 20 * Math.log(Util.getParam(this._gain[0], c) * 0.707);
15705
+ let g = Util.atodb(Util.getParam(this._gain[0], c) * 0.707);
15644
15706
  let r = Util.msToS(Math.max(0, Util.getParam(this._gain[1], c)));
15645
15707
  this.source.volume.rampTo(g, r, time);
15646
15708
 
@@ -16159,6 +16221,7 @@ class MonoSynth extends Instrument {
16159
16221
  // // synth specific variables;
16160
16222
  this._note = [ 0, 0 ];
16161
16223
  this._slide = [ 0 ];
16224
+ this._firstSlide = true;
16162
16225
  this._voices = [ 1 ];
16163
16226
  this._detune = [ 0 ];
16164
16227
 
@@ -16210,14 +16273,15 @@ class MonoSynth extends Instrument {
16210
16273
 
16211
16274
  // get the slide time for next note and set the frequency
16212
16275
  let s = Util.divToS(Util.getParam(this._slide, c), this.bpm());
16213
- if (s > 0){
16276
+ if (s > 0 && !this._firstSlide){
16214
16277
  this.synth.frequency.rampTo(f, s, time);
16215
16278
  } else {
16216
16279
  this.synth.frequency.setValueAtTime(f, time);
16280
+ this._firstSlide = false;
16217
16281
  }
16218
16282
  }
16219
16283
 
16220
- super(d=[0.1], v=[3]){
16284
+ super(v=[3], d=[0.111]){
16221
16285
  // add unison voices and detune the spread
16222
16286
  // first argument is the detune amount
16223
16287
  // second argument changes the amount of voices
@@ -16225,11 +16289,6 @@ class MonoSynth extends Instrument {
16225
16289
  this._detune = Util.toArray(d);
16226
16290
  }
16227
16291
 
16228
- fat(...a){
16229
- // alias for super synth
16230
- this.super(...a);
16231
- }
16232
-
16233
16292
  slide(s){
16234
16293
  // portamento from one note to another
16235
16294
  this._slide = Util.toArray(s);
@@ -16525,8 +16584,10 @@ class PolySample extends PolyInstrument {
16525
16584
  // get the start position
16526
16585
  let p = dur * Util.getParam(this._pos, c);
16527
16586
 
16528
- // when sample is loaded, start
16529
- this.sources[id].start(time, p);
16587
+ // when sample is loaded allow playback to start
16588
+ if (this.sources[id].loaded){
16589
+ this.sources[id].start(time, p);
16590
+ }
16530
16591
  }
16531
16592
 
16532
16593
  sound(s){
@@ -16607,6 +16668,7 @@ class PolySynth extends PolyInstrument {
16607
16668
  this._wave = Util.toArray(t);
16608
16669
  this._note = [ 0, 0 ];
16609
16670
  this._slide = [ 0 ];
16671
+ this._firstSlide = [];
16610
16672
  this._voices = [ 1 ];
16611
16673
  this._detune = [ 0 ];
16612
16674
 
@@ -16620,6 +16682,7 @@ class PolySynth extends PolyInstrument {
16620
16682
  this.sources[i] = new Tone.FatOscillator().connect(this.adsrs[i]);
16621
16683
  this.sources[i].count = 1;
16622
16684
  this.sources[i].start();
16685
+ this._firstSlide[i] = true;
16623
16686
  }
16624
16687
  }
16625
16688
 
@@ -16652,10 +16715,12 @@ class PolySynth extends PolyInstrument {
16652
16715
 
16653
16716
  // get the slide time for next note and set the frequency
16654
16717
  let s = Util.divToS(Util.getParam(this._slide, c), this.bpm());
16655
- if (s > 0){
16718
+ if (s > 0 && !this._firstSlide[id]){
16656
16719
  this.sources[id].frequency.rampTo(f, s, time);
16657
16720
  } else {
16658
16721
  this.sources[id].frequency.setValueAtTime(f, time);
16722
+ // first time the synth plays it doesn't slide!
16723
+ this._firstSlide[id] = false;
16659
16724
  }
16660
16725
  }
16661
16726
 
@@ -16665,7 +16730,7 @@ class PolySynth extends PolyInstrument {
16665
16730
  this._note = [Util.toArray(i), Util.toArray(o)];
16666
16731
  }
16667
16732
 
16668
- super(d=[0.1], v=[3]){
16733
+ super(v=[3], d=[0.1]){
16669
16734
  // add unison voices and detune the spread
16670
16735
  // first argument is the detune amount
16671
16736
  // second argument changes the amount of voices
@@ -16673,11 +16738,6 @@ class PolySynth extends PolyInstrument {
16673
16738
  this._detune = Util.toArray(d);
16674
16739
  }
16675
16740
 
16676
- fat(...a){
16677
- // alias for super synth
16678
- this.super(...a);
16679
- }
16680
-
16681
16741
  slide(s){
16682
16742
  // portamento from one note to another
16683
16743
  this._slide = Util.toArray(s);
@@ -16722,6 +16782,7 @@ class Sequencer {
16722
16782
  // Tone looper
16723
16783
  this._event;
16724
16784
  this._loop;
16785
+ this._once = false;
16725
16786
  this.makeLoop();
16726
16787
 
16727
16788
  console.log('=> class Sequencer()');
@@ -16793,6 +16854,13 @@ class Sequencer {
16793
16854
  }
16794
16855
  // increment count for sequencing
16795
16856
  this._count++;
16857
+
16858
+ // if the sample is set to only play once mute the loop
16859
+ // afterwards and dispose
16860
+ if (this._once){
16861
+ this._loop.mute = 1;
16862
+ this._loop.dispose();
16863
+ }
16796
16864
  }
16797
16865
 
16798
16866
  if (this._time){
@@ -16849,7 +16917,7 @@ class Sequencer {
16849
16917
  this._loop.stop();
16850
16918
  }
16851
16919
 
16852
- time(t, o=0, s=[1]){
16920
+ time(t, o=0){
16853
16921
  // set the timing interval and offset
16854
16922
  if (t === 'free'){
16855
16923
  this._time = null;
@@ -16857,11 +16925,22 @@ class Sequencer {
16857
16925
  } else {
16858
16926
  this._time = Util.formatRatio(t, this.bpm());
16859
16927
  this._offset = Util.formatRatio(o, this.bpm());
16860
- // set timing division optionally, also possible via timediv()
16861
- // this.timediv(s);
16862
16928
  }
16863
16929
  }
16864
16930
 
16931
+ once(o=0){
16932
+ // play the sample/synth/midi once or not?
16933
+ // the moment of playing is determined by the time and offset
16934
+ this._once = (o > 0 || o === 'on' || o === 'true') ? true : false;
16935
+ }
16936
+
16937
+ ratchet(p=1, s=[1]){
16938
+ // set the ratcheting probability and subdivision
16939
+ // for now defaults to the timediv method
16940
+ Util.log(`ratchet() is not yet supported. Defaults to timediv() with probability of 1`);
16941
+ this.timediv(s);
16942
+ }
16943
+
16865
16944
  timediv(s){
16866
16945
  // set timing subdivisions for the loop
16867
16946
  let tmp = Util.toArray(s);
@@ -16910,6 +16989,22 @@ module.exports = Sequencer;
16910
16989
  },{"./Util.js":66,"tone":44,"webmidi":55}],66:[function(require,module,exports){
16911
16990
  const { noteToMidi, toScale, mtof } = require('total-serialism').Translate;
16912
16991
 
16992
+ // replace defaults with incoming parameters
16993
+ function mapDefaults(params, defaults){
16994
+ defaults.splice(0, params.length, ...params);
16995
+ return defaults.map(p => toArray(p));
16996
+ }
16997
+
16998
+ // convert amplitude to dBFS scale
16999
+ function atodb(a=0){
17000
+ return 20 * Math.log(a);
17001
+ }
17002
+
17003
+ // convert dbFS to amplitude
17004
+ function dbtoa(db=0){
17005
+ return 10 ** (db/20);
17006
+ }
17007
+
16913
17008
  // clip a value between a specified range
16914
17009
  function clip(v, l, h){
16915
17010
  return Math.max(l, Math.min(h, v));
@@ -17035,6 +17130,11 @@ function divToS(d, bpm){
17035
17130
  }
17036
17131
  }
17037
17132
 
17133
+ // convert division format to frequency in Hz based on bpm
17134
+ function divToF(d, bpm){
17135
+ return 1.0 / divToS(d, bpm)
17136
+ }
17137
+
17038
17138
  // convert note value to a frequency
17039
17139
  function noteToFreq(i, o){
17040
17140
  if (isNaN(i)){
@@ -17103,7 +17203,7 @@ function log(msg){
17103
17203
  }
17104
17204
  }
17105
17205
 
17106
- module.exports = { clip, assureNum, lookup, randLookup, isRandom, getParam, toArray, msToS, formatRatio, divToS, toMidi, mtof, noteToMidi, noteToFreq, assureWave, log }
17206
+ module.exports = { mapDefaults, atodb, dbtoa, clip, assureNum, lookup, randLookup, isRandom, getParam, toArray, msToS, formatRatio, divToS, divToF, toMidi, mtof, noteToMidi, noteToFreq, assureWave, log }
17107
17207
  },{"total-serialism":47}],67:[function(require,module,exports){
17108
17208
  module.exports={
17109
17209
  "uptempo" : 10,
@@ -17322,6 +17422,12 @@ class MercuryInterpreter {
17322
17422
  'lowPass' : (args) => {
17323
17423
  this.setLowPass(...args);
17324
17424
  // engine.setLowPass(...args);
17425
+ },
17426
+ 'samples' : (args) => {
17427
+ // load samples in the audiobuffer
17428
+ // this can be a single url to a soundfile
17429
+ // or a url to a folder that will be searched through
17430
+ this.addBuffers(args);
17325
17431
  }
17326
17432
  }
17327
17433
 
@@ -17490,6 +17596,9 @@ class Mercury extends MercuryInterpreter {
17490
17596
  this.highPassF = new Tone.Filter(5, 'highpass');
17491
17597
  Tone.Destination.chain(this.lowPassF, this.highPassF, this.gain);
17492
17598
 
17599
+ // an RMS meter for reactive visuals
17600
+ this.meter;
17601
+
17493
17602
  // a recorder for the sound
17494
17603
  this.recorder = new Tone.Recorder({ mimeType: 'audio/webm' });
17495
17604
  this.gain.connect(this.recorder);
@@ -17587,8 +17696,9 @@ class Mercury extends MercuryInterpreter {
17587
17696
  // set the bpm and optionally ramp in milliseconds
17588
17697
  setBPM(bpm, ramp=0) {
17589
17698
  this.bpm = bpm;
17590
- if (ramp > 0){
17591
- Tone.Transport.bpm.rampTo(bpm, ramp / 1000);
17699
+ let t = Util.divToS(ramp, bpm);
17700
+ if (t > 0){
17701
+ Tone.Transport.bpm.rampTo(bpm, t);
17592
17702
  } else {
17593
17703
  Tone.Transport.bpm.setValueAtTime(bpm, Tone.now());
17594
17704
  }
@@ -17705,31 +17815,34 @@ class Mercury extends MercuryInterpreter {
17705
17815
 
17706
17816
  // set lowpass frequency cutoff and ramptime
17707
17817
  setLowPass(f, t=0){
17708
- this.lowPass = f;
17818
+ this.lowPass = (f === 'default')? 18000 : f;
17819
+ t = Util.divToS(t, this.bpm);
17709
17820
  if (t > 0){
17710
- this.lowPassF.frequency.rampTo(f, t/1000, Tone.now());
17821
+ this.lowPassF.frequency.rampTo(this.lowPass, t, Tone.now());
17711
17822
  } else {
17712
- this.lowPassF.frequency.setValueAtTime(f, Tone.now());
17823
+ this.lowPassF.frequency.setValueAtTime(this.lowPass, Tone.now());
17713
17824
  }
17714
17825
  }
17715
17826
 
17716
17827
  // set highpass frequency cutoff and ramptime
17717
17828
  setHighPass(f, t=0){
17718
- this.highPass = f;
17829
+ this.highPass = (f === 'default')? 5 : f;
17830
+ t = Util.divToS(t, this.bpm);
17719
17831
  if (t > 0){
17720
- this.highPassF.frequency.rampTo(f, t/1000, Tone.now());
17832
+ this.highPassF.frequency.rampTo(this.highPass, t, Tone.now());
17721
17833
  } else {
17722
- this.highPassF.frequency.setValueAtTime(f, Tone.now());
17834
+ this.highPassF.frequency.setValueAtTime(this.highPass, Tone.now());
17723
17835
  }
17724
17836
  }
17725
17837
 
17726
17838
  // set volume in floatingpoint and ramptime
17727
17839
  setVolume(v, t=0){
17728
- this.volume = v;
17840
+ this.volume = (v === 'default')? 1 : v;
17841
+ t = Util.divToS(t, this.bpm);
17729
17842
  if (t > 0){
17730
- this.gain.gain.rampTo(v, t/1000, Tone.now());
17843
+ this.gain.gain.rampTo(this.volume, t, Tone.now());
17731
17844
  } else {
17732
- this.gain.gain.setValueAtTime(v, Tone.now());
17845
+ this.gain.gain.setValueAtTime(this.volume, Tone.now());
17733
17846
  }
17734
17847
  }
17735
17848
 
@@ -17765,6 +17878,28 @@ class Mercury extends MercuryInterpreter {
17765
17878
  isRecording(){
17766
17879
  return this.recorder.state;
17767
17880
  }
17881
+
17882
+ // add a Tone RMS meter to use for signal analysis
17883
+ addMeter(smooth=0.7){
17884
+ this.meter = new Tone.Meter(smooth);
17885
+ this.meter.normalRange = true;
17886
+ this.gain.connect(this.meter);
17887
+ }
17888
+
17889
+ // return the meter value as float betwee 0-1
17890
+ getMeter(){
17891
+ return this.meter.getValue();
17892
+ }
17893
+
17894
+ // get the webaudio context Mercury is producing sound on
17895
+ // getContext(){
17896
+ // return Tone.getContext();
17897
+ // }
17898
+
17899
+ // get the destination output Mercury is sending sound to
17900
+ // getDestination(){
17901
+ // return Tone.getDestination();
17902
+ // }
17768
17903
  }
17769
17904
  module.exports = { Mercury };
17770
17905
  },{"./core/Util.js":66,"./interpreter":68,"tone":44,"webmidi":55}]},{},[69])(69)