mirror of
https://github.com/scratchfoundation/scratch-audio.git
synced 2024-12-22 14:02:29 -05:00
clean up and convert webaudio nodes to tone js objects
This commit is contained in:
parent
b0786e6dbf
commit
7a25ced256
1 changed files with 24 additions and 109 deletions
133
src/vocoder.js
133
src/vocoder.js
|
@ -1,3 +1,7 @@
|
|||
/*
|
||||
|
||||
*/
|
||||
|
||||
var Tone = require('tone');
|
||||
|
||||
function Vocoder () {
|
||||
|
@ -25,9 +29,8 @@ Vocoder.prototype.setCarrierOscFrequency = function (freq) {
|
|||
this.oscillatorNode.frequency.rampTo(freq, 0.05);
|
||||
};
|
||||
|
||||
// this function will algorithmically re-calculate vocoder bands, distributing evenly
|
||||
// this function algorithmically calculates vocoder bands, distributing evenly
|
||||
// from startFreq to endFreq, splitting evenly (logarhythmically) into a given numBands.
|
||||
// The function places this info into the vocoderBands and numVocoderBands variables.
|
||||
Vocoder.prototype.generateVocoderBands = function (startFreq, endFreq, numBands) {
|
||||
// Remember: 1200 cents in octave, 100 cents per semitone
|
||||
|
||||
|
@ -41,7 +44,6 @@ Vocoder.prototype.generateVocoderBands = function (startFreq, endFreq, numBands)
|
|||
for (var i=0; i<numBands; i++) {
|
||||
vocoderBands[i] = new Object();
|
||||
vocoderBands[i].frequency = currentFreq;
|
||||
//console.log( "Band " + i + " centered at " + currentFreq + "Hz" );
|
||||
currentFreq = currentFreq * scale;
|
||||
}
|
||||
|
||||
|
@ -50,149 +52,62 @@ Vocoder.prototype.generateVocoderBands = function (startFreq, endFreq, numBands)
|
|||
|
||||
Vocoder.prototype.initBandpassFilters = function () {
|
||||
|
||||
var modFilterBands = new Array(); // tuned bandpass filters
|
||||
var modFilterPostGains = new Array(); // post-filter gains.
|
||||
var heterodynes = new Array(); // gain nodes used to multiply bandpass X sine
|
||||
var powers = new Array(); // gain nodes used to multiply prev out by itself
|
||||
var lpFilters = new Array(); // tuned lowpass filters to remove doubled copy of product
|
||||
var lpFilterPostGains = new Array(); // gain nodes for tuning input to waveshapers
|
||||
var carrierBands = new Array(); // tuned bandpass filters, same as modFilterBands but in carrier chain
|
||||
var carrierFilterPostGains = new Array(); // post-bandpass gain adjustment
|
||||
var carrierBandGains = new Array(); // these are the "control gains" driven by the lpFilters
|
||||
|
||||
var waveShaperCurve = new Float32Array(65536);
|
||||
// Populate with a "curve" that does an abs()
|
||||
var n = 65536;
|
||||
var n2 = n / 2;
|
||||
|
||||
for (var i = 0; i < n2; ++i) {
|
||||
var x = i / n2;
|
||||
waveShaperCurve[n2 + i] = x;
|
||||
waveShaperCurve[n2 - i - 1] = x;
|
||||
}
|
||||
|
||||
// Set up a high-pass filter to add back in the fricatives, etc.
|
||||
var hpFilter = Tone.context.createBiquadFilter();
|
||||
hpFilter.type = 'highpass';
|
||||
hpFilter.frequency.value = 8000;
|
||||
hpFilter.Q.value = 1; // no peaking
|
||||
this.modulatorInput.connect( hpFilter);
|
||||
var hpFilter = new Tone.Filter(8000, 'highpass');
|
||||
this.modulatorInput.connect(hpFilter);
|
||||
hpFilter.connect(this.outputGain);
|
||||
|
||||
var hpFilterGain = Tone.context.createGain();
|
||||
hpFilterGain.gain.value = 1.0;
|
||||
for (var i=0; i<this.vocoderBands.length; i++) {
|
||||
|
||||
hpFilter.connect(hpFilterGain);
|
||||
hpFilterGain.connect(this.outputGain);
|
||||
|
||||
//clear the arrays
|
||||
modFilterBands.length = 0;
|
||||
modFilterPostGains.length = 0;
|
||||
heterodynes.length = 0;
|
||||
powers.length = 0;
|
||||
lpFilters.length = 0;
|
||||
lpFilterPostGains.length = 0;
|
||||
carrierBands.length = 0;
|
||||
carrierFilterPostGains.length = 0;
|
||||
carrierBandGains.length = 0;
|
||||
|
||||
var rectifierCurve = new Float32Array(65536);
|
||||
for (i=-32768; i<32768; i++) {
|
||||
rectifierCurve[i+32768] = ((i>0)?i:-i)/32768;
|
||||
}
|
||||
|
||||
for (i=0; i<this.vocoderBands.length; i++) {
|
||||
// CREATE THE MODULATOR CHAIN
|
||||
// create the bandpass filter in the modulator chain
|
||||
var modulatorFilter = Tone.context.createBiquadFilter();
|
||||
modulatorFilter.type = 'bandpass'; // Bandpass filter
|
||||
modulatorFilter.frequency.value = this.vocoderBands[i].frequency;
|
||||
modulatorFilter.Q.value = this.FILTER_QUALITY; // initial quality
|
||||
var modulatorFilter = new Tone.Filter(this.vocoderBands[i].frequency, 'bandpass', -24);
|
||||
modulatorFilter.Q.value = this.FILTER_QUALITY;
|
||||
this.modulatorInput.connect(modulatorFilter);
|
||||
modFilterBands.push(modulatorFilter);
|
||||
|
||||
// Now, create a second bandpass filter tuned to the same frequency -
|
||||
// this turns our second-order filter into a 4th-order filter,
|
||||
// which has a steeper rolloff/octave
|
||||
var secondModulatorFilter = Tone.context.createBiquadFilter();
|
||||
secondModulatorFilter.type = 'bandpass'; // Bandpass filter
|
||||
secondModulatorFilter.frequency.value = this.vocoderBands[i].frequency;
|
||||
secondModulatorFilter.Q.value = this.FILTER_QUALITY; // initial quality
|
||||
modulatorFilter.chainedFilter = secondModulatorFilter;
|
||||
modulatorFilter.connect(secondModulatorFilter);
|
||||
|
||||
// create a post-filtering gain to bump the levels up.
|
||||
var modulatorFilterPostGain = Tone.context.createGain();
|
||||
var modulatorFilterPostGain = new Tone.Gain();
|
||||
modulatorFilterPostGain.gain.value = 6;
|
||||
secondModulatorFilter.connect(modulatorFilterPostGain);
|
||||
modFilterPostGains.push(modulatorFilterPostGain);
|
||||
modulatorFilter.connect(modulatorFilterPostGain);
|
||||
|
||||
// Create the sine oscillator for the heterodyne
|
||||
var heterodyneOscillator = Tone.context.createOscillator();
|
||||
heterodyneOscillator.frequency.value = this.vocoderBands[i].frequency;
|
||||
|
||||
heterodyneOscillator.start(0);
|
||||
var heterodyneOscillator = new Tone.Oscillator(this.vocoderBands[i].frequency).start();
|
||||
|
||||
// Create the node to multiply the sine by the modulator
|
||||
var heterodyne = Tone.context.createGain();
|
||||
var heterodyne = new Tone.Gain(0); // audio-rate inputs are summed with initial intrinsic value
|
||||
modulatorFilterPostGain.connect(heterodyne);
|
||||
heterodyne.gain.value = 0.0; // audio-rate inputs are summed with initial intrinsic value
|
||||
heterodyneOscillator.connect(heterodyne.gain);
|
||||
|
||||
var heterodynePostGain = Tone.context.createGain();
|
||||
heterodynePostGain.gain.value = 2.0; // GUESS: boost
|
||||
var heterodynePostGain = new Tone.Gain(2.0);
|
||||
heterodyne.connect(heterodynePostGain);
|
||||
heterodynes.push(heterodynePostGain);
|
||||
|
||||
|
||||
// Create the rectifier node
|
||||
var rectifier = Tone.context.createWaveShaper();
|
||||
rectifier.curve = rectifierCurve;
|
||||
var rectifier = new Tone.WaveShaper([1, 0, 1]);
|
||||
heterodynePostGain.connect(rectifier);
|
||||
|
||||
// Create the lowpass filter to mask off the difference (near zero)
|
||||
var lpFilter = Tone.context.createBiquadFilter();
|
||||
lpFilter.type = "lowpass"; // Lowpass filter
|
||||
lpFilter.frequency.value = 5.0; // Guesstimate! Mask off 20Hz and above.
|
||||
var lpFilter = new Tone.Filter(5, 'lowpass');
|
||||
lpFilter.Q.value = 1; // don't need a peak
|
||||
lpFilters.push(lpFilter);
|
||||
rectifier.connect(lpFilter);
|
||||
|
||||
var lpFilterPostGain = Tone.context.createGain();
|
||||
var lpFilterPostGain = new Tone.Gain();
|
||||
lpFilterPostGain.gain.value = 1.0;
|
||||
lpFilter.connect(lpFilterPostGain);
|
||||
lpFilterPostGains.push(lpFilterPostGain);
|
||||
|
||||
var waveshaper = Tone.context.createWaveShaper();
|
||||
waveshaper.curve = waveShaperCurve;
|
||||
var waveshaper = new Tone.WaveShaper([1, 0, 1]);
|
||||
lpFilterPostGain.connect(waveshaper);
|
||||
|
||||
|
||||
// Create the bandpass filter in the carrier chain
|
||||
var carrierFilter = Tone.context.createBiquadFilter();
|
||||
carrierFilter.type = 'bandpass';
|
||||
carrierFilter.frequency.value = this.vocoderBands[i].frequency;
|
||||
var carrierFilter = new Tone.Filter(this.vocoderBands[i].frequency, 'bandpass', -24);
|
||||
carrierFilter.Q.value = this.FILTER_QUALITY;
|
||||
carrierBands.push(carrierFilter);
|
||||
this.carrierInput.connect(carrierFilter);
|
||||
|
||||
// We want our carrier filters to be 4th-order filter too.
|
||||
var secondCarrierFilter = Tone.context.createBiquadFilter();
|
||||
secondCarrierFilter.type = 'bandpass'; // Bandpass filter
|
||||
secondCarrierFilter.frequency.value = this.vocoderBands[i].frequency;
|
||||
secondCarrierFilter.Q.value = this.FILTER_QUALITY; // initial quality
|
||||
carrierFilter.chainedFilter = secondCarrierFilter;
|
||||
carrierFilter.connect(secondCarrierFilter);
|
||||
|
||||
var carrierFilterPostGain = Tone.context.createGain();
|
||||
carrierFilterPostGain.gain.value = 10.0;
|
||||
secondCarrierFilter.connect(carrierFilterPostGain);
|
||||
carrierFilterPostGains.push(carrierFilterPostGain);
|
||||
var carrierFilterPostGain = new Tone.Gain(10);
|
||||
carrierFilter.connect(carrierFilterPostGain);
|
||||
|
||||
// Create the carrier band gain node
|
||||
var bandGain = Tone.context.createGain();
|
||||
carrierBandGains.push(bandGain);
|
||||
var bandGain = new Tone.Gain(0); // audio-rate inputs are summed with initial intrinsic value
|
||||
carrierFilterPostGain.connect(bandGain);
|
||||
bandGain.gain.value = 0.0; // audio-rate inputs are summed with initial intrinsic value
|
||||
waveshaper.connect(bandGain.gain); // connect the lp controller
|
||||
|
||||
bandGain.connect(this.outputGain);
|
||||
|
|
Loading…
Reference in a new issue