#pragma once
#include <dsp/common.hpp>


namespace rack {
namespace dsp {


/** Deprecated. Use VuMeter2 instead. */
struct VuMeter {
	/** Decibel level difference between adjacent meter lights */
	float dBInterval = 3.0;
	float dBScaled;
	/** Value should be scaled so that 1.0 is clipping */
	void setValue(float v) {
		dBScaled = std::log10(std::fabs(v)) * 20.0 / dBInterval;
	}
	/** Returns the brightness of the light indexed by i.
	Light 0 is a clip light (red) which is either on or off.
	All others are smooth lights which are fully bright at -dBInterval*i and higher, and fully off at -dBInterval*(i-1).
	*/
	float getBrightness(int i) {
		if (i == 0) {
			return (dBScaled >= 0.0) ? 1.0 : 0.0;
		}
		else {
			return math::clamp(dBScaled + i, 0.0, 1.0);
		}
	}
};

DEPRECATED typedef VuMeter VUMeter;


/** Models a VU meter with smoothing.
Supports peak and RMS (root-mean-square) metering.
Usage example for a strip of lights with 3dB increments:
```
// Update VuMeter state every frame.
vuMeter.process(deltaTime, output);
// Iterate lights every ~512 frames (less than a screen refresh).
for (int i = 0; i < 6; i++) {
	float b = vuMeter.getBrightness(-3.f * (i + 1), -3.f * i);
	// No need to use setSmoothBrightness() since VuMeter2 smooths the value for you.
	lights[i].setBrightness(b);
}
```
*/
struct VuMeter2 {
	enum Mode {
		PEAK,
		RMS
	};
	Mode mode = PEAK;
	/** Either the smoothed peak or the mean-square of the brightness, depending on the mode. */
	float v = 0.f;
	/** Inverse time constant in 1/seconds */
	float lambda = 30.f;

	void reset() {
		v = 0.f;
	}

	void process(float deltaTime, float value) {
		if (mode == RMS) {
			value = std::pow(value, 2);
			v += (value - v) * lambda * deltaTime;
		}
		else {
			value = std::fabs(value);
			if (value >= v) {
				v = value;
			}
			else {
				v += (value - v) * lambda * deltaTime;
			}
		}
	}

	/** Returns the LED brightness measuring tick marks between dbMin and dbMax.
	For example, `getBrightness(-6.f, 0.f)` will be at minimum brightness at -6dB and maximum brightness at 0dB.
	Set dbMin == dbMax == 0.f for a clip indicator that turns fully on when db >= dbMax.
	Expensive, so call this infrequently.
	*/
	float getBrightness(float dbMin, float dbMax) {
		float db = amplitudeToDb((mode == RMS) ? std::sqrt(v) : v);
		if (db >= dbMax)
			return 1.f;
		else if (db <= dbMin)
			return 0.f;
		else
			return math::rescale(db, dbMin, dbMax, 0.f, 1.f);
	}
};


} // namespace dsp
} // namespace rack
