/*
 *
 *
 *    resiir.h
 *
 *    Header file for simple C++ class implementing a resonant IIR filter.
 *
 *    Copyright (C) 2015-2016 by Matt Roberts,
 *    All rights reserved.
 *
 *    License: GNU GPL3 (www.gnu.org)
 *
 *    Theory: http://www.music.mcgill.ca/~gary/307/week2/filters.html
 *            (Section "Resonance Filters")
 *
 */

#ifndef __KK5JY_RESIIR_H
#define __KK5JY_RESIIR_H

#ifdef __linux__
#include <cmath>
#include <vector>
#else
#include <math.h>
#endif

#include "nlimits.h"

namespace KK5JY {
	namespace DSP {
		template <typename sample_t = double, typename coef_t = sample_t>
		class ResonantFilter {
			private:
				// coefs
				coef_t a[2];
				coef_t b[2];

				// history
				sample_t x[2];
				sample_t y[2];

			public:
				// run the filter over one sample
				sample_t filter(const sample_t input);

				// configure the filter coefficients (see ctor for details)
				void configure(double omega, double r);

				#ifdef __linux__
				std::vector<coef_t> getCoefs() const;
				std::vector<sample_t> getHistory() const;
				#endif

			public:
				//
				//  Construct a second-order IIR filter;  y[n] = x[n] - a1*y[n-1] - a2*y[n-2]
				//  with a single peak in its frequency magnitude response
				//
				//    omega = frequency of peak response; f0 / fs
				//    r  = sharpness, in (0, 1), with larger values producing a narrower
				//         filter bandwidth (higher Q).
				//
				ResonantFilter(double omega, double r) { configure(omega, r); }
		};

		//
		//  ResonantFilter::configure(omega, r)
		//
		template <typename sample_t, typename coef_t>
		inline void ResonantFilter<sample_t, coef_t>::configure(double omega, double r) {
			// initialize coefs
			a[0] = -2.0 * r * cos(2.0 * M_PI * omega);
			a[1] = r * r;
			b[0] = (1.0 - (r * r)) / 2.0;
			b[1] = -b[0];

			// initialize history
			for (int i = 0; i != 2; ++i) {
				x[i] = y[i] = 0.0;
			}
		}

		//
		//  ResonantFilter::filter(...)
		//
		template <typename sample_t, typename coef_t>
		inline sample_t ResonantFilter<sample_t, coef_t>::filter(const sample_t input) {
			// calculate the filter output; 4x mult; 3x add
			sample_t result = (b[0] * input) + (b[1] * x[1]) - (a[0] * y[0]) - (a[1] * y[1]);

			// update the history
			x[1] = x[0];
			x[0] = input;
			y[1] = y[0];
			y[0] = result;

			return result;
		}

		#ifdef __linux__
		template <typename sample_t, typename coef_t>
		std::vector<coef_t> ResonantFilter<sample_t, coef_t>::getCoefs() const {
			std::vector<coef_t> result;
			result.push_back(a[0]);
			result.push_back(a[1]);
			result.push_back(b[0]);
			result.push_back(b[1]);
			return result;
		}

		template <typename sample_t, typename coef_t>
		std::vector<sample_t> ResonantFilter<sample_t, coef_t>::getHistory() const {
			std::vector<sample_t> result;
			result.push_back(x[0]);
			result.push_back(x[1]);
			result.push_back(y[0]);
			result.push_back(y[1]);
			return result;
		}
		#endif
	}
}

#endif // __RESIIR_H
