From 82b85a7ee86b8885ebe1cc826bed82d00078e81f Mon Sep 17 00:00:00 2001 From: DIVYANSHU RAJ <43696525+endurance21@users.noreply.github.com> Date: Mon, 20 Jul 2020 12:38:27 +0530 Subject: [PATCH] replacement of es5 functions to es6 class def feat.P5.FFT (#502) * replacement of es5 functions to es6 class def feat.P5.FFT * added class methods inside of declaration, facilating autoboxing --- src/app.js | 3 +- src/fft.js | 974 +++++++++++++++++++++++++++-------------------------- 2 files changed, 493 insertions(+), 484 deletions(-) diff --git a/src/app.js b/src/app.js index 6bea179b..453309d6 100644 --- a/src/app.js +++ b/src/app.js @@ -8,7 +8,8 @@ import './audioWorklet'; import './panner'; import './soundfile'; import './amplitude'; -import './fft'; +import FFT from './fft'; +p5.FFT = FFT; import './signal'; import './oscillator'; import './envelope'; diff --git a/src/fft.js b/src/fft.js index 49bddc58..153d0548 100644 --- a/src/fft.js +++ b/src/fft.js @@ -84,534 +84,542 @@ import p5sound from './master'; * } * */ -p5.FFT = function (smoothing, bins) { - this.input = this.analyser = p5sound.audiocontext.createAnalyser(); - - Object.defineProperties(this, { - bins: { - get: function () { - return this.analyser.fftSize / 2; - }, - set: function (b) { - this.analyser.fftSize = b * 2; +class FFT { + constructor(smoothing, bins) { + this.input = this.analyser = p5sound.audiocontext.createAnalyser(); + + Object.defineProperties(this, { + bins: { + get: function () { + return this.analyser.fftSize / 2; + }, + set: function (b) { + this.analyser.fftSize = b * 2; + }, + configurable: true, + enumerable: true, }, - configurable: true, - enumerable: true, - }, - smoothing: { - get: function () { - return this.analyser.smoothingTimeConstant; + smoothing: { + get: function () { + return this.analyser.smoothingTimeConstant; + }, + set: function (s) { + this.analyser.smoothingTimeConstant = s; + }, + configurable: true, + enumerable: true, }, - set: function (s) { - this.analyser.smoothingTimeConstant = s; - }, - configurable: true, - enumerable: true, - }, - }); + }); - // set default smoothing and bins - this.smooth(smoothing); - this.bins = bins || 1024; + // set default smoothing and bins + this.smooth(smoothing); + this.bins = bins || 1024; - // default connections to p5sound fftMeter - p5sound.fftMeter.connect(this.analyser); + // default connections to p5sound fftMeter + p5sound.fftMeter.connect(this.analyser); - this.freqDomain = new Uint8Array(this.analyser.frequencyBinCount); - this.timeDomain = new Uint8Array(this.analyser.frequencyBinCount); + this.freqDomain = new Uint8Array(this.analyser.frequencyBinCount); + this.timeDomain = new Uint8Array(this.analyser.frequencyBinCount); - // predefined frequency ranges, these will be tweakable - this.bass = [20, 140]; - this.lowMid = [140, 400]; - this.mid = [400, 2600]; - this.highMid = [2600, 5200]; - this.treble = [5200, 14000]; + // predefined frequency ranges, these will be tweakable + this.bass = [20, 140]; + this.lowMid = [140, 400]; + this.mid = [400, 2600]; + this.highMid = [2600, 5200]; + this.treble = [5200, 14000]; - // add this p5.SoundFile to the soundArray - p5sound.soundArray.push(this); -}; + // add this p5.SoundFile to the soundArray + p5sound.soundArray.push(this); + } -/** - * Set the input source for the FFT analysis. If no source is - * provided, FFT will analyze all sound in the sketch. - * - * @method setInput - * @for p5.FFT - * @param {Object} [source] p5.sound object (or web audio API source node) - */ -p5.FFT.prototype.setInput = function (source) { - if (!source) { - p5sound.fftMeter.connect(this.analyser); - } else { - if (source.output) { - source.output.connect(this.analyser); - } else if (source.connect) { - source.connect(this.analyser); + /** + * Set the input source for the FFT analysis. If no source is + * provided, FFT will analyze all sound in the sketch. + * + * @method setInput + * @for p5.FFT + * @param {Object} [source] p5.sound object (or web audio API source node) + */ + setInput(source) { + if (!source) { + p5sound.fftMeter.connect(this.analyser); + } else { + if (source.output) { + source.output.connect(this.analyser); + } else if (source.connect) { + source.connect(this.analyser); + } + p5sound.fftMeter.disconnect(); } - p5sound.fftMeter.disconnect(); } -}; -/** - * Returns an array of amplitude values (between -1.0 and +1.0) that represent - * a snapshot of amplitude readings in a single buffer. Length will be - * equal to bins (defaults to 1024). Can be used to draw the waveform - * of a sound. - * - * @method waveform - * @for p5.FFT - * @param {Number} [bins] Must be a power of two between - * 16 and 1024. Defaults to 1024. - * @param {String} [precision] If any value is provided, will return results - * in a Float32 Array which is more precise - * than a regular array. - * @return {Array} Array Array of amplitude values (-1 to 1) - * over time. Array length = bins. - * - */ -p5.FFT.prototype.waveform = function () { - var bins, mode; - var normalArray = new Array(); - - for (var i = 0; i < arguments.length; i++) { - if (typeof arguments[i] === 'number') { - bins = arguments[i]; - this.analyser.fftSize = bins * 2; + /** + * Returns an array of amplitude values (between -1.0 and +1.0) that represent + * a snapshot of amplitude readings in a single buffer. Length will be + * equal to bins (defaults to 1024). Can be used to draw the waveform + * of a sound. + * + * @method waveform + * @for p5.FFT + * @param {Number} [bins] Must be a power of two between + * 16 and 1024. Defaults to 1024. + * @param {String} [precision] If any value is provided, will return results + * in a Float32 Array which is more precise + * than a regular array. + * @return {Array} Array Array of amplitude values (-1 to 1) + * over time. Array length = bins. + * + */ + waveform() { + var bins, mode; + var normalArray = new Array(); + + for (var i = 0; i < arguments.length; i++) { + if (typeof arguments[i] === 'number') { + bins = arguments[i]; + this.analyser.fftSize = bins * 2; + } + if (typeof arguments[i] === 'string') { + mode = arguments[i]; + } } - if (typeof arguments[i] === 'string') { - mode = arguments[i]; + + // getFloatFrequencyData doesnt work in Safari as of 5/2015 + if (mode && !p5.prototype._isSafari()) { + timeToFloat(this, this.timeDomain); + this.analyser.getFloatTimeDomainData(this.timeDomain); + return this.timeDomain; + } else { + timeToInt(this, this.timeDomain); + this.analyser.getByteTimeDomainData(this.timeDomain); + for (var j = 0; j < this.timeDomain.length; j++) { + var scaled = p5.prototype.map(this.timeDomain[j], 0, 255, -1, 1); + normalArray.push(scaled); + } + return normalArray; } } - // getFloatFrequencyData doesnt work in Safari as of 5/2015 - if (mode && !p5.prototype._isSafari()) { - timeToFloat(this, this.timeDomain); - this.analyser.getFloatTimeDomainData(this.timeDomain); - return this.timeDomain; - } else { - timeToInt(this, this.timeDomain); - this.analyser.getByteTimeDomainData(this.timeDomain); - for (var j = 0; j < this.timeDomain.length; j++) { - var scaled = p5.prototype.map(this.timeDomain[j], 0, 255, -1, 1); - normalArray.push(scaled); + /** + * Returns an array of amplitude values (between 0 and 255) + * across the frequency spectrum. Length is equal to FFT bins + * (1024 by default). The array indices correspond to frequencies + * (i.e. pitches), from the lowest to the highest that humans can + * hear. Each value represents amplitude at that slice of the + * frequency spectrum. Must be called prior to using + * getEnergy(). + * + * @method analyze + * @for p5.FFT + * @param {Number} [bins] Must be a power of two between + * 16 and 1024. Defaults to 1024. + * @param {Number} [scale] If "dB," returns decibel + * float measurements between + * -140 and 0 (max). + * Otherwise returns integers from 0-255. + * @return {Array} spectrum Array of energy (amplitude/volume) + * values across the frequency spectrum. + * Lowest energy (silence) = 0, highest + * possible is 255. + * @example + *
+ * let osc, fft; + * + * function setup(){ + * let cnv = createCanvas(100,100); + * cnv.mousePressed(startSound); + * osc = new p5.Oscillator(); + * osc.amp(0); + * fft = new p5.FFT(); + * } + * + * function draw(){ + * background(220); + * + * let freq = map(mouseX, 0, windowWidth, 20, 10000); + * freq = constrain(freq, 1, 20000); + * osc.freq(freq); + * + * let spectrum = fft.analyze(); + * noStroke(); + * fill(255, 0, 255); + * for (let i = 0; i< spectrum.length; i++){ + * let x = map(i, 0, spectrum.length, 0, width); + * let h = -height + map(spectrum[i], 0, 255, height, 0); + * rect(x, height, width / spectrum.length, h ); + * } + * + * stroke(255); + * if (!osc.started) { + * text('tap here and drag to change frequency', 10, 20, width - 20); + * } else { + * text(round(freq)+'Hz', 10, 20); + * } + * } + * + * function startSound() { + * osc.start(); + * osc.amp(0.5, 0.2); + * } + * + * function mouseReleased() { + * osc.amp(0, 0.2); + * } + *
+ * + * + */ + analyze() { + var mode; + + for (var i = 0; i < arguments.length; i++) { + if (typeof arguments[i] === 'number') { + this.bins = arguments[i]; + this.analyser.fftSize = this.bins * 2; + } + if (typeof arguments[i] === 'string') { + mode = arguments[i]; + } } - return normalArray; - } -}; -/** - * Returns an array of amplitude values (between 0 and 255) - * across the frequency spectrum. Length is equal to FFT bins - * (1024 by default). The array indices correspond to frequencies - * (i.e. pitches), from the lowest to the highest that humans can - * hear. Each value represents amplitude at that slice of the - * frequency spectrum. Must be called prior to using - * getEnergy(). - * - * @method analyze - * @for p5.FFT - * @param {Number} [bins] Must be a power of two between - * 16 and 1024. Defaults to 1024. - * @param {Number} [scale] If "dB," returns decibel - * float measurements between - * -140 and 0 (max). - * Otherwise returns integers from 0-255. - * @return {Array} spectrum Array of energy (amplitude/volume) - * values across the frequency spectrum. - * Lowest energy (silence) = 0, highest - * possible is 255. - * @example - *
- * let osc, fft; - * - * function setup(){ - * let cnv = createCanvas(100,100); - * cnv.mousePressed(startSound); - * osc = new p5.Oscillator(); - * osc.amp(0); - * fft = new p5.FFT(); - * } - * - * function draw(){ - * background(220); - * - * let freq = map(mouseX, 0, windowWidth, 20, 10000); - * freq = constrain(freq, 1, 20000); - * osc.freq(freq); - * - * let spectrum = fft.analyze(); - * noStroke(); - * fill(255, 0, 255); - * for (let i = 0; i< spectrum.length; i++){ - * let x = map(i, 0, spectrum.length, 0, width); - * let h = -height + map(spectrum[i], 0, 255, height, 0); - * rect(x, height, width / spectrum.length, h ); - * } - * - * stroke(255); - * if (!osc.started) { - * text('tap here and drag to change frequency', 10, 20, width - 20); - * } else { - * text(round(freq)+'Hz', 10, 20); - * } - * } - * - * function startSound() { - * osc.start(); - * osc.amp(0.5, 0.2); - * } - * - * function mouseReleased() { - * osc.amp(0, 0.2); - * } - *
- * - * - */ -p5.FFT.prototype.analyze = function () { - var mode; + if (mode && mode.toLowerCase() === 'db') { + freqToFloat(this); + this.analyser.getFloatFrequencyData(this.freqDomain); + return this.freqDomain; + } else { + freqToInt(this, this.freqDomain); + this.analyser.getByteFrequencyData(this.freqDomain); + var normalArray = Array.apply([], this.freqDomain); - for (var i = 0; i < arguments.length; i++) { - if (typeof arguments[i] === 'number') { - this.bins = arguments[i]; - this.analyser.fftSize = this.bins * 2; - } - if (typeof arguments[i] === 'string') { - mode = arguments[i]; + return normalArray; } } - if (mode && mode.toLowerCase() === 'db') { - freqToFloat(this); - this.analyser.getFloatFrequencyData(this.freqDomain); - return this.freqDomain; - } else { - freqToInt(this, this.freqDomain); - this.analyser.getByteFrequencyData(this.freqDomain); - var normalArray = Array.apply([], this.freqDomain); + /** + * Returns the amount of energy (volume) at a specific + * + * frequency, or the average amount of energy between two + * frequencies. Accepts Number(s) corresponding + * to frequency (in Hz), or a String corresponding to predefined + * frequency ranges ("bass", "lowMid", "mid", "highMid", "treble"). + * Returns a range between 0 (no energy/volume at that frequency) and + * 255 (maximum energy). + * NOTE: analyze() must be called prior to getEnergy(). Analyze() + * tells the FFT to analyze frequency data, and getEnergy() uses + * the results determine the value at a specific frequency or + * range of frequencies.

+ * + * @method getEnergy + * @for p5.FFT + * @param {Number|String} frequency1 Will return a value representing + * energy at this frequency. Alternately, + * the strings "bass", "lowMid" "mid", + * "highMid", and "treble" will return + * predefined frequency ranges. + * @param {Number} [frequency2] If a second frequency is given, + * will return average amount of + * energy that exists between the + * two frequencies. + * @return {Number} Energy Energy (volume/amplitude) from + * 0 and 255. + * + */ + getEnergy(frequency1, frequency2) { + var nyquist = p5sound.audiocontext.sampleRate / 2; + + if (frequency1 === 'bass') { + frequency1 = this.bass[0]; + frequency2 = this.bass[1]; + } else if (frequency1 === 'lowMid') { + frequency1 = this.lowMid[0]; + frequency2 = this.lowMid[1]; + } else if (frequency1 === 'mid') { + frequency1 = this.mid[0]; + frequency2 = this.mid[1]; + } else if (frequency1 === 'highMid') { + frequency1 = this.highMid[0]; + frequency2 = this.highMid[1]; + } else if (frequency1 === 'treble') { + frequency1 = this.treble[0]; + frequency2 = this.treble[1]; + } - return normalArray; + if (typeof frequency1 !== 'number') { + throw 'invalid input for getEnergy()'; + } else if (!frequency2) { + // if only one parameter: + var index = Math.round((frequency1 / nyquist) * this.freqDomain.length); + return this.freqDomain[index]; + } else if (frequency1 && frequency2) { + // if two parameters: + // if second is higher than first + if (frequency1 > frequency2) { + var swap = frequency2; + frequency2 = frequency1; + frequency1 = swap; + } + var lowIndex = Math.round( + (frequency1 / nyquist) * this.freqDomain.length + ); + var highIndex = Math.round( + (frequency2 / nyquist) * this.freqDomain.length + ); + + var total = 0; + var numFrequencies = 0; + // add up all of the values for the frequencies + for (var i = lowIndex; i <= highIndex; i++) { + total += this.freqDomain[i]; + numFrequencies += 1; + } + // divide by total number of frequencies + var toReturn = total / numFrequencies; + return toReturn; + } else { + throw 'invalid input for getEnergy()'; + } } -}; -/** - * Returns the amount of energy (volume) at a specific - * - * frequency, or the average amount of energy between two - * frequencies. Accepts Number(s) corresponding - * to frequency (in Hz), or a String corresponding to predefined - * frequency ranges ("bass", "lowMid", "mid", "highMid", "treble"). - * Returns a range between 0 (no energy/volume at that frequency) and - * 255 (maximum energy). - * NOTE: analyze() must be called prior to getEnergy(). Analyze() - * tells the FFT to analyze frequency data, and getEnergy() uses - * the results determine the value at a specific frequency or - * range of frequencies.

- * - * @method getEnergy - * @for p5.FFT - * @param {Number|String} frequency1 Will return a value representing - * energy at this frequency. Alternately, - * the strings "bass", "lowMid" "mid", - * "highMid", and "treble" will return - * predefined frequency ranges. - * @param {Number} [frequency2] If a second frequency is given, - * will return average amount of - * energy that exists between the - * two frequencies. - * @return {Number} Energy Energy (volume/amplitude) from - * 0 and 255. - * - */ -p5.FFT.prototype.getEnergy = function (frequency1, frequency2) { - var nyquist = p5sound.audiocontext.sampleRate / 2; - - if (frequency1 === 'bass') { - frequency1 = this.bass[0]; - frequency2 = this.bass[1]; - } else if (frequency1 === 'lowMid') { - frequency1 = this.lowMid[0]; - frequency2 = this.lowMid[1]; - } else if (frequency1 === 'mid') { - frequency1 = this.mid[0]; - frequency2 = this.mid[1]; - } else if (frequency1 === 'highMid') { - frequency1 = this.highMid[0]; - frequency2 = this.highMid[1]; - } else if (frequency1 === 'treble') { - frequency1 = this.treble[0]; - frequency2 = this.treble[1]; + // compatability with v.012, changed to getEnergy in v.0121. Will be deprecated... + getFreq(freq1, freq2) { + console.log('getFreq() is deprecated. Please use getEnergy() instead.'); + var x = this.getEnergy(freq1, freq2); + return x; } - if (typeof frequency1 !== 'number') { - throw 'invalid input for getEnergy()'; - } else if (!frequency2) { - // if only one parameter: - var index = Math.round((frequency1 / nyquist) * this.freqDomain.length); - return this.freqDomain[index]; - } else if (frequency1 && frequency2) { - // if two parameters: - // if second is higher than first - if (frequency1 > frequency2) { - var swap = frequency2; - frequency2 = frequency1; - frequency1 = swap; + /** + * Returns the + * + * spectral centroid of the input signal. + * NOTE: analyze() must be called prior to getCentroid(). Analyze() + * tells the FFT to analyze frequency data, and getCentroid() uses + * the results determine the spectral centroid.

+ * + * @method getCentroid + * @for p5.FFT + * @return {Number} Spectral Centroid Frequency of the spectral centroid in Hz. + * + * + * @example + *
+ * function setup(){ + * cnv = createCanvas(100,100); + * cnv.mousePressed(userStartAudio); + * sound = new p5.AudioIn(); + * sound.start(); + * fft = new p5.FFT(); + * sound.connect(fft); + *} + * + *function draw() { + * if (getAudioContext().state !== 'running') { + * background(220); + * text('tap here and enable mic to begin', 10, 20, width - 20); + * return; + * } + * let centroidplot = 0.0; + * let spectralCentroid = 0; + * + * background(0); + * stroke(0,255,0); + * let spectrum = fft.analyze(); + * fill(0,255,0); // spectrum is green + * + * //draw the spectrum + * for (let i = 0; i < spectrum.length; i++){ + * let x = map(log(i), 0, log(spectrum.length), 0, width); + * let h = map(spectrum[i], 0, 255, 0, height); + * let rectangle_width = (log(i+1)-log(i))*(width/log(spectrum.length)); + * rect(x, height, rectangle_width, -h ) + * } + * let nyquist = 22050; + * + * // get the centroid + * spectralCentroid = fft.getCentroid(); + * + * // the mean_freq_index calculation is for the display. + * let mean_freq_index = spectralCentroid/(nyquist/spectrum.length); + * + * centroidplot = map(log(mean_freq_index), 0, log(spectrum.length), 0, width); + * + * stroke(255,0,0); // the line showing where the centroid is will be red + * + * rect(centroidplot, 0, width / spectrum.length, height) + * noStroke(); + * fill(255,255,255); // text is white + * text('centroid: ', 10, 20); + * text(round(spectralCentroid)+' Hz', 10, 40); + *} + *
+ */ + getCentroid() { + var nyquist = p5sound.audiocontext.sampleRate / 2; + var cumulative_sum = 0; + var centroid_normalization = 0; + + for (var i = 0; i < this.freqDomain.length; i++) { + cumulative_sum += i * this.freqDomain[i]; + centroid_normalization += this.freqDomain[i]; } - var lowIndex = Math.round((frequency1 / nyquist) * this.freqDomain.length); - var highIndex = Math.round((frequency2 / nyquist) * this.freqDomain.length); - - var total = 0; - var numFrequencies = 0; - // add up all of the values for the frequencies - for (var i = lowIndex; i <= highIndex; i++) { - total += this.freqDomain[i]; - numFrequencies += 1; - } - // divide by total number of frequencies - var toReturn = total / numFrequencies; - return toReturn; - } else { - throw 'invalid input for getEnergy()'; - } -}; -// compatability with v.012, changed to getEnergy in v.0121. Will be deprecated... -p5.FFT.prototype.getFreq = function (freq1, freq2) { - console.log('getFreq() is deprecated. Please use getEnergy() instead.'); - var x = this.getEnergy(freq1, freq2); - return x; -}; + var mean_freq_index = 0; -/** - * Returns the - * - * spectral centroid of the input signal. - * NOTE: analyze() must be called prior to getCentroid(). Analyze() - * tells the FFT to analyze frequency data, and getCentroid() uses - * the results determine the spectral centroid.

- * - * @method getCentroid - * @for p5.FFT - * @return {Number} Spectral Centroid Frequency of the spectral centroid in Hz. - * - * - * @example - *
- * function setup(){ - * cnv = createCanvas(100,100); - * cnv.mousePressed(userStartAudio); - * sound = new p5.AudioIn(); - * sound.start(); - * fft = new p5.FFT(); - * sound.connect(fft); - *} - * - *function draw() { - * if (getAudioContext().state !== 'running') { - * background(220); - * text('tap here and enable mic to begin', 10, 20, width - 20); - * return; - * } - * let centroidplot = 0.0; - * let spectralCentroid = 0; - * - * background(0); - * stroke(0,255,0); - * let spectrum = fft.analyze(); - * fill(0,255,0); // spectrum is green - * - * //draw the spectrum - * for (let i = 0; i < spectrum.length; i++){ - * let x = map(log(i), 0, log(spectrum.length), 0, width); - * let h = map(spectrum[i], 0, 255, 0, height); - * let rectangle_width = (log(i+1)-log(i))*(width/log(spectrum.length)); - * rect(x, height, rectangle_width, -h ) - * } - * let nyquist = 22050; - * - * // get the centroid - * spectralCentroid = fft.getCentroid(); - * - * // the mean_freq_index calculation is for the display. - * let mean_freq_index = spectralCentroid/(nyquist/spectrum.length); - * - * centroidplot = map(log(mean_freq_index), 0, log(spectrum.length), 0, width); - * - * stroke(255,0,0); // the line showing where the centroid is will be red - * - * rect(centroidplot, 0, width / spectrum.length, height) - * noStroke(); - * fill(255,255,255); // text is white - * text('centroid: ', 10, 20); - * text(round(spectralCentroid)+' Hz', 10, 40); - *} - *
- */ -p5.FFT.prototype.getCentroid = function () { - var nyquist = p5sound.audiocontext.sampleRate / 2; - var cumulative_sum = 0; - var centroid_normalization = 0; - - for (var i = 0; i < this.freqDomain.length; i++) { - cumulative_sum += i * this.freqDomain[i]; - centroid_normalization += this.freqDomain[i]; - } - - var mean_freq_index = 0; + if (centroid_normalization !== 0) { + mean_freq_index = cumulative_sum / centroid_normalization; + } - if (centroid_normalization !== 0) { - mean_freq_index = cumulative_sum / centroid_normalization; + var spec_centroid_freq = + mean_freq_index * (nyquist / this.freqDomain.length); + return spec_centroid_freq; } - var spec_centroid_freq = mean_freq_index * (nyquist / this.freqDomain.length); - return spec_centroid_freq; -}; - -/** - * Smooth FFT analysis by averaging with the last analysis frame. - * - * @method smooth - * @param {Number} smoothing 0.0 < smoothing < 1.0. - * Defaults to 0.8. - */ -p5.FFT.prototype.smooth = function (s) { - if (typeof s !== 'undefined') { - this.smoothing = s; + /** + * Smooth FFT analysis by averaging with the last analysis frame. + * + * @method smooth + * @param {Number} smoothing 0.0 < smoothing < 1.0. + * Defaults to 0.8. + */ + smooth(s) { + if (typeof s !== 'undefined') { + this.smoothing = s; + } + return this.smoothing; } - return this.smoothing; -}; -p5.FFT.prototype.dispose = function () { - // remove reference from soundArray - var index = p5sound.soundArray.indexOf(this); - p5sound.soundArray.splice(index, 1); + dispose() { + // remove reference from soundArray + var index = p5sound.soundArray.indexOf(this); + p5sound.soundArray.splice(index, 1); - if (this.analyser) { - this.analyser.disconnect(); - delete this.analyser; + if (this.analyser) { + this.analyser.disconnect(); + delete this.analyser; + } } -}; -/** - * Returns an array of average amplitude values for a given number - * of frequency bands split equally. N defaults to 16. - * NOTE: analyze() must be called prior to linAverages(). Analyze() - * tells the FFT to analyze frequency data, and linAverages() uses - * the results to group them into a smaller set of averages.

- * - * @method linAverages - * @for p5.FFT - * @param {Number} N Number of returned frequency groups - * @return {Array} linearAverages Array of average amplitude values for each group - */ -p5.FFT.prototype.linAverages = function (_N) { - var N = _N || 16; // This prevents undefined, null or 0 values of N - - var spectrum = this.freqDomain; - var spectrumLength = spectrum.length; - var spectrumStep = Math.floor(spectrumLength / N); - - var linearAverages = new Array(N); - // Keep a second index for the current average group and place the values accordingly - // with only one loop in the spectrum data - var groupIndex = 0; - - for (var specIndex = 0; specIndex < spectrumLength; specIndex++) { - linearAverages[groupIndex] = - linearAverages[groupIndex] !== undefined - ? (linearAverages[groupIndex] + spectrum[specIndex]) / 2 - : spectrum[specIndex]; - - // Increase the group index when the last element of the group is processed - if (specIndex % spectrumStep === spectrumStep - 1) { - groupIndex++; + /** + * Returns an array of average amplitude values for a given number + * of frequency bands split equally. N defaults to 16. + * NOTE: analyze() must be called prior to linAverages(). Analyze() + * tells the FFT to analyze frequency data, and linAverages() uses + * the results to group them into a smaller set of averages.

+ * + * @method linAverages + * @for p5.FFT + * @param {Number} N Number of returned frequency groups + * @return {Array} linearAverages Array of average amplitude values for each group + */ + + linAverages(_N) { + var N = _N || 16; // This prevents undefined, null or 0 values of N + + var spectrum = this.freqDomain; + var spectrumLength = spectrum.length; + var spectrumStep = Math.floor(spectrumLength / N); + + var linearAverages = new Array(N); + // Keep a second index for the current average group and place the values accordingly + // with only one loop in the spectrum data + var groupIndex = 0; + + for (var specIndex = 0; specIndex < spectrumLength; specIndex++) { + linearAverages[groupIndex] = + linearAverages[groupIndex] !== undefined + ? (linearAverages[groupIndex] + spectrum[specIndex]) / 2 + : spectrum[specIndex]; + + // Increase the group index when the last element of the group is processed + if (specIndex % spectrumStep === spectrumStep - 1) { + groupIndex++; + } } - } - return linearAverages; -}; + return linearAverages; + } -/** - * Returns an array of average amplitude values of the spectrum, for a given - * set of - * Octave Bands - * NOTE: analyze() must be called prior to logAverages(). Analyze() - * tells the FFT to analyze frequency data, and logAverages() uses - * the results to group them into a smaller set of averages.

- * - * @method logAverages - * @for p5.FFT - * @param {Array} octaveBands Array of Octave Bands objects for grouping - * @return {Array} logAverages Array of average amplitude values for each group - */ -p5.FFT.prototype.logAverages = function (octaveBands) { - var nyquist = p5sound.audiocontext.sampleRate / 2; - var spectrum = this.freqDomain; - var spectrumLength = spectrum.length; - - var logAverages = new Array(octaveBands.length); - // Keep a second index for the current average group and place the values accordingly - // With only one loop in the spectrum data - var octaveIndex = 0; - - for (var specIndex = 0; specIndex < spectrumLength; specIndex++) { - var specIndexFrequency = Math.round( - (specIndex * nyquist) / this.freqDomain.length - ); - - // Increase the group index if the current frequency exceeds the limits of the band - if (specIndexFrequency > octaveBands[octaveIndex].hi) { - octaveIndex++; + /** + * Returns an array of average amplitude values of the spectrum, for a given + * set of + * Octave Bands + * NOTE: analyze() must be called prior to logAverages(). Analyze() + * tells the FFT to analyze frequency data, and logAverages() uses + * the results to group them into a smaller set of averages.

+ * + * @method logAverages + * @for p5.FFT + * @param {Array} octaveBands Array of Octave Bands objects for grouping + * @return {Array} logAverages Array of average amplitude values for each group + */ + logAverages(octaveBands) { + var nyquist = p5sound.audiocontext.sampleRate / 2; + var spectrum = this.freqDomain; + var spectrumLength = spectrum.length; + + var logAverages = new Array(octaveBands.length); + // Keep a second index for the current average group and place the values accordingly + // With only one loop in the spectrum data + var octaveIndex = 0; + + for (var specIndex = 0; specIndex < spectrumLength; specIndex++) { + var specIndexFrequency = Math.round( + (specIndex * nyquist) / this.freqDomain.length + ); + + // Increase the group index if the current frequency exceeds the limits of the band + if (specIndexFrequency > octaveBands[octaveIndex].hi) { + octaveIndex++; + } + + logAverages[octaveIndex] = + logAverages[octaveIndex] !== undefined + ? (logAverages[octaveIndex] + spectrum[specIndex]) / 2 + : spectrum[specIndex]; } - logAverages[octaveIndex] = - logAverages[octaveIndex] !== undefined - ? (logAverages[octaveIndex] + spectrum[specIndex]) / 2 - : spectrum[specIndex]; + return logAverages; } - return logAverages; -}; + /** + * Calculates and Returns the 1/N + * Octave Bands + * N defaults to 3 and minimum central frequency to 15.625Hz. + * (1/3 Octave Bands ~= 31 Frequency Bands) + * Setting fCtr0 to a central value of a higher octave will ignore the lower bands + * and produce less frequency groups. + * + * @method getOctaveBands + * @for p5.FFT + * @param {Number} N Specifies the 1/N type of generated octave bands + * @param {Number} fCtr0 Minimum central frequency for the lowest band + * @return {Array} octaveBands Array of octave band objects with their bounds + */ + getOctaveBands(_N, _fCtr0) { + var N = _N || 3; // Default to 1/3 Octave Bands + var fCtr0 = _fCtr0 || 15.625; // Minimum central frequency, defaults to 15.625Hz + + var octaveBands = []; + var lastFrequencyBand = { + lo: fCtr0 / Math.pow(2, 1 / (2 * N)), + ctr: fCtr0, + hi: fCtr0 * Math.pow(2, 1 / (2 * N)), + }; + octaveBands.push(lastFrequencyBand); + + var nyquist = p5sound.audiocontext.sampleRate / 2; + while (lastFrequencyBand.hi < nyquist) { + var newFrequencyBand = {}; + newFrequencyBand.lo = lastFrequencyBand.hi; + newFrequencyBand.ctr = lastFrequencyBand.ctr * Math.pow(2, 1 / N); + newFrequencyBand.hi = newFrequencyBand.ctr * Math.pow(2, 1 / (2 * N)); + + octaveBands.push(newFrequencyBand); + lastFrequencyBand = newFrequencyBand; + } -/** - * Calculates and Returns the 1/N - * Octave Bands - * N defaults to 3 and minimum central frequency to 15.625Hz. - * (1/3 Octave Bands ~= 31 Frequency Bands) - * Setting fCtr0 to a central value of a higher octave will ignore the lower bands - * and produce less frequency groups. - * - * @method getOctaveBands - * @for p5.FFT - * @param {Number} N Specifies the 1/N type of generated octave bands - * @param {Number} fCtr0 Minimum central frequency for the lowest band - * @return {Array} octaveBands Array of octave band objects with their bounds - */ -p5.FFT.prototype.getOctaveBands = function (_N, _fCtr0) { - var N = _N || 3; // Default to 1/3 Octave Bands - var fCtr0 = _fCtr0 || 15.625; // Minimum central frequency, defaults to 15.625Hz - - var octaveBands = []; - var lastFrequencyBand = { - lo: fCtr0 / Math.pow(2, 1 / (2 * N)), - ctr: fCtr0, - hi: fCtr0 * Math.pow(2, 1 / (2 * N)), - }; - octaveBands.push(lastFrequencyBand); - - var nyquist = p5sound.audiocontext.sampleRate / 2; - while (lastFrequencyBand.hi < nyquist) { - var newFrequencyBand = {}; - newFrequencyBand.lo = lastFrequencyBand.hi; - newFrequencyBand.ctr = lastFrequencyBand.ctr * Math.pow(2, 1 / N); - newFrequencyBand.hi = newFrequencyBand.ctr * Math.pow(2, 1 / (2 * N)); - - octaveBands.push(newFrequencyBand); - lastFrequencyBand = newFrequencyBand; + return octaveBands; } - - return octaveBands; -}; +} // helper methods to convert type from float (dB) to int (0-255) function freqToFloat(fft) { @@ -635,4 +643,4 @@ function timeToInt(fft) { } } -export default p5.FFT; +export default FFT;