Autotel

Sample slicing in Tone.js

Capture

Today I needed to make a sample that would play a portion of a sample instead of playing the whole sample, from a nexus ui waveshape element. There are three problems that disallowed this:

  • Tone.js player object can do this natively, but when you start a sample from the middle, you will get a clicking noise, because the buffer value at the defined position is most likely not zero. IMG_20160506_172510[1]
  • Tone.js sampler includes an envelope: with this, you can soften the initial click; and ramp the start of the sample from zero, but doesn't allow playing a sample from a start and end point.
  • Actually, the sampler allows this, but only when the sample is in loop mode.

Solution

So, I made a fork of the Tone.js library, that allows the sampler to have start and end points. They are inserted when triggering attack, which is out of the Tone logic; but it allowed me to solve the problem fast, to continue my collaborative MsComposer95 project.

line 18504; replacement for Tone.Sampler.prototype.triggerAttack:

/** * Start the sample and simultaneously trigger the envelopes. * @param {string=} sample The name of the sample to trigger, defaults to * the last sample used. * @param {Time} [time=now] The time when the sample should start * @param {number} [velocity=1] The velocity of the note * @param {range} [] Define start and end points within the sample player, if you don't want to play it entirely * @returns {Tone.Sampler} this * @example * sampler.triggerAttack("B.1"); */ Tone.Sampler.prototype.triggerAttack = function (name, time, velocity, range) { time = this.toSeconds(time); if (name) { this.sample = name; } if( (range||{}).hasOwnProperty("start")&&(range||{}).hasOwnProperty("end") ){ this.player.start(0,range.start,range.end); }else{ console.log("no range"); this.player.start(time); }

  this.envelope.triggerAttack(time, velocity);
  this.filterEnvelope.triggerAttack(time);
  return this;

};

So, you would make it work by calling it this way:

//where sampler is a Tone Sampler, and loopStart & loopEnd are defined sampler.triggerAttack(false,0,1,{start:loopStart,end:loopEnd});

Future needs

My future push will consist of using the sampler's loopStart and loopEnd points instead of injecting the range object. You can grab the tweaked version in my Tone.js fork.

You can download a working example here.