mirror of
https://github.com/Tonejs/Tone.js
synced 2024-11-16 00:27:58 +00:00
Merge branch 'dev' of https://github.com/Tonejs/Tone.js into dev
This commit is contained in:
commit
ba1e40ddd3
2 changed files with 193 additions and 47 deletions
|
@ -3,13 +3,14 @@ define(["Tone/core/Tone", "Tone/component/Analyser", "Tone/core/AudioNode"], fun
|
|||
"use strict";
|
||||
|
||||
/**
|
||||
* @class Tone.Meter gets the [RMS](https://en.wikipedia.org/wiki/Root_mean_square)
|
||||
* @class Tone.Meter gets the Peak or [RMS](https://en.wikipedia.org/wiki/Root_mean_square)
|
||||
* of an input signal with some averaging applied. It can also get the raw
|
||||
* value of the input signal.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {Tone.AudioNode}
|
||||
* @param {Number} smoothing The amount of smoothing applied between frames.
|
||||
* @param {'rms' | 'peak'} type Calculation method of dB value, defaults to RMS
|
||||
* @example
|
||||
* var meter = new Tone.Meter();
|
||||
* var mic = new Tone.UserMedia().open();
|
||||
|
@ -32,10 +33,25 @@ define(["Tone/core/Tone", "Tone/component/Analyser", "Tone/core/AudioNode"], fun
|
|||
|
||||
//set the smoothing initially
|
||||
this.smoothing = options.smoothing;
|
||||
|
||||
/**
|
||||
* Calculation method used to get the dB value
|
||||
* @type {'rms' | 'peak'}
|
||||
*/
|
||||
this.type = options.type;
|
||||
};
|
||||
|
||||
Tone.extend(Tone.Meter, Tone.AudioNode);
|
||||
|
||||
/**
|
||||
* Calculation methods available for dB value, default is RMS
|
||||
* @enum {String}
|
||||
*/
|
||||
Tone.Meter.Type = {
|
||||
RMS : "rms",
|
||||
Peak : "peak"
|
||||
};
|
||||
|
||||
/**
|
||||
* The defaults
|
||||
* @type {Object}
|
||||
|
@ -43,7 +59,8 @@ define(["Tone/core/Tone", "Tone/component/Analyser", "Tone/core/AudioNode"], fun
|
|||
* @const
|
||||
*/
|
||||
Tone.Meter.defaults = {
|
||||
"smoothing" : 0.8
|
||||
"smoothing" : 0.8,
|
||||
"type" : Tone.Meter.Type.RMS
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -51,11 +68,19 @@ define(["Tone/core/Tone", "Tone/component/Analyser", "Tone/core/AudioNode"], fun
|
|||
* @returns {Decibels}
|
||||
*/
|
||||
Tone.Meter.prototype.getLevel = function(){
|
||||
this._analyser.type = "fft";
|
||||
var values = this._analyser.getValue();
|
||||
var offset = 28; // normalizes most signal levels
|
||||
// TODO: compute loudness from FFT
|
||||
return Math.max.apply(this, values) + offset;
|
||||
|
||||
switch (this.type){
|
||||
case Tone.Meter.Type.RMS:
|
||||
var rmsFloatValue = this.getRmsFloatValue(values);
|
||||
return Tone.gainToDb(rmsFloatValue);
|
||||
case Tone.Meter.Type.Peak:
|
||||
var peakFloatValue = this.getPeakFloatValue(values);
|
||||
return Tone.gainToDb(peakFloatValue);
|
||||
default:
|
||||
// Sanity check, should have thrown while setting type
|
||||
throw new TypeError("Tone.Meter: invalid type: " + this.type);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -63,11 +88,43 @@ define(["Tone/core/Tone", "Tone/component/Analyser", "Tone/core/AudioNode"], fun
|
|||
* @returns {Number}
|
||||
*/
|
||||
Tone.Meter.prototype.getValue = function(){
|
||||
this._analyser.type = "waveform";
|
||||
var value = this._analyser.getValue();
|
||||
return value[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the peak value from a Float32Array, uses absolute values so
|
||||
* negative values are counted towards the peak.
|
||||
*
|
||||
* @param {Float32Array} values Float32Array with amplitude ratio readings
|
||||
* @returns {Number}
|
||||
*/
|
||||
Tone.Meter.prototype.getPeakFloatValue = function(values){
|
||||
var peak = 0;
|
||||
for (var i = 0; i < values.length; i++){
|
||||
var value = Math.abs(values[i]);
|
||||
if (value > peak){
|
||||
peak = value;
|
||||
}
|
||||
}
|
||||
return peak;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the [RMS](https://en.wikipedia.org/wiki/Root_mean_square) value from a Float32Array
|
||||
*
|
||||
* @param {Float32Array} values Float32Array with amplitude ratio readings
|
||||
* @returns {Number}
|
||||
*/
|
||||
Tone.Meter.prototype.getRmsFloatValue = function(values){
|
||||
var totalSquared = 0;
|
||||
for (var i = 0; i < values.length; i++){
|
||||
var value = values[i];
|
||||
totalSquared += value * value;
|
||||
}
|
||||
return Math.sqrt(totalSquared / values.length);
|
||||
};
|
||||
|
||||
/**
|
||||
* A value from 0 -> 1 where 0 represents no time averaging with the last analysis frame.
|
||||
* @memberOf Tone.Meter#
|
||||
|
@ -81,7 +138,25 @@ define(["Tone/core/Tone", "Tone/component/Analyser", "Tone/core/AudioNode"], fun
|
|||
},
|
||||
set : function(val){
|
||||
this._analyser.smoothing = val;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Either 'rms' or 'peak', determines calculation method of getValue
|
||||
* @memberOf Tone.Meter#
|
||||
* @type {'rms' | 'peak'}
|
||||
* @name type
|
||||
*/
|
||||
Object.defineProperty(Tone.Meter.prototype, "type", {
|
||||
get : function(){
|
||||
return this._type;
|
||||
},
|
||||
set : function(type){
|
||||
if (type !== Tone.Meter.Type.RMS && type !== Tone.Meter.Type.Peak){
|
||||
throw new TypeError("Tone.Meter: invalid type: " + type);
|
||||
}
|
||||
this._type = type;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,44 +7,93 @@ function (Meter, Basic, Offline, Test, Signal, PassAudio, Tone, Merge, Oscillato
|
|||
Basic(Meter);
|
||||
|
||||
context("Metering", function(){
|
||||
|
||||
it("handles input and output connections", function(){
|
||||
var meter = new Meter();
|
||||
Test.connect(meter);
|
||||
meter.connect(Test);
|
||||
meter.dispose();
|
||||
});
|
||||
|
||||
it("handles getter/setter as Object", function(){
|
||||
var meter = new Meter();
|
||||
var values = {
|
||||
"smoothing" : 0.2
|
||||
};
|
||||
meter.set(values);
|
||||
expect(meter.get().smoothing).to.equal(0.2);
|
||||
meter.dispose();
|
||||
});
|
||||
|
||||
it("can be constructed with an object", function(){
|
||||
var meter = new Meter({
|
||||
"smoothing" : 0.3
|
||||
});
|
||||
expect(meter.smoothing).to.equal(0.3);
|
||||
meter.dispose();
|
||||
});
|
||||
|
||||
it("passes the audio through", function(){
|
||||
var meter;
|
||||
return PassAudio(function(input){
|
||||
meter = new Meter();
|
||||
input.chain(meter, Tone.Master);
|
||||
});
|
||||
});
|
||||
|
||||
if (Supports.ONLINE_TESTING){
|
||||
|
||||
it("measures the incoming signal", function(done){
|
||||
if (Supports.ONLINE_TESTING) {
|
||||
it("handles input and output connections", function(){
|
||||
var meter = new Meter();
|
||||
Test.connect(meter);
|
||||
meter.connect(Test);
|
||||
meter.dispose();
|
||||
});
|
||||
|
||||
it("handles getter/setter as Object", function(){
|
||||
var meter = new Meter();
|
||||
var values = {
|
||||
"smoothing" : 0.2
|
||||
};
|
||||
meter.set(values);
|
||||
expect(meter.get().smoothing).to.equal(0.2);
|
||||
meter.dispose();
|
||||
});
|
||||
|
||||
it("can be constructed with an object", function(){
|
||||
var meter = new Meter({
|
||||
"smoothing" : 0.3
|
||||
});
|
||||
expect(meter.smoothing).to.equal(0.3);
|
||||
meter.dispose();
|
||||
});
|
||||
|
||||
it("passes the audio through", function(){
|
||||
var meter;
|
||||
return PassAudio(function(input){
|
||||
meter = new Meter();
|
||||
input.chain(meter, Tone.Master);
|
||||
});
|
||||
});
|
||||
|
||||
it("defaults to rms level of incoming signal", function(){
|
||||
var meter = new Meter();
|
||||
expect(meter.type).to.equal("rms");
|
||||
meter.dispose();
|
||||
});
|
||||
|
||||
it("throws when invalid type is set", function(){
|
||||
var meter = new Meter();
|
||||
expect(function() {
|
||||
meter.type = "shouldThrow";
|
||||
}).to.throw("Tone.Meter: invalid type: shouldThrow");
|
||||
meter.dispose();
|
||||
|
||||
expect(function(){
|
||||
new Meter({ type: "shouldThrow" });
|
||||
}).to.throw("Tone.Meter: invalid type: shouldThrow");
|
||||
});
|
||||
|
||||
it("can change type", function(){
|
||||
var meter = new Meter();
|
||||
expect(meter.type).to.equal("rms");
|
||||
|
||||
meter.type = "peak";
|
||||
expect(meter.type).to.equal("peak");
|
||||
|
||||
meter.type = "rms";
|
||||
expect(meter.type).to.equal("rms");
|
||||
|
||||
meter.dispose();
|
||||
});
|
||||
|
||||
it("gets peak value correctly", function(){
|
||||
var values = [1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0];
|
||||
var meter = new Meter();
|
||||
var peak = meter.getPeakFloatValue(values);
|
||||
expect(peak).to.equal(10);
|
||||
|
||||
meter.dispose();
|
||||
});
|
||||
|
||||
// Based on https://rosettacode.org/wiki/Averages/Root_mean_square#JavaScript
|
||||
it("gets rms value correctly", function(){
|
||||
var values = [-1.0, 2.0, -3.0, 4.0, -5.0, 6.0, -7.0, 8.0, -9.0, 10.0];
|
||||
var meter = new Meter();
|
||||
var peak = meter.getRmsFloatValue(values);
|
||||
expect(peak).to.be.closeTo(6.2048, 0.0001);
|
||||
|
||||
meter.dispose();
|
||||
});
|
||||
|
||||
|
||||
it("measures the peak incoming signal", function(done){
|
||||
var meter = new Meter({ type: "peak" });
|
||||
var signal = new Signal(1).connect(meter);
|
||||
setTimeout(function(){
|
||||
expect(meter.getValue()).to.be.closeTo(1, 0.05);
|
||||
|
@ -54,8 +103,19 @@ function (Meter, Basic, Offline, Test, Signal, PassAudio, Tone, Merge, Oscillato
|
|||
}, 400);
|
||||
});
|
||||
|
||||
it("can get the level of the incoming signal", function(done){
|
||||
var meter = new Meter();
|
||||
it("measures the rms incoming signal", function(done){
|
||||
var meter = new Meter({ type: "rms" });
|
||||
var signal = new Signal(1).connect(meter);
|
||||
setTimeout(function(){
|
||||
expect(meter.getValue()).to.be.closeTo(1, 0.05);
|
||||
meter.dispose();
|
||||
signal.dispose();
|
||||
done();
|
||||
}, 400);
|
||||
});
|
||||
|
||||
it("can get the peak level of the incoming signal", function(done){
|
||||
var meter = new Meter({ type: "peak" });
|
||||
var osc = new Oscillator().connect(meter).start();
|
||||
osc.volume.value = -6;
|
||||
setTimeout(function(){
|
||||
|
@ -65,8 +125,19 @@ function (Meter, Basic, Offline, Test, Signal, PassAudio, Tone, Merge, Oscillato
|
|||
done();
|
||||
}, 400);
|
||||
});
|
||||
}
|
||||
|
||||
it("can get the rms level of the incoming signal", function(done){
|
||||
var meter = new Meter({ type: "rms" });
|
||||
var osc = new Oscillator().connect(meter).start();
|
||||
osc.volume.value = -6;
|
||||
setTimeout(function(){
|
||||
expect(meter.getLevel()).to.be.closeTo(-9, 1);
|
||||
meter.dispose();
|
||||
osc.dispose();
|
||||
done();
|
||||
}, 400);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue