Tone.js/Tone/component/analysis/Meter.ts

124 lines
3.5 KiB
TypeScript
Raw Normal View History

2019-09-09 21:53:39 +00:00
import { gainToDb } from "../../core/type/Conversions";
import { NormalRange } from "../../core/type/Units";
2019-09-09 21:53:39 +00:00
import { optionsFromArguments } from "../../core/util/Defaults";
import { MeterBase, MeterBaseOptions } from "./MeterBase";
import { warn } from "../../core/util/Debug";
import { Analyser } from "./Analyser";
2019-09-09 21:53:39 +00:00
export interface MeterOptions extends MeterBaseOptions {
2019-09-09 21:53:39 +00:00
smoothing: NormalRange;
normalRange: boolean;
channels: number;
2019-09-09 21:53:39 +00:00
}
/**
* Meter gets the [RMS](https://en.wikipedia.org/wiki/Root_mean_square)
* of an input signal. It can also get the raw value of the input signal.
*
* @example
* const meter = new Tone.Meter();
* const mic = new Tone.UserMedia();
2019-10-23 03:39:35 +00:00
* mic.open();
2019-10-23 03:04:52 +00:00
* // connect mic to the meter
2019-09-09 21:53:39 +00:00
* mic.connect(meter);
2019-10-23 03:39:35 +00:00
* // the current level of the mic
* setInterval(() => console.log(meter.getValue()), 100);
2019-09-16 14:15:23 +00:00
* @category Component
2019-09-09 21:53:39 +00:00
*/
export class Meter extends MeterBase<MeterOptions> {
2019-09-09 21:53:39 +00:00
readonly name: string = "Meter";
/**
* If the output should be in decibels or normal range between 0-1. If `normalRange` is false,
* the output range will be the measured decibel value, otherwise the decibel value will be converted to
* the range of 0-1
*/
normalRange: boolean;
2019-09-09 21:53:39 +00:00
/**
* A value from between 0 and 1 where 0 represents no time averaging with the last analysis frame.
*/
smoothing: number;
/**
* The previous frame's value
*/
private _rms = 0;
/**
* @param smoothing The amount of smoothing applied between frames.
*/
2019-09-09 22:23:48 +00:00
constructor(smoothing?: NormalRange);
2019-09-09 21:53:39 +00:00
constructor(options?: Partial<MeterOptions>);
constructor() {
super(optionsFromArguments(Meter.getDefaults(), arguments, ["smoothing"]));
const options = optionsFromArguments(Meter.getDefaults(), arguments, ["smoothing"]);
this.input = this.output = this._analyser = new Analyser({
context: this.context,
size: 256,
type: "waveform",
channels: options.channels,
});
this.smoothing = options.smoothing,
2020-06-17 03:14:19 +00:00
this.normalRange = options.normalRange;
2019-09-09 21:53:39 +00:00
}
static getDefaults(): MeterOptions {
return Object.assign(MeterBase.getDefaults(), {
2019-09-09 21:53:39 +00:00
smoothing: 0.8,
normalRange: false,
channels: 1,
2019-09-09 21:53:39 +00:00
});
}
/**
* Use [[getValue]] instead. For the previous getValue behavior, use DCMeter.
* @deprecated
2019-09-09 21:53:39 +00:00
*/
getLevel(): number | number[] {
warn("'getLevel' has been changed to 'getValue'");
return this.getValue();
}
/**
2019-12-15 21:02:19 +00:00
* Get the current value of the incoming signal.
* Output is in decibels when [[normalRange]] is `false`.
* If [[channels]] = 1, then the output is a single number
* representing the value of the input signal. When [[channels]] > 1,
* then each channel is returned as a value in a number array.
*/
getValue(): number | number[] {
const aValues = this._analyser.getValue();
const channelValues = this.channels === 1 ? [aValues as Float32Array] : aValues as Float32Array[];
const vals = channelValues.map(values => {
const totalSquared = values.reduce((total, current) => total + current * current, 0);
const rms = Math.sqrt(totalSquared / values.length);
// the rms can only fall at the rate of the smoothing
// but can jump up instantly
this._rms = Math.max(rms, this._rms * this.smoothing);
return this.normalRange ? this._rms : gainToDb(this._rms);
});
if (this.channels === 1) {
return vals[0];
} else {
return vals;
}
}
/**
* The number of channels of analysis.
*/
get channels(): number {
return this._analyser.channels;
2019-09-09 21:53:39 +00:00
}
dispose(): this {
super.dispose();
this._analyser.dispose();
return this;
}
}